Repeated Enter in the slow /resume picker splits a session: an extension's session.send wakes an invisible context (not the visible chat), which also loses tools
- Dominant language
- Shell
- Stars
- 11.2k
- Forks
- 1.9k
- Avg merge
- 14h 16m
- Merged PRs (30d)
- 6
Description
## Summary
After resuming a long-lived session, pressing **Enter multiple times in the `/resume` picker** appears to bind more than one active *context* to the same session. Afterward, when an extension calls **`session.send(...)`** to wake the session with an incoming prompt, the resulting turn is handled by a context that is **not** the one visible in the interactive TUI — so the user never sees it. The visible chat can also **lose extension-provided tools** (a tool that worked earlier later returns *"tool does not exist"*). It is a **single OS process and a single session id** throughout — the split is between contexts *within* the session, not between processes.
## Concrete setup (repro vehicle)
Observed with **[agent-relay](https://github.com/joniba/agent-relay)**, an extension that delivers messages from *other* Copilot CLI sessions into this one as prompts via `session.send` (so a peer session can "message" this session). This makes the bug easy to see: a peer sent several messages, and the agent **received and replied to each** — generating responses and calling the extension's send tool to reply. But all of that happened in the **invisible** context, so **none of it appeared in the user's visible chat**, even though the user was actively watching the session. The agent wasn't "acting on its own" — it was answering prompts the extension delivered; the bug is that those prompts (and the agent's answers) went to a context the user can't see or control.
## Environment
- Copilot CLI **v1.0.64-0** (also seen on 1.0.62–1.0.63)
- OS: Windows · Node: v24.x
- Session: long-lived — **~16 resumes** over several days, with **several auto-compactions**
## What seems to trigger it
1. The `/resume` picker is **slow to load** (waits on MCP/extension connections), so the **first Enter appears to do nothing**.
2. Pressing Enter again (and/or arrow-up/down → Enter) issues **additional resume attempts**.
3. The session log then records **multiple `session.resume` events very close together** — observed **3 within ~30 seconds**, two of them sharing the same parent event.
## Steps to reproduce (best-effort)
1. Install an extension that calls `session.send(...)` to inject prompts — e.g. [agent-relay](https://github.com/joniba/agent-relay), which delivers messages from other sessions as prompts.
2. Keep a session alive long enough to accumulate several resumes and auto-compactions.
3. Resume it via the `/resume` picker; while it's slow, **press Enter multiple times**.
4. Trigger the extension to `session.send(...)` (e.g. have another session message this one).
5. Observe where the resulting turn goes — the visible chat, or only the event log.
## Expected
`session.send(...)` wakes the **visible** chat; the injected prompt and the agent's handling appear in the interactive TUI (the normal path, cf. #2065).
## Actual
- The injected prompt is handled by a **separate, invisible context**: the turn is written to the session event log but **never rendered** in the TUI.
- The visible chat context **concurrently loses extension-provided tools** — the **same extension tool succeeded from one context and ~14 minutes later failed with *"tool does not exist"* from the visible context**, same process + session id, while the invisible context still had the tool.
- Both contexts append to the **same session event log**; the invisible turns are later **compacted out**, so neither the user nor the resumed visible context retains them.
## How it was diagnosed
- Verified a **single OS process and single session id** owned the session (process parent-chain + open-handle inspection) — ruling out duplicate processes.
- The event log showed: incoming `session.send` prompts with **no corresponding visible response**; the extension's send-tool **succeeding from one context and failing from the visible context minutes apart**; **multiple `session.resume` within 30s**; and several **compaction** events.
- Each turn carried its own fresh system prompt + task id, consistent with per-turn agent invocations sharing one session log.
## Impact
- Incoming prompts delivered by an extension are **answered by an invisible context** — responses are generated and tools are called (e.g. messaging peers back) with **no visibility or approval** in the user's chat.
- The visible session **silently loses tools** and can't see or control what the other context does in the user's name.
- Affected turns are **lost to compaction**, leaving no in-chat record.
## Related
- #2065 — extensions executing tool-calls without waking the chat (the normal "wake the chat" path this deviates from)
- #2543 — concurrent sub-agent events corrupting session state
- #1820 — tool set stripped mid-session
## Possible mitigations / asks
- **De-bounce / lock** the `/resume` action so repeated Enter can't bind multiple contexts to one session.
- Make `session.send` **always target the user-visible context**, or clearly **indicate** when a non-visible context handles a turn.
- Don't let a session's **tool set silently diverge** between contexts.
Contributor guide
Research direction
Start at the `/resume` picker and trace how repeated Enter presses create `session.resume` events and bind contexts. Then follow `session.send(...)` through context selection and compare it with the visible TUI path described in #2065. Done means repeated resume attempts cannot split one session, injected prompts reach the visible context, and tools do not diverge between contexts.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100