microsoft / microsoft/vscode

Agent Host chat stays active after response completes while an attached async shell is running

Open
#336,348 1 comment 0 reactions 1 assignee Claimed by @roblourens View on GitHub
agent-host bug
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

## Problem

In Copilot Agent Host chat, the response finishes but the chat progress indicator keeps spinning while an attached asynchronous shell command remains alive. Later messages can be treated as steering messages even though the previous answer has finished.

A long-running preview server triggered this in a real conversation. Even a subsequent tool-free response remained open for more than 48 minutes. Stopping the server restored normal turn completion.

## Environment

- VS Code Insiders 1.138.0, commit `37a7dda8100be458b06583283b1d7c88b787da69`
- macOS, Apple Silicon
- Bundled Copilot runtime: `1.0.84-4`
- Reproduction script tested with Node.js `v24.18.0`

## UI reproduction

1. In Copilot Agent Host chat, ask the agent to start a long-lived command using the built-in `bash` tool with `mode: "async"`, without detaching it, then finish its answer.
2. Observe that the answer finishes but the chat turn remains active.
3. Send a short follow-up requiring no tools. The answer finishes, but its turn also remains active.
4. Stop the attached command. Subsequent foreground turns complete normally.

Expected: the foreground response completes independently of a deliberately long-lived background command. Background activity can retain its own indicator.

Actual: the foreground chat turn stays open until background work stops, another message forces a turn boundary, or the user cancels.

## Standalone reproduction script

**[View/download `repro-attached-shell-idle.mjs`](https://gist.github.com/jruales/2de149a4746f44a1450e79aef4e0d5ec)**

The linked Gist contains only the reproduction script. No conversation logs or Agent Host log archive are included. GitHub's direct file-upload API was unavailable, so the script is provided as a downloadable Gist rather than an inline issue attachment.

Run:

```sh
node repro-attached-shell-idle.mjs
```

The script defaults to the bundled runtime under the macOS Apple Silicon VS Code Insiders application. For another installation, set `IDLE_REPRO_RUNTIME` to the package directory containing both `index.js` and `copilot-sdk/index.js`.

It uses the installed SDK/runtime with a deterministic OpenAI-compatible HTTP stub bound to loopback. It does not make real model inference requests or require model credentials. It creates isolated temporary runtime data, runs bounded attached `sleep 30` commands, explicitly stops them, shuts down its runtime, and removes temporary data.

For each of three fresh sessions, it:

1. Sends a no-background-work control message.
2. Starts an attached asynchronous shell via the native built-in tool invocation pipeline.
3. Sends two consecutive messages while that command is still running.
4. Checks for `assistant.idle` and the absence of `session.idle` for two seconds after each answer.
5. Stops the command and checks that `session.idle` arrives.

### Measured results

| Trial | Control: answer to session idle | Completed answers withholding session idle | Stop command to session idle |
|---|---:|---:|---:|
| 1 | 1 ms | 2/2 | 5 ms |
| 2 | 1 ms | 2/2 | 3 ms |
| 3 | 1 ms | 2/2 | 2 ms |

All six affected answers emitted `assistant.idle`, but none emitted `session.idle` during the observation window while the shell was running. All three controls and recoveries passed.

**Scope:** this is a repeatable SDK/runtime event reproduction, not an automated VS Code UI test. The UI symptom was observed in the real chat. After stopping its preview server, the final answer was followed by Agent Host `Session idle` and protocol `chat/turnComplete` within 314 ms.

## Investigation

The runtime intentionally distinguishes:

- `assistant.idle`: the foreground agent loop has finished, even if related background work remains.
- `session.idle`: foreground and relevant background work are idle; running attached shell commands defer this event.

In the installed VS Code build, [`CopilotSessionWrapper.onIdle`](https://github.com/microsoft/vscode/blob/37a7dda8100be458b06583283b1d7c88b787da69/src/vs/platform/agentHost/node/copilot/copilotSessionWrapper.ts) subscribes to `session.idle`. The corresponding [`CopilotAgentSession` handler](https://github.com/microsoft/vscode/blob/37a7dda8100be458b06583283b1d7c88b787da69/src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts) completes the active chat turn. The installed Agent Host bundle has no `assistant.idle` handling.

This appears to conflate foreground turn completion with all-session idle. Please investigate handling root foreground idle separately from background activity, preserving cancellation, pending tool-result completion, and stale-event guards. Completing on every `assistant.turn_end` is not equivalent: that event also occurs between model/tool iterations.

Proposed fix and regression-test checklist

This is a proposed implementation direction, not an implemented or validated patch. The fix should be in VS Code's Copilot Agent Host adapter, preserving the runtime's distinction between foreground idle and whole-session idle.

### Implementation approach

1. Add a typed `assistant.idle` subscription in `CopilotSessionWrapper`. Use root-agent foreground idle to finish the corresponding foreground chat turn, even while attached background commands remain alive.
2. Refactor `CopilotAgentSession`'s current idle handler to separate foreground completion from whole-session cleanup. Reuse the appropriate completion bookkeeping, while retaining `session.idle` for whole-session cleanup and relevant fallback cases. Foreground completion must not imply that background shells or agents have stopped.
3. Preserve lifecycle guards rather than replacing the event name blindly:
- Ignore subagent idle events when completing the parent response.
- Wait for pending file-edit/tool-result processing before emitting the terminal chat action.
- Do not convert cancellation into successful completion.
- Do not let an older idle event close a newer or queued turn, including across asynchronous completion drains.
- Complete each turn exactly once when both idle events arrive.
- Ensure later background completion notifications can start a new response and still be attributed correctly.
4. Audit the consequences of `_clearActiveTurn()`: its `_onTurnEnded()` callback also notifies lifecycle bookkeeping that can release a deferred CLI restart. Completing the foreground turn earlier must not inadvertently terminate background work through that path. Keep background activity visible independently where appropriate.

Do not use every `assistant.turn_end` as the completion signal: it also occurs between model/tool iterations.

### Regression tests

- [ ] A foreground answer completes while an attached command remains running.
- [ ] A second user message completes normally with that same command still alive.
- [ ] Stopping the command, and receiving the later `session.idle`, does not emit duplicate completion.
- [ ] Subagent idle events do not complete the root turn.
- [ ] Cancellation remains cancellation, including when a replacement/queued turn exists.
- [ ] Stale idle events cannot complete a newer turn.
- [ ] Pending file-edit/tool-result completion is drained before closing the turn.
- [ ] Background-agent and shell notifications can start subsequent responses without losing attribution.
- [ ] Deferred restart/disposal handling does not kill background work merely because the foreground answer finished.
- [ ] A real Agent Host integration/UI reproduction confirms that foreground progress stops while the background command stays alive.

The standalone SDK reproduction should continue passing unchanged: withholding `session.idle` while an attached command runs is intentional runtime behavior. The VS Code regression test should verify that this no longer leaves the foreground chat response active.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.