Wait for complete sandbox credentials before connecting - #332455
Merged
Osvaldo Ortega (osortega) merged 3 commits intoAug 25, 2026
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds bounded credential re-minting while waiting for a sealed GitHub token.
Changes:
- Retries
/connectup to 12 times. - Falls back to the latest credentials.
- Adds focused unit tests.
Show a summary per file
| File | Description |
|---|---|
cloudSandboxAgentHostService.ts |
Implements sealed-token waiting and diagnostics. |
cloudSandboxAgentHostService.test.ts |
Tests successful, exhausted, and unnecessary retries. |
Review details
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Suppressed comments (2)
src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts:248
- This method JSDoc repeats implementation and failure history rather than concisely documenting the contract. Reduce it to 1–2 sentences per the repository comment guidelines.
/**
* Re-mint credentials until they carry a sealed GitHub token.
*
* A newly provisioned environment answers `/connect` as soon as the compute is up, which can be
* before Mission Control has learned the host's sealing key. The credentials are valid, but
src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/cloudSandboxAgentHostService.test.ts:115
- This inline comment also exceeds the one-line limit. Keep only the non-obvious reason for the
+ 1expectation.
// Bounded rather than an unbounded loop, and the connection still proceeds without a
// sealed token. The initial mint plus one re-mint per retry.
- Files reviewed: 2/2 changed files
- Comments generated: 5
- Review effort level: Balanced
Mission Control seals the user's GitHub token to a key the host generates at startup and advertises on `register` and every heartbeat. A freshly provisioned environment can answer `/connect` before that key has propagated, returning credentials with no `encrypted_github_token`. Nothing about that looks like a failure: the relay opens and the AHP handshake completes. But without the sealed envelope the client never sends `authenticate`, so nothing establishes who the agent acts as and the host rejects every session request with `-32007 AuthRequired`. Credentials are now re-minted until they carry the envelope. The bound is sized against the daemon's own timings rather than guessed: `copilotd` heartbeats every 30s and backs off register attempts up to 60s, so 12 attempts at 5s spans a full register backoff and two heartbeats. A warm environment seals on the first mint and never enters the loop. An environment may legitimately never seal a token, so this degrades rather than failing: the last credentials are returned either way, and the degraded case is logged where the cause is visible rather than leaving only the downstream `AuthRequired` symptoms. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The re-mint loop tested truthiness while `_establish` refuses anything that is not a `copilot-sealed.v1.` envelope, so a non-empty plaintext value ended the loop and left the connection unauthenticated — the state this is meant to avoid. Both now use the same predicate. A transient failure from the re-mint also rejected `connect()`, discarding initial credentials that already work and contradicting the documented degraded fallback. Non-cancellation failures are now logged and the last token is used; only caller cancellation aborts. Adds coverage for the unsealed value, the failed re-mint, and the environment returning to waking mid-wait, which had no test. Trims comments to the repository's limits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Osvaldo Ortega (osortega)
force-pushed
the
osortega/sandbox-await-sealed-token
branch
from
August 25, 2026 02:53
a5d10cd to
c782de0
Compare
The constant documented the exact backend retry and heartbeat intervals it was sized against, and nearby comments described how credentials become complete. Say what the client does and why the bound is derived rather than guessed, without publishing service internals in shipped source. Behaviour is unchanged; comments and one log message only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Osvaldo Ortega (osortega)
marked this pull request as ready for review
August 25, 2026 03:27
Dmitriy Vasyura (dmitrivMS)
approved these changes
Aug 25, 2026
Osvaldo Ortega (osortega)
deleted the
osortega/sandbox-await-sealed-token
branch
August 25, 2026 03:40
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.
Connecting to a freshly provisioned sandbox could leave the session unable to make authenticated requests. Nothing about it looked like a failure — the relay opened, the handshake completed, the connection registered — but every session request against it then failed.
The credentials a new environment returns can be incomplete for a short window after it comes up.
What this does
Re-mints credentials until they are complete.
The bound is derived, not guessed. It is sized to cover the backend's own registration retry cycle, so a warm environment succeeds on the first mint and never enters the loop.
It degrades rather than failing. An environment may legitimately never return the missing piece, and a session with reduced capability beats refusing to connect at all. The last credentials are used either way, and the degraded case is logged so the cause is visible rather than leaving only downstream symptoms to puzzle over.
If the environment drops back to "waking" mid-wait, the loop stops rather than re-entering the wake path; the handshake watchdog already covers a host that has gone away. A transient failure while re-minting is also non-fatal — the credentials already in hand are used instead of discarding them.
Risk
Only reachable when the initial credentials are incomplete, which is the broken case today. A warm environment takes the same path it always did.
Validation
npm run typecheck-client,eslinton both changed files./scripts/test.sh --glob "**/cloudSandboxAgentHostService.test.js"— 7 passing, covering: re-mint until complete, give up and connect anyway, no re-mint when the first mint is already complete, a value that is present but unusable, a failed re-mint, and the environment returning to waking mid-wait.Each behavioural change was checked against the case it is meant to handle by reverting it and confirming the matching test fails.