github / github/copilot-cli

ACP drops subagent source identity and flattens parallel output into the parent stream

Aperta
#4,106 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
area:agents area:non-interactive
Lingua principale
Shell
Stelle
11.2k
Fork
1.9k
Merge medio
14h 16m
PR unite (30g)
6

Descrizione

### Describe the bug

When `copilot --acp` runs multiple synchronous subagents in parallel, subagent text deltas are forwarded into the parent ACP session as ordinary `agent_message_chunk` updates, but their source identity is removed.

Copilot's internal/SDK events already distinguish these messages with `agentId` and `data.parentToolCallId`. The outer `task` tool calls also have stable, distinct `toolCallId` values. However, the ACP notifications for the corresponding text contain only:

```json
{
"method": "session/update",
"params": {
"sessionId": "",
"update": {
"sessionUpdate": "agent_message_chunk",
"content": {
"type": "text",
"text": "..."
}
}
}
}
```

There is no `_meta`, `parentToolCallId`, `agentId`, message ID, or child session ID on these updates. Consequently, an ACP client cannot tell whether a chunk came from the primary agent, subagent A, or subagent B. When parallel subagents stream concurrently, their text can be interleaved and rendered as a corrupted primary-agent response.

This appears to be an ACP adapter regression/gap rather than a core event-model limitation. In particular, #2265 was closed after newer SDK versions added `parentToolCallId` to `assistant.message` and `assistant.message_delta`; that field is available before the ACP mapping but is not preserved in the ACP output.

### Affected version

GitHub Copilot CLI `1.0.71-0`

### Steps to reproduce the behavior

1. Start Copilot in ACP stdio mode:

```bash
copilot --acp
```

2. From an ACP client, call `initialize`, then `session/new`.

3. Send a prompt that requires exactly two synchronous Explore subagents to run in parallel, with distinguishable output. For example:

```text
Launch exactly two Explore subagents concurrently.

Subagent A must return a long report beginning with ALPHA_SUBAGENT_START
and ending with ALPHA_SUBAGENT_END.

Subagent B must return a long report beginning with BETA_SUBAGENT_START
and ending with BETA_SUBAGENT_END.

Wait for both, then reply PRIMARY_FINAL.
```

4. Record every `session/update` notification without transforming it.

5. Observe that ACP first emits two distinct outer `tool_call` updates. Each has a different `toolCallId` and a `rawInput` object identifying the subagent invocation, for example:

```json
{
"sessionUpdate": "tool_call",
"toolCallId": "",
"status": "pending",
"rawInput": {
"name": "alpha-probe",
"agent_type": "explore",
"description": "...",
"prompt": "..."
}
}
```

6. While those tasks run, observe many `agent_message_chunk` notifications. All of them have the exact same field shape and none carries either outer task's `toolCallId` or another source identifier.

7. Compare with the session's native events. The corresponding subagent events are distinguishable:

```json
{
"type": "assistant.message",
"agentId": "",
"data": {
"parentToolCallId": "",
"interactionId": "",
"content": "ALPHA_SUBAGENT_START..."
}
}
```

In one controlled run with two parallel Explore subagents, ACP emitted 2,333 `agent_message_chunk` notifications. Every chunk had only `sessionUpdate`, `content.type`, and `content.text`. No source metadata was present.

The final outer `tool_call_update` for each task does contain that subagent's complete, correctly separated response in `rawOutput.content`, and it exactly matches the corresponding native `assistant.message`. However, this arrives only at completion, after the unscoped deltas have already been emitted into the parent message stream.

### Expected behavior

ACP clients should be able to distinguish primary-agent output from each subagent's output.

A minimal backward-compatible fix would be to preserve the identity already present on the native event, for example in `_meta` on the ACP notification or update:

```json
{
"_meta": {
"copilot": {
"agentId": "",
"parentToolCallId": ""
}
}
}
```

Subagent lifecycle events should ideally also be exposed so clients can create and finish the correct UI activity item.

Longer term, this could align with ACP's child-session/subagent direction. Until source-aware ACP updates are available, a safer fallback would be to suppress subagent message/reasoning deltas from the parent ACP message stream rather than emit them as indistinguishable primary-agent chunks. The completed task result is already available through the corresponding outer `tool_call_update.rawOutput`.

### Additional context

- Reproduced on Linux x86_64 using ACP over stdio.
- This is most visible with two or more parallel synchronous subagents because their streamed text may interleave.
- Primary-agent output is also represented as the same unscoped `agent_message_chunk` shape, so an ACP client cannot safely filter or route individual chunks by source.
- The two outer `task` tool calls remain distinguishable by `toolCallId`; the loss occurs specifically when native assistant/reasoning events are mapped to ACP message/thought chunks.
- Related but not duplicate:
- #2265 concerns SDK/Fleet per-subagent output and was closed after `parentToolCallId` was added to SDK events. This report is about that identity being dropped by the ACP adapter.
- #2645 concerns missing SDK delta events. Here, deltas are emitted over ACP, but without their source identity.

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Inizia dal punto di ingresso `copilot --acp` e dal mapping che converte gli eventi nativi `assistant.message` e `assistant.message_delta` in notifiche ACP `session/update`. Riproduci due subagent Explore in parallelo, quindi confronta ogni `agent_message_chunk` con il `toolCallId` dell’attività esterna e il `parentToolCallId` nativo. Il lavoro è completato quando i client ACP possono distinguere l’output primario da quello dei subagent, oppure quando i delta dei subagent vengono soppressi in modo sicuro fino all’arrivo del risultato dell’attività separata.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
shell
Ambito
api, cli
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Tranquilla
Chiarezza
Abbastanza chiara
Idoneità per principianti
45/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.