Keep locally provisioned sessions out of the list until they are real - #332457
Merged
Osvaldo Ortega (osortega) merged 4 commits intoAug 25, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Prevents locally provisioned sandbox sessions from duplicating or disappearing before the host recognizes them.
Changes:
- Withholds provisional sessions from listings while keeping them resolvable.
- Adds bounded eviction protection and host-list reconciliation hooks.
- Publishes sessions when placeholders retire, including failed-send handling.
Show a summary per file
| File | Description |
|---|---|
baseAgentHostSessionsProvider.ts |
Adds overridable eviction hooks. |
remoteAgentHostSessionsProvider.ts |
Tracks withheld and provisional sessions. |
cloudSandboxAgentHostContribution.ts |
Seeds provisioned sessions provisionally. |
copilotChatSessionsProvider.ts |
Publishes sessions during placeholder retirement. |
remoteAgentHostSessionsProvider.test.ts |
Tests listing and eviction behavior. |
cloudSandboxAgentHostContribution.test.ts |
Updates provisioning test provider. |
copilotChatSessionsProvider.test.ts |
Tests successful and failed publication. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 7/7 changed files
- Comments generated: 2
- Review effort level: Balanced
A provider that creates a session remotely has to seed it into its cache before connecting, so a later discovery pass reconciles against it instead of creating a second entry. But the caller is still showing an optimistic placeholder row for that same session until the first turn is dispatched, so publishing the seed straight away listed the session twice for as long as the environment took to wake (~8s observed). Retiring the placeholder earlier is not an option: swapping when the session merely exists hands the UI a session it cannot open yet and the view falls back to the new-session screen. So the seed is now withheld from `getSessions` instead, and revealed as the placeholder is retired. The reveal is silent because the list re-reads on any change, letting a single event swap both rows rather than listing both for a frame. Withheld sessions stay reachable by resource, so opening one still works. Separately, such a session could be evicted before it existed on the host. Connecting triggers a reconciliation that prunes anything cached but not listed, and the session id is minted before the host materializes the session, so that first listing can legitimately omit it. Evicting there dropped the row the user was looking at and bounced the view to the new-session screen, re-adding the session seconds later. Sessions seeded as provisional now resist that eviction until the host lists them. The base provider already made this exemption for a sibling case, so that is generalized into an overridable hook rather than adding a parallel mechanism. The protection is bounded: without a deadline, a session the host will never list becomes a permanent row that only a reload clears, trading a visible flash for an invisible one. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two ways the provisional guard could leave a session worse off than no guard at all. A failure after the seed — a rejected connect, or teardown while the sandbox wakes — returned before the caller received a provider, so nothing could publish the seed. Discovery does not rescue it either: a later pass finds the entry already in the cache and only backfills its project. The session existed remotely and was invisible until reload. It is now published on the way out, while the provider is still the registered one. The grace period also started at seed time, before connecting. Waking a sandbox can take minutes, so a slow wake burned the whole period before the host had said anything, and the first listing that omitted the session met an already-expired deadline — evicting it immediately and recreating the disappearance the guard exists to prevent. The clock now starts when a connected host first omits the session, which is what the grace is actually measuring. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Osvaldo Ortega (osortega)
force-pushed
the
osortega/sessions-list-provisional-sessions
branch
from
August 25, 2026 02:53
bd4be98 to
0b715a0
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The withheld/provisional handling lived on RemoteAgentHostSessionsProvider, which SSH and tunnel connections also use, even though only the cloud sandbox calls any of it. Those connections carried ~95 lines of state and API that mean nothing to them. It now lives in CloudSandboxSessionsProvider, wired through the contribution's existing provider-construction seam, and RemoteAgentHostSessionsProvider is byte-identical to before this change. What stays in the base provider is the pair of eviction hooks, which default to "evictable" and a no-op. Eviction happens inside `_refreshSessions`, so a subclass can only influence it from there, and the base already made this exact exemption for its own pending session. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Osvaldo Ortega (osortega)
marked this pull request as ready for review
August 25, 2026 04:07
Osvaldo Ortega (osortega)
enabled auto-merge (squash)
August 25, 2026 04:09
Dirk Bäumer (dbaeumer)
approved these changes
Aug 25, 2026
Osvaldo Ortega (osortega)
deleted the
osortega/sessions-list-provisional-sessions
branch
August 25, 2026 07:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two list bugs with the same root: a session that exists remotely before the host knows about it.
1. The row appeared twice for ~8 seconds
A provider that creates a session remotely seeds it into its cache before connecting, so a later discovery pass reconciles against it instead of creating a second entry. But the caller is still showing an optimistic placeholder row for that same session until the first turn is dispatched — so publishing the seed straight away listed the session twice while the environment woke.
Measured on a real run: seed at
19.658, placeholder retired at27.615.Retiring the placeholder earlier is not an option — swapping when the session merely exists hands the UI a session it cannot open yet and the view falls back to the new-session screen. That was tried and reverted.
So the seed is withheld from
getSessionsinstead, and revealed as the placeholder is retired:getSessionByResourcereads the cache directly, so opening still resolves — hidden from the list, not from the app.2. The row vanished, the view went home, then it came back
Connecting triggers a reconciliation that prunes anything cached but not listed by the host. But the session id is minted before the host materializes the session, so that first listing can legitimately omit it. The prune landed ~400ms after the placeholder swap, deleting the row the user was looking at:
Sessions seeded as provisional now resist that eviction until the host lists them. The base provider already made this exemption for a sibling case ("some hosts briefly omit the just-sent eager session"), so that is generalized into an overridable hook rather than adding a parallel mechanism.
The protection is bounded (2 min). Without a deadline, a session the host will never list becomes a permanent row that only a reload clears — trading a visible flash for an invisible one. Both directions are tested.
Risk
_isSessionEvictabledefaults totrueand_onHostListedSessionsis empty, so every provider that does not opt in behaves exactly as before. Only sessions seeded withprovisionaltake the new paths.Validation
npm run typecheck-client,npm run valid-layers-check./scripts/test.sh --glob "**/vs/sessions/**/*.test.js"— 2469 passing