Agent Host loses conversation context when config refresh resume falls back to createSession
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
- Copilot Chat Extension Version: N/A (built-in Agent Host)
- VS Code Version: 1.139.0-insider (`046944034292b5479b4e9a50ad1a508033ffb64f`)
- OS Version: macOS arm64
- Feature (e.g. agent/edit/ask mode): Agent Host, follow-up in an existing Copilot session
- Selected model (e.g. GPT 4.1, Claude 3.7 Sonnet): GPT-5.6 Sol
- Copilot runtime: `1.0.84-5`
- Logs: Agent Host debug bundle captured locally; sanitized causal timeline is included below.
Steps to Reproduce:
1. Open an existing Agent Host session with prior conversation history.
2. Cause the active client plugin snapshot to change structurally before sending the next message. In the captured run, this caused `ActiveClient.requiresRestart(...)` to refresh the SDK session with `reason=clientPluginsChanged`.
3. Send a context-dependent follow-up, such as “Was there a fix in the runtime?” after the previous response diagnosed a runtime bug.
4. Observe that the UI keeps the existing transcript, but the assistant behaves as if the prior conversation never happened and asks which runtime the user means.
## Expected
A configuration-driven SDK-session refresh preserves the established conversation. If resume fails, Agent Host should rehydrate the replacement from durable history or fail visibly rather than continue with an empty model context.
## Actual
The refresh shut down the old SDK session, failed to resume it, and silently fell back to `createSession` with the same ID. The replacement reconstructed zero prior turns, so the follow-up was sent to the model with only the system message and the new user message.
The UI still displayed the old transcript, making the newly empty model context invisible to the user.
## Causal timeline
Session: `e795b6a7-872d-4a67-8916-4f8ac7c80591`
Before the follow-up, the session resumed normally:
```text
08:36:55.809 Preparing SDK session: kind=resume
08:36:58.012 SDK resumeSession settled: outcome=success
08:36:58.021 Read 304 persisted event(s); reconstructing turns
08:36:58.023 Reconstructed 1 turn(s) from 304 event(s)
```
Submitting the follow-up triggered a config refresh:
```text
08:38:09.474 Session configuration changed, refreshing session: operation=sendMessage, reason=clientPluginsChanged
08:38:09.474 SDK session disconnect RPC started: disconnectRpc=pending, shutdownReceived=false
08:38:09.529 SDK session shutdown received: disconnectRpc=pending, shutdownReceived=true
08:38:09.529 SDK session disconnect wait completed: disconnectRpc=pending, shutdownReceived=true
08:38:09.532 Preparing SDK session: kind=resume
08:38:09.535 SDK resumeSession started
08:38:09.878 SDK session disconnect RPC completed
08:38:09.882 SDK resumeSession settled: outcome=error
08:38:09.882 SDK resumeSession failed: session not found
08:38:09.882 Resume reported no session history; falling back to createSession with same ID
08:38:10.056 Fallback createSession succeeded
08:38:10.057 Reading persisted session events
08:38:10.058 Read 3 persisted event(s); reconstructing turns
08:38:10.058 Reconstructed 0 turn(s) from 3 event(s)
08:38:10.061 sendMessage called: "Was there a fix in the runtime?"
```
The runtime confirms that it cleaned up the prior session and then created a new session with the same ID:
```text
15:38:09.528 Cleaning up session ...: Session closed after last owner detached
15:38:09.636 Closing session ...
15:38:09.877 session.dispose
15:38:09.882 session not found
15:38:09.908 Received session.create request ...
15:38:10.022 Created session ...
```
The resulting model call reported only two messages in context. It loaded a generic search skill and asked:
> Which runtime or feature are you referring to? A PR, issue, thread, or error message would help me check the right context.
## Analysis
This appears to be a teardown/resume race combined with an unsafe fallback:
1. A structural client-plugin change refreshes the SDK session during `sendMessage`.
2. Agent Host treats the `session.shutdown` notification as sufficient teardown completion while the disconnect RPC is still pending.
3. Resume begins before the old disconnect RPC fully settles.
4. The runtime reports `session not found`.
5. Agent Host creates a replacement session with the same ID, but only the new session's three startup events are available to turn reconstruction.
6. Sending proceeds despite reconstructing zero turns from a previously established conversation.
Even if the resume failure is expected, continuing with an empty context is data loss from the model's perspective and should not be success-shaped.
## Related issues
- #327125 fixed an earlier config-refresh teardown/resume race, but that issue resulted in termination/hanging rather than a successful-looking empty continuation.
- #328590 limited destructive handling of transient resume failures; the history here remains visible in the UI, but is not rehydrated into the replacement SDK session.
- #331118 covers a first-turn config refresh that leaves a chat active; this report concerns an established session silently losing prior turns while the follow-up continues.
Contributor guide
Research direction
Start by tracing the Agent Host session refresh path described in the causal timeline, especially disconnect completion, resumeSession, and the createSession fallback. Reproduce the clientPluginsChanged refresh and inspect how persisted events are reconstructed after resume reports “session not found.” Done means an established conversation is preserved after refresh, or the follow-up fails visibly instead of continuing with an empty model context.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100