Agent Host: background client auto-cancels side chat ask_user requests after 5s grace window
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
A background window that has an agent session open — but does **not** have that session's side chat open — auto-cancels the side chat's `ask_user` (chat input) requests after the 5s unobserved grace window. The cancellation is reported back to the model as an empty answer, so the agent sees blank responses and concludes the question UI is broken.
## Repro
1. Open an agent-host session (Copilot CLI provider) in window A.
2. Open the *same* session in window B, then leave B idle (do not open the side chat there).
3. In window A, create a side chat on that session and ask the agent to run an interactive Q&A (any flow that calls `ask_user`).
4. Answer the question in window A.
**Expected:** the answer reaches the agent.
**Actual:** ~5s after each question appears, it is cancelled. The tool result delivered to the model is `User responded: ` (empty). The agent retries and eventually gives up with something like "the question responses are arriving blank."
## Evidence
From an Agent Host debug log export (session `copilotcli:/7b1237b7…`, same day, same session):
| Chat | `ask_user` calls | Outcome |
|---|---|---|
| default chat | 35 | all answered normally |
| side chat `ed341250…` | 4 | **all blank** |
Every side-chat request was cancelled at almost exactly 5.00s after being raised:
```
08:22:27.480 [Copilot] User input request: requestId=26399d8f…
08:22:32.485 [AgentService] dispatchAction: type=chat/inputCompleted,
clientId=d8c2cb9c-dfc1-42ad-b48e-14043bb4e304, clientSeq=11
{"requestId":"26399d8f…","response":"cancel"}
08:22:32.486 [Copilot] User input response: response=cancel
→ chat/toolCallComplete result content: "User responded: "
```
Identical for the other three requests (clientSeq 12, 13, 14).
Key detail: the cancelling client `d8c2cb9c…` is **not** the window that rendered the question. The rendering window (connection `910c1d14…`) was correctly subscribed to the side-chat channel and rendered the carousel (`kind: questionCarousel`, `ChatListItemRenderer#renderQuestionCarousel`). The cancelling client's only other activity was a draft change in the session's *default* chat 11 hours earlier — it woke up solely to emit these four cancels.
## Cause
`_watchForSessionInputNeeded` in `src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts` runs in **every** client that has the session open. It is driven by the session-level `inputNeeded` queue, which by design contains blockers from *all* chats in the session, including side chats.
The `SessionInputRequestKind.ChatInput` branch (~line 2273) cancels after `UNOBSERVED_CLIENT_TOOL_GRACE_MS` (5000ms) unless the request was claimed:
```ts
if (cancelled || this._renderedRequests.get().has(inputKey)) { return; }
```
`_renderedRequests` is an **in-process observable, local to each window**. So "did anyone claim this?" actually means "did *I* claim this?". A background client cannot know another window is rendering the carousel, and its cancel wins the race.
The default chat is immune because every client with the session open renders it and therefore claims it. A side chat only exists as a chat editor in the window that created it, so a background client never claims it.
Note the sibling `ToolClientExecution` branch *does* have an ownership guard:
```ts
if (initial.clientId !== this._config.connection.clientId) {
return; // A different client owns this call.
}
```
The `ChatInput` branch has no equivalent.
Introduced in 180ee1eb594 ("agentHost: drive tool execution from the session input queue", #328989).
The `ToolAuthentication` (~line 2408) and confirmation-deny (~line 2427) branches use the same window-local `_renderedRequests` check, so side-chat MCP auth flows and tool confirmations are likely auto-cancelled/auto-denied the same way.
## Secondary issue
An `ask_user` cancellation surfaces to the model as `User responded: ` — an empty answer rather than an explicit cancellation signal. That is why the agent kept retrying (four times) instead of stopping: it had no way to distinguish "user submitted nothing" from "your request was cancelled". Cancellations should be distinguishable in the tool result.
## Workaround
Close the other window that has the session open, or open the side chat in that window too.
Contributor guide
Assessment
This issue has not been assessed yet.