agentHost: centralize session and chat catalog metadata - #332410
agentHost: centralize session and chat catalog metadata#332410Sandeep Somavarapu (sandy081) wants to merge 65 commits into
Conversation
Add a backward-compatible sessions_v2 catalog, legacy-first synchronization receipts, reconciliation, shadow validation, central fallback reads, and durable chat metadata while retaining open-only content in per-session databases.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adapt the sessions_v2 catalog and reconciliation work to the latest Agent Host composition, adoption, metadata, and test infrastructure changes.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make sessions_v2 an independent current registry, import directly from current, legacy, and provider sources, mirror runtime identities for downgrade compatibility, and reconcile cross-version changes with durable exclusions and versioned markers.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move catalog source resolution and downgrade-compatible peer chat persistence out of AgentService into focused helpers without changing migration or runtime behavior.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Introduces a centralized, verified sessions_v2 catalog for efficient Agent Host session listing, migration, downgrade compatibility, and recovery.
Changes:
- Adds catalog schemas, canonical projections, synchronization receipts, migration, reconciliation, and shadow validation.
- Integrates central listing with session mutations, peer chats, Git state, titles, and legacy adoption.
- Adds extensive persistence, compatibility, recovery, and rollout tests.
Show a summary per file
| File | Description |
|---|---|
src/vs/platform/agentHost/common/agent.ts |
Extends adoption results with recovered list metadata. |
src/vs/platform/agentHost/common/sessionDataService.ts |
Defines catalog synchronization receipt APIs. |
src/vs/platform/agentHost/node/agentHostBootstrap.ts |
Wires catalog persistence into bootstrap. |
src/vs/platform/agentHost/node/agentHostCatalogListReader.ts |
Converts verified catalog rows into list metadata. |
src/vs/platform/agentHost/node/agentHostCatalogProjection.ts |
Implements canonical bounded projections and hashing. |
src/vs/platform/agentHost/node/agentHostCatalogReconciliationService.ts |
Repairs interrupted or stale synchronization. |
src/vs/platform/agentHost/node/agentHostCatalogShadowValidator.ts |
Compares central and legacy listing results. |
src/vs/platform/agentHost/node/agentHostCatalogSourceResolver.ts |
Resolves canonical catalog source metadata. |
src/vs/platform/agentHost/node/agentHostCatalogSyncService.ts |
Coordinates local-first catalog synchronization. |
src/vs/platform/agentHost/node/agentHostDatabase.ts |
Adds sessions_v2, exclusions, and compatibility migrations. |
src/vs/platform/agentHost/node/agentHostGitStateService.ts |
Persists Git summaries through the catalog path. |
src/vs/platform/agentHost/node/agentHostPeerChatStore.ts |
Persists downgrade-compatible peer-chat membership. |
src/vs/platform/agentHost/node/agentHostServices.ts |
Registers catalog-aware persistence dependencies. |
src/vs/platform/agentHost/node/agentHostSessionTitleController.ts |
Routes title metadata through catalog synchronization. |
src/vs/platform/agentHost/node/agentHostSessionsV2MigrationService.ts |
Implements direct, resumable v2 migration. |
src/vs/platform/agentHost/node/agentService.ts |
Integrates migration, listing, synchronization, and peer chats. |
src/vs/platform/agentHost/node/agentServiceComposition.ts |
Supplies catalog persistence callbacks. |
src/vs/platform/agentHost/node/agentSessionRegistry.ts |
Adds dual-registry compatibility behavior. |
src/vs/platform/agentHost/node/agentSideEffects.ts |
Persists list-visible state changes centrally. |
src/vs/platform/agentHost/node/copilot/copilotAgent.ts |
Updates legacy adoption and metadata recovery. |
src/vs/platform/agentHost/node/localCommands/localChatCommand.ts |
Extends coordinated metadata persistence. |
src/vs/platform/agentHost/node/localCommands/renameLocalCommand.ts |
Persists title and title-source metadata together. |
src/vs/platform/agentHost/node/sessionCoordination.ts |
Integrates catalog-aware session coordination. |
src/vs/platform/agentHost/node/sessionDatabase.ts |
Adds synchronization snapshot storage and transactions. |
src/vs/platform/agentHost/test/common/sessionTestHelpers.ts |
Extends test database helpers for receipts. |
src/vs/platform/agentHost/test/node/agentHostCatalogListReader.test.ts |
Tests central row conversion and eligibility. |
src/vs/platform/agentHost/test/node/agentHostCatalogProjection.test.ts |
Tests canonicalization, limits, and verification. |
src/vs/platform/agentHost/test/node/agentHostCatalogReconciliationService.test.ts |
Tests repair and interrupted-write recovery. |
src/vs/platform/agentHost/test/node/agentHostCatalogShadowValidator.test.ts |
Tests shadow mismatch classification. |
src/vs/platform/agentHost/test/node/agentHostCatalogSourceResolver.test.ts |
Tests source and legacy metadata resolution. |
src/vs/platform/agentHost/test/node/agentHostCatalogSyncService.test.ts |
Tests synchronization ordering and recovery. |
src/vs/platform/agentHost/test/node/agentHostDatabase.test.ts |
Tests v2 schema and compatibility behavior. |
src/vs/platform/agentHost/test/node/agentHostGitStateService.test.ts |
Tests catalog-backed Git persistence. |
src/vs/platform/agentHost/test/node/agentHostPeerChatStore.test.ts |
Tests peer-chat storage and malformed data handling. |
src/vs/platform/agentHost/test/node/agentHostServices.test.ts |
Tests service dependency registration. |
src/vs/platform/agentHost/test/node/agentHostSessionTitleController.test.ts |
Tests coordinated title persistence. |
src/vs/platform/agentHost/test/node/agentHostTurnTelemetry.test.ts |
Updates telemetry fixtures for persistence wiring. |
src/vs/platform/agentHost/test/node/agentService.test.ts |
Tests rollout modes, migration, compatibility, and adoption. |
src/vs/platform/agentHost/test/node/agentServiceTestUtils.ts |
Extends AgentService database test doubles. |
src/vs/platform/agentHost/test/node/agentSessionRegistry.test.ts |
Tests dual-registry session lifecycle behavior. |
src/vs/platform/agentHost/test/node/agentSideEffects.test.ts |
Tests catalog persistence from state changes. |
src/vs/platform/agentHost/test/node/copilotAgent.test.ts |
Tests legacy adoption metadata behavior. |
src/vs/platform/agentHost/test/node/sessionCoordination.test.ts |
Tests catalog-aware coordination behavior. |
src/vs/platform/agentHost/test/node/sessionDatabase.test.ts |
Tests receipt migration and atomic persistence. |
src/vs/sessions/contrib/providers/agentHost/AGENT_HOST_SESSIONS_PROVIDER.md |
Documents the centralized catalog architecture. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 44/45 changed files
- Comments generated: 7
- Review effort level: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adopts the upstream Agent Host provider-service and chat-contribution refactors while keeping the session-catalog (Option D) work: - Rebases the durable `sessions.modified_time` column onto the sessions_v2 chain as migration 9, and adds `sessions_v2.modified_time` as migration 10 so the authoritative registry owns recency in both tables. - Threads `modifiedTime` through every sessions_v2 read, registration, envelope upsert and receipt, and makes `updateSessionModifiedTime` advance both registries in one transaction. - Moves provider registration onto `IAgentHostProviderService` while preserving catalog reconciliation scheduling and serialized discovery registrations, and routes deferred provider catalogs through the sessions_v2 importer. - Renames the catalog payload's artifact flag to `isArtifact` and adopts the upstream artifact parse contract. - Lets the catalog list reader serve the fresher of the payload and registry recency, and withholds the readable-catalog marker when an import pass leaves candidates unimported. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Base:
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…on-db-data-migration-plan
…on-db-data-migration-plan
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
roblourens
left a comment
There was a problem hiding this comment.
[Experimental performance review bot]
Automated experimental performance review.
(Written by Copilot)
Avoid creating local session storage for adoptable legacy chats discovered after migration is enabled. Directory creation makes the legacy provider hide sessions before explicit adoption. Preserve existing compatibility synchronization for locally stored sessions and non-legacy discovery. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Vijay Upadya (@vijayupadya) I have now reproduced the disappearance in a real Code OSS window using a macOS port of your supplied 800-session generator and the migration-OFF -> full restart with migration-ON sequence. The confirmed fix is pushed in 892b6e0. Root cause: the initial importer already avoided creating storage for unopened legacy sessions, but runtime discovery still used the storage-creating catalog synchronization path. An empty import completed with migration OFF leaves a persisted marker; after enabling migration, legacy sessions arrive through runtime discovery. That path created Fix: runtime discovery of adoptable legacy sessions now uses the non-creating catalog synchronization path. Absent local storage stays absent; existing local databases retain the coordinated compatibility/receipt path. Other runtime discovery behavior is unchanged. Live validation with fresh, isolated profiles:
After the fix, only the three explicitly opened ordinary legacy sessions acquired local Agent Host databases; unopened legacy sessions did not. The five external fixtures retained their normal separate discovery behavior. All 800 original conversation histories remained intact. Added a regression test for runtime discovery after a completed empty import: it fails before this change and passes afterward. Validation: 529 relevant unit tests passed, 16 skipped; targeted ESLint and commit hygiene passed. Correction to my earlier update: the title-generation fix in 3392bf9 addressed a real additional path, but its focused test did not reproduce or fully fix this 800-session runtime-discovery collapse. The above results are from the actual supplied dataset flow. These changes are not in production. The lingering bad state discussed earlier applies only to test profiles run against earlier buggy builds of this PR, not production users. Please retest with a fresh isolated user-data profile and regenerated dataset; retaining the old affected profile for diagnosis is fine, and no production recovery migration is required for this PR-only bug. The reported |
Compare summary status deltas against the notifier's previous snapshot and only queue catalog persistence when persisted fields change. Activity-only and transient status updates avoid database work while read/archive changes and explicit field clears remain synchronized. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge main through 58ba154, retaining central registry and peer membership authority, central-only legacy discovery, and projected summary synchronization. Adopt upstream automation service construction and cover batched artifact persistence and database-free catalog listing after restart. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stop reconciliation scheduling and drain accepted dispatches and catalog synchronization before shutting down providers and closing the central database. This preserves read/archive updates already published to clients across an immediate host restart without blocking normal passive notifications on catalog work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wait for asynchronous tool completions before normal SDK idle clears the active turn. Preserve immediate abort handling and original tool-result attribution when a replacement turn starts while file-edit persistence is pending. Add deterministic delayed-completion, abort, and replacement-turn regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Fixed the failing Electron checks with two ordering fixes:
No tests were disabled, fixtures rewritten, or timeouts increased. |
roblourens
left a comment
There was a problem hiding this comment.
[Experimental performance review bot]
Automated experimental performance review.
(Written by Copilot)
|
Sandeep Somavarapu (@sandy081)
|
Do not subscribe to untitled UI identities when Back returns to the new-chat input. Wait for the existing provisional-session service to publish a backend and follow its mapping changes, releasing obsolete subscriptions. Cover Back, provisioning, replacement, retirement, and return to a started session. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Vijay Upadya (@vijayupadya) Thanks for confirming that the disappearing sessions issue is resolved. I reproduced your new console error on the latest head in a real Code OSS window using your generated legacy dataset: all three legacy-open -> Back attempts produced The cause was the chat input metadata pills: Back creates a new untitled UI draft, and the pills converted that UI identity directly to a backend URI and subscribed to it before a corresponding backend existed. A debugger breakpoint confirmed Fixed and pushed in 3ec5b68. The pills now wait for an existing provisional backend mapping, use that backend URI, and follow mapping replacement/retirement without subscribing to an untitled UI identity. No database/adoption behavior was changed. Validation:
Please retry on this head; the earlier affected test profile can be used to verify this particular console-error fix. |
Merge main through 0af2bfd while retaining central session identity, peer membership, compatibility receipts, non-creating legacy discovery, shutdown drains, and provisional draft subscriptions. Adopt upstream per-turn edit-completion tracking and coverage. Verify restricted pull-request association changes persist to the central catalog and remain database-free on restarted listings; align architecture documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
roblourens
left a comment
There was a problem hiding this comment.
[Experimental performance review bot]
Automated experimental performance review.
(Written by Copilot)
Gate automatic reconciliation scheduling and discovery-driven verification until host startup and the first listing complete, then start deferred periodic maintenance. Preserve explicit refreshes and immediate durable mutations. Cover zero pre-startup maintenance I/O and replacement-turn completion independent of an aborted edit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📬 CODENOTIFYThe following users are being notified based on files changed in this PR: Ulugbek Abdullaev (@ulugbekna)Matched files:
|
Merge main through f3f750b, retaining central catalog and peer membership authority, startup-gated repair, shutdown drains, and provisional draft routing. Apply existing-session announcement suppression in the central importer, reconcile hydrated titles through the catalog, synchronize automatic archival immediately, and preserve repository-root cleanup with peer deletion fences. Adapt upstream regression tests and document the integrated persistence behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge main through 0febfb6. Preserve upstream serialized, durable artifact publication while routing user removals and tool mutations through ordered central-catalog synchronization. Adapt failure/concurrency tests to catalog receipts and verify empty artifact collections survive restarted database-free listing. Retain registry, peer membership, migration, startup, shutdown, and draft subscription fixes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Establish host startup and the first listing before waiting for background catalog repair in the persisted-title test, matching the startup-gated maintenance contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Check provider availability before taking the per-session exclusive open, so periodic reconciliation of sessions whose provider is not registered no longer opens their local database on every pass. Retry semantics are unchanged. Also un-nest the startup-gating test, which was defined inside another test and therefore never ran. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add chat.agentSessions.sessionCatalog.enabled, defaulting to true and frozen at its first read so the store backing the session list cannot change while the host runs. When disabled the host skips catalog import and background repair and lists from provider metadata and per-session storage, while registry identity and compatibility writes continue unchanged. Clear the verification marker while disabled so the next enabled start re-verifies every cached payload. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge main through bae4309. Retain central catalog authority, the rollback switch, startup-gated repair, and provisional draft routing. Carry main's session-config-aware changeset metadata into the listing fallback, and run its legacy per-kind blob migration in the catalog maintenance pass, where the database is already open, so listing stays free of per-session reads. Adapt the pills test to the merged constructor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Emit the catalog mode, the catalog-served and provider-fallback row counts, and the resolve phase duration on every listing, so a run with the session catalog enabled can be compared directly against one with it disabled. Catalog-served rows cost no provider or session-database read; fallback rows cost both. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`listSessions()` attaches a trailing refresh to an in-flight listing so that concurrent callers share the hand-off instead of each starting their own recomputation, and `clear()` keeps the entry reachable while that hand-off is attached. Nothing released the entry once the hand-off itself settled, so it stayed in `_inFlightListSessions` as a permanently settled result: every later listing was served from it and the host never recomputed again. A later epoch bump does not free it either -- `listSessions()` takes the `if (!inFlight.trailing)` = false path and returns the settled trailing verbatim -- so the only escape is a host restart. Release the entry once the trailing hand-off settles, which is the point after which no caller can join it. `clear()` and `_startSessionListComputation` are untouched, so the starvation fix from #333646 still holds, and `startTrailing()` replaces the map entry with its own computation, so the identity check leaves that fresh entry alone. This regression was introduced by this pull request, in e1bc2e3 ("agentHost: harden catalog reconciliation"), which added an epoch short-circuit to the trailing hand-off: - inFlight.trailing = inFlight.promise.then(startTrailing, startTrailing); + inFlight.trailing = inFlight.promise.then( + result => inFlight.epoch === epoch ? result : startTrailing(), + startTrailing, + ); Before that change `startTrailing` ran on every settle, and because `_startSessionListComputation` replaces the map entry, the pinned entry was always displaced by a fresh one whose `trailing` is unset -- so `clear()` could always collect it. With the short-circuit an unchanged epoch resolves to `result` and never calls `startTrailing()`, so nothing replaces the entry and it stays pinned forever. The retention in `clear()` came in earlier with 9e59633 ("[cherry-pick] Avoid Agent Host session listing starvation (#333646)") and is benign on its own, because that commit always replaced the entry. origin/main is therefore NOT affected and needs no separate fix; e1bc2e3 exists only on this branch. The cached-entry logic is mode-independent by construction -- `listSessions` and `_startSessionListComputation` never consult the session-catalog gate, and `_isSessionCatalogEnabled()` is only read inside `_computeSessions` for data sourcing -- so the fix applies in both catalog modes. The regression test reproduces the defect deterministically with the catalog disabled only, where the provider metadata round-trip gives a reliable point at which to hold a listing open; with the catalog enabled the same sequence races against projection writes that bump the epoch again. Observed in the field as an agent host that stopped recomputing listings entirely: six `listSessions` requests served in ~1ms with no computation at all after the host wedged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merges 19 upstream commits up to 60ac69b ("agentHost: trust Codex workspace hooks per thread"). The merge was conflict-free: only three files overlapped, each with additive changes in disjoint regions. - agentHostSchema.ts: upstream's workspaceTrust root config key alongside our AgentHostSessionCatalogEnabledConfigKey. - chat.shared.contribution.ts: upstream's dictation model/web changes alongside our ChatConfiguration.SessionCatalogEnabled registration. - copilotAgentSession.test.ts: upstream's turn-settings tests alongside our aborted-turn edit-completion test. No catalog adaptation was required: upstream touched none of the listing path, per-session metadata, or list payloads, so no legacy-listing improvement needed porting into the catalog-served path. The core catalog files (agentHostDatabase, agentHostPeerChatStore, agentHostCatalogSyncService, agentHostSessionsV2MigrationService) are untouched by this merge. Upstream dropped the customTerminalToolEnabled parameter from getAgentHostCopilotSandboxSettingId; all call sites already use the new single-argument form, so no call-site updates were needed on our side. Validation: typecheck-client clean; agentService 519 passing / 16 pending / 0 failing (matches the pre-merge baseline); catalog list-reader, projection, reconciliation, source-resolver and sync, peer chat store, sessionDatabase and agentSideEffects all green; copilotAgent(+Session) and session input pills green; the picker and sandbox-forwarder suites upstream changed green; the two host-restart integration tests pass on Codex, Claude and Copilot; eslint and git diff --check clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move `chat.agentSessions.sessionCatalog.enabled` to
`chat.agentHost.sessionCatalog.enabled`. The setting governs how the agent host
serves its session list, not the agent sessions UI, so it belongs in the
existing `chat.agentHost.*` namespace alongside the other host-level settings.
Tag it `advanced` as well as `experimental` so it surfaces under the advanced
filter: it is a rollback lever for the catalog read path rather than a tuning
option.
Register a configuration migration so an existing value carries over instead of
silently reverting to the default. The migration clears the old key and writes
the new one only when the new key is unset, so a value already set under the new
name is not clobbered -- the same shape as the existing
`chat.experimental.autoApprovals.enabled` migration.
The agent host root key `sessionCatalogEnabled` is unchanged: forwarding is
driven by the registration's `agentHost: { key }` through the configuration
registry rather than by the workbench setting id, so nothing on the host side
needs to move.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0a96803 to
08aae25
Compare
`chat.agentHost.sessionCatalog.enabled` is APPLICATION-scoped, so an existing value can live in the application settings file. The migration inspected only the user target, silently dropping such a value and resetting the user to the default. Follows the `unifiedWorkspacePicker` precedent. Also restate the wedge regression test's comment: the trailing refresh pins a settled entry because its success handler observes a matching epoch and returns the settled result rather than starting a replacement computation. That is an epoch-timing bug, not a catalog-mode one, which is why the catalog-enabled variant cannot fail -- projection writes bump the epoch again and heal the map before a test can observe it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. What this PR changes
Today,
listSessionsoften enumerates providers and opens many individualsession.dbfiles to obtain titles, workspace information, read state, changes, and chats.This PR makes
agent-host.dbthe central index for session-list data:sessions_v2: authoritative session identity, provider, timestamps, registration source, external state, and chat-backing identity.Full conversation data—including turns, drafts, attachments, annotations, and detailed edits—remains isolated in individual session and chat databases.
2. Listing and synchronization
Normal
listSessionscalls now read verified payloads directly fromagent-host.dbinstead of opening every conversation database.If one payload is missing, stale, or invalid:
Changes to titles, read state, workspace information, changes, or chats mark the payload dirty. Background reconciliation uses revisions and compare-and-set operations so delayed work cannot overwrite newer state.
3. Migration and compatibility
Existing sessions are imported from the legacy registry and provider discovery. Migration is resumable, tracked per provider, and normally runs in the background so the first list does not wait for the full migration.
The PR also protects upgrade and downgrade scenarios:
peerChats.All product schema changes are introduced in one new v5 migration, after upstream’s existing v4 migration. A separate normalizer handles databases produced by earlier versions of this draft PR.
4. Rollback setting
chat.agentHost.sessionCatalog.enabled(defaulttrue, tagged advanced) is the rollback lever for the catalog read path. When disabled, the host performs no catalog import and no background repair, andlistSessionsuses provider metadata and the per-session fallback instead of the cached payload.The value is frozen at its first read, so the store backing the list cannot change while the host runs and a change requires a full restart. Registry identity and every compatibility write run in both modes, so the two stores stay current and interchangeable and switching either way is safe and reversible. The verification marker is cleared while disabled, so the next enabled start re-verifies every row.
It is a safety switch rather than a tuning option: section 6 measures what it costs.
5. Testing
Coverage includes:
The affected unit suites pass, and the real-process compatibility matrix passed 20/20 scenarios.
6. Performance
Measured on one build (
0a96803ad20, content-identical to current08aae258ce2) with the catalog enabled versus disabled, so both arms share the same code, profile, and session set: ~645 registered sessions,mode='recent'. Every sample self-identifies its mode in thelistSessions computedlog line, so no arm is inferred from settings.Steady state n=34 enabled / n=22 disabled; cold start three matched quit-and-relaunch cycles per arm.
Every disabled listing performs 645 provider fallbacks and zero catalog reads; every enabled listing is the exact inverse, at roughly 2.85 database opens per session per listing. The win is replacing 645 per-session provider round-trips with a single batch read. It does not make reads free and it does not eliminate startup database I/O -- enabled mode still performs about 1,180 opens cold, which is why cold start is only 2.5x on database opens while steady state is 172x. The
resolve 0 msfigure means the resolve loop had nothing left to do, not that the catalog read is free: that read happens in an earlier batch phase, outside the measured window.Caveats: single profile and machine, so this is directional evidence rather than a benchmark. The cost is per-session and will not generalize to a much smaller session count, and only
mode='recent'was measured. The enabled steady-state window contained more chat-turn activity than the disabled one, and turn-triggered listings are faster (95 ms versus 161 ms median), so excluding them gives 3.8x rather than 4.2x. Cold start rests on three runs per arm: enough for a ratio, not a tight interval.These steady-state numbers were not collectable before the listing-cache fix below.
Listing-cache fix
A settled listing computation could be pinned in
_inFlightListSessions, after which every later listing was served from that stale entry and the host never recomputed until restart, so the session list silently stopped updating.This was a regression introduced by this PR in e1bc2e3 ("agentHost: harden catalog reconciliation"), not a pre-existing
mainissue.mainattaches its trailing refresh asinFlight.promise.then(startTrailing, startTrailing), where both handlers start a fresh computation that replaces the map entry, so the map self-heals. e1bc2e3 made the success handler conditional (result => inFlight.epoch === epoch ? result : startTrailing()), so on the matching-epoch path it returns the settled result without starting a replacement, and the stale entry is never evicted.Fixed in b7bc797 by releasing the entry once the trailing hand-off settles, with a regression test that fails before the fix and passes after.
7. PR size
The PR adds 18,315 lines, but approximately 11,547 lines—63%—are tests.
The remaining approximately 6.8K lines implement the database schema, catalog projection, migration, synchronization, reconciliation, chat membership, compatibility handling, and Agent Service integration. The size reflects the reliability work required for a persistence migration rather than the amount of data being moved.