Agent Host: Peer-chat subagent replay corrupts backend session URI
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
Does this issue occur when all extensions are disabled?: N/A — this is in the core Agent Host peer-chat restore path
- VS Code Version: Insiders, source containing `dfddfedcf4c9` / PR #326063
- OS Version: macOS
Steps to Reproduce:
1. Open a persisted Copilot CLI Agent Host session that has an additional/peer chat.
2. Spawn and complete one or more subagents from that peer chat.
3. Restart/reload the Agent Host and reopen the peer chat so its SDK history is reconstructed.
4. Observe history enrichment subscribing to a URI shaped like:
`ahp-chat:////subagent/`
Expected:
The restored tool call points to the canonical child chat URI:
`ahp-chat://subagent//`, and its inner calls are restored.
Actual:
History enrichment repeatedly fails. For example:
```text
Failed to enrich history with subagent calls:
ahp-chat://b50562c0-bc92-4739-b5df-6fd1db072283/Y29waWxvdGNsaTovMWI1MWY3YjEtM2YxNi00MzFlLTkzNTMtYjM2Y2RiNjhiOWQw/subagent/call_JJf7M32l2KevaEOd63visZCG
Session not found on backend:
copilotcli:/1b51f7b1-3f16-431e-9353-b36cdb68b9d0
```
The backend session exists and the peer chat resumes successfully immediately before these subscription failures. The persisted SDK event log also contains the completed subagent and its child events.
## Root cause
Commit `dfddfedcf4c9` (`agentHost: separate peer chat session and storage URIs`, part of #326063) changed `CopilotAgentSession` history reconstruction to use `_storageUri` instead of `sessionUri`:
- `_computeMappedEvents()` now calls `mapSessionEvents(this._storageUri, ...)`.
- `getSubagentSessions()` now uses `this._storageUri` as `parentSessionStr`.
For a peer chat, `_storageUri` is an `ahp-chat` channel URI, not a backend session URI. `mapSessionEvents()` passes that value to `buildSubagentSessionUri()`, which appends `/subagent/` and produces the malformed resource above.
When `parseChatUri()` later receives that URI, it treats the entire path—including `/subagent/`—as base64. `/` and the suffix letters are valid base64 characters, so decoding does not throw. It decodes the first segment to the valid `copilotcli:/...` URI and appends deterministic binary garbage from the suffix. `AgentService.restoreSession()` then correctly fails metadata lookup for that corrupted session URI.
This was reproduced byte-for-byte by decoding the malformed path. The AHP snapshot confirms the restored tool has:
- `_meta.toolKind: "subagent"`
- no `_meta.subagentChatUri`
- a `ToolResultSubagentContent.resource` containing the malformed peer-chat-plus-session-suffix URI
There is a second exposure point: `CopilotAgent.getSubagentSessions()` deliberately returns no children for non-default chats, so peer-chat subagent state is not eagerly registered and no canonical catalog resource overrides the malformed replay fallback.
The same logical-session/storage-routing conflation also makes replay derive peer-chat MCP identities from scheme `ahp-chat` and the encoded path rather than the actual provider/session ID.
## Suggested fix
- Keep logical backend `sessionUri` separate from storage/routing URI in replay mapping.
- Generate and stamp `buildSubagentChatUri(parentSessionUri, toolCallId)` for restored subagent tool metadata.
- Restore/register subagent chat state discovered in peer-chat history.
- As defense in depth, make `parseChatUri()` reject unexpected extra path segments for non-subagent authorities rather than decoding them as base64.
Changing only `parseChatUri()` would avoid the binary corruption but would not restore peer-chat subagent history; the malformed resource must be fixed at its producer.
Contributor guide
Assessment
This issue has not been assessed yet.