github / github/copilot-cli

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

Abierto
#4,106 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

area:agents area:non-interactive
Lenguaje dominante
Shell
Estrellas
11.2k
Forks
1.9k
Merge medio
14 h 16 min
PR fusionados (30 d)
6

Descripción

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:

{
  "method": "session/update",
  "params": {
    "sessionId": "<parent-session>",
    "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:

    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:

    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:

    {
      "sessionUpdate": "tool_call",
      "toolCallId": "<subagent-A-tool-call>",
      "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:

    {
      "type": "assistant.message",
      "agentId": "<subagent-A-tool-call>",
      "data": {
        "parentToolCallId": "<subagent-A-tool-call>",
        "interactionId": "<subagent-A-interaction>",
        "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:

{
  "_meta": {
    "copilot": {
      "agentId": "<subagent-A-tool-call>",
      "parentToolCallId": "<subagent-A-tool-call>"
    }
  }
}

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.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza con el punto de entrada copilot --acp y el mapeo que convierte los eventos nativos assistant.message y assistant.message_delta en notificaciones ACP session/update. Reproduce dos subagentes Explore en paralelo y, después, compara cada agent_message_chunk con el toolCallId de la tarea externa y el parentToolCallId nativo. El trabajo está terminado cuando los clientes ACP pueden distinguir entre la salida principal y la de los subagentes, o cuando los deltas de los subagentes se suprimen de forma segura hasta que llegue el resultado de la tarea separada.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
shell
Área
api, cli
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Tranquilo
Claridad
Bastante claro
Aptitud para principiantes
45/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.