You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Agent Adaptor already provides a useful foundation for orchestrating work
across different local agent CLIs. For example, a host can run Claude Code to
implement a change and then run Codex to review it. However, this orchestration
still happens at the host level: Claude Code cannot autonomously call Codex as
a subagent during its own run, and none of the supported CLIs can currently
call an arbitrary remote agent as a subagent-like collaborator.
This issue proposes a delegation mechanism that lets any host-curated remote
agent that speaks A2A or AG-UI appear inside a supported local agent CLI
(Claude Code, Codex, Cursor, and future adapters) as a visual subagent. The
remote agent remains remote: it keeps its own workspace, permissions, runtime,
tools, identity, and protocol semantics. The local agent only receives a final
structured summary/result through a tool call, while the UI receives the remote
agent's live progress through a side-channel stream.
The minimum product contract is:
When a local agent calls a remote agent, the remote agent's progress, text,
artifacts, and terminal state are streamed in real time over the SSE /
AG-UI layer as a nested subagent. These live events must not be injected
into or otherwise pollute the parent agent's model context as ordinary tool
output.
When the remote agent finishes, the parent agent receives a concise,
structured tool result containing the remote task summary, status, artifact
references, and protocol identifiers needed for audit and replay.
The implementation shape is an MCP delegation tool plus a host-side stream
overlay:
The local agent calls an MCP tool such as delegate_to_agent.
The tool delegates a bounded task to a remote A2A or AG-UI agent.
While the tool call is active, the host streams remote task events through a
delegation event bus into the same SSE / AG-UI response as the parent run.
The parent model receives only the final structured tool result and then
continues its own turn.
With this mechanism, Team Agent mode is no longer constrained to subagents
implemented inside a single agent CLI. A host can compose heterogeneous agent
technologies, for example Claude Code as the parent, Codex as a review
subagent, Cursor as an implementation subagent, and remote A2A/AG-UI agents as
specialized collaborators.
1. Target Product Effect
The target UI should look like this:
Parent agent is working...
Tool: delegate_to_agent(research)
Remote subagent: research
status: starting
text: Searching official A2A SDK examples...
text: Comparing task streaming and artifact behavior...
artifact: a2a-sdk-notes.md
status: completed
Parent agent resumes with remote result...
The browser or IDE receives one continuous SSE / AG-UI stream. The user should
not need to open a second socket, watch a second task screen, or wait for the
remote task to finish before seeing remote progress.
2. How Close This Gets to Native Subagents
Dimension
Target fidelity
Notes
Real-time visible output
High
A2A streaming or AG-UI remote events can be merged into the parent SSE stream.
Nested lifecycle
High
subagent.started, progress, artifacts, terminal event can be shown under the parent tool call.
Cancellation
High
Parent cancel must cascade to the active A2A task.
Auditability
High
Store delegation ID, remote task ID, remote context ID, agent card fingerprint, events, and final result.
Parent agent receives final result
High
The MCP tool returns a structured tool result after remote completion or failure.
Parent agent watches tokens while reasoning
Medium-low
Most local agent protocols block while a tool call is active. The UI can see remote tokens; the parent model usually sees only the final tool result.
Shared workspace
Intentionally no
Remote agent owns its workspace. Local files must be passed explicitly as artifacts or references.
Shared permissions
Intentionally no
Remote auth and permissions are owned by the remote agent / host policy.
Shared tool namespace
Intentionally no
Remote tool calls may be opaque unless the remote agent exposes them as A2A / AG-UI events.
Conclusion: the design can closely match native subagent visualization and
lifecycle, but it must not claim same-process execution equivalence.
Assumption after issue #4 merges: this workstream consumes the A2A client and
bridge primitives from PR #6 instead of re-implementing A2A wire handling. In
particular, issue #5 should use:
pkg/clients/a2a.GetTaskRequest for polling/recovery with tenant and HistoryLength preservation.
pkg/clients/a2a.CancelTaskRequest for cancellation cascade with tenant and
structured cancellation metadata.
pkg/bridges/a2a.ArtifactAssistantOutput (assistant-output) when mapping
remote bridge text artifacts to subagent.text.*.
pkg/bridges/a2a.ArtifactAgentAdaptorResult (agent-adaptor-result) when
building the final structured tool result.
Relevant A2A surfaces to consume from issue #4 and map into delegation events:
Agent Card discovery and capability metadata.
Message send and streaming send.
Task status updates.
Task artifact updates.
Task cancellation.
Task lookup / subscription for reconnect and recovery.
Relevant local SDK surfaces to reuse:
RunHandle.StreamEvents() <-chan StreamPayload
pkg/bridges/agui translation from StreamPayload to AG-UI events.
pkg/bridges/sse single HTTP SSE stream.
WithMCP / WithDefaultMCP for tool exposure to local agents.
WithRuntimeServices / RuntimeServiceManager for per-run service setup.
4. Scope
In Scope
A host-managed A2A/AG-UI delegation tool exposed to local agents through MCP.
A run-scoped event bus that streams remote delegation events while the MCP
tool call is still executing.
A stream mux that merges parent RunHandle.StreamEvents() with delegation
events into one AG-UI / SSE stream.
Mapping remote A2A task status, messages, artifacts, errors, and cancellation
into nested subagent events.
BDD scenarios, TDD boundaries, and robustness tests.
Out of Scope
Automatic agent routing by the SDK.
Treating arbitrary remote A2A agents as default SDK agents.
Adding a built-in HTTP/gRPC server to core SDK.
Making concrete adapters import or understand A2A.
Pretending remote stdout/stderr exists. Remote protocol traces are not RunResult.RawStreams.
Giving the model arbitrary A2A endpoint URLs. Hosts must curate remote
agents.
5. Architecture
sequenceDiagram
participant UI as Browser / IDE
participant SSE as SSE/AG-UI mux
participant SDK as agent-adaptor RunHandle
participant Parent as Local Codex/Claude/Cursor
participant MCP as A2A delegation MCP tool
participant Bus as DelegationEventBus
participant Remote as Remote A2A/AG-UI agent
UI->>SSE: POST chat request
SSE->>SDK: sdk.Start(... WithStreaming, WithMCP)
SDK->>Parent: adapter.Run(...)
Parent-->>SSE: parent StreamPayloads
Parent->>MCP: tool call delegate_to_agent
MCP->>Bus: delegation.started
Bus-->>SSE: subagent.started
MCP->>Remote: A2A sendStreaming / subscribe
Remote-->>MCP: task status / message / artifact events
MCP->>Bus: subagent text/status/artifact events
Bus-->>SSE: nested subagent stream
Remote-->>MCP: terminal task status
MCP->>Bus: delegation.finished
MCP-->>Parent: structured tool result
Parent-->>SSE: parent resumes and finishes
SSE-->>UI: one ordered SSE stream
Loading
The important detail: the remote stream does not pass through the parent model
protocol. It travels through a host-side event bus and is multiplexed into the
same UI stream.
7. Required New Capability: Run-Scoped MCP Context
This is the first hard boundary.
To render a remote task as a local subagent, the delegation MCP tool must know
which parent run it belongs to. Ideally it also knows which parent tool call it
belongs to.
MCPServerSpec is resolved before runtime services, so a runtime-created
URL/token cannot currently be appended to MCP config without a small SDK
enhancement.
MCPServerSpec.Headers and MCPServerSpec.Env are static host-provided
values today; they do not automatically receive run ID or a per-run token.
Therefore, a reliable implementation needs one of these mechanisms.
Option A: Runtime-Backed MCP Server Injection (Preferred)
Allow a runtime service to declare that it exposes an MCP server for the run.
The SDK would append this server to the effective MCP payload before adapter
profile materialization.
This is smaller than runtime-backed injection but less expressive.
Option C: Weak Correlation (MVP Only)
Use a shared MCP server and correlate by temporal proximity:
Parent stream emits tool_call.start with Name == "delegate_to_agent".
MCP server receives a delegation request.
The bridge associates the next unclaimed delegation with the active parent
tool call.
This can work for demos but is not robust enough for production because
parallel tool calls and retries can misassociate events.
Design Decision
The production design should use Option A or Option B. Option C is acceptable
only for a spike and must be guarded by tests that demonstrate its known
failure modes.
8. Required New Capability: Delegation Stream Overlay
This is the second hard boundary.
The parent adapter cannot emit remote A2A task events because those events are
produced by the MCP tool process, not by the parent CLI process. Therefore RunHandle.StreamEvents() alone is not enough.
Add a host-side event bus:
typeDelegationEventstruct {
RunIDstringParentToolCallIDstringDelegationIDstringAgentKeystringAgentNamestringProtocolstring// "a2a" or "agui"RemoteTaskIDstringRemoteContextIDstringRemoteMessageIDstringRemoteArtifactIDstringKindDelegationEventKindDeltastringTextstringArtifact*DelegationArtifactStatusstringError*DelegationErrorRawmap[string]anyTime time.Time
}
Agent endpoints are configured by the host, not by the model.
Agent Cards are fetched by the host and cached with a fingerprint.
Auth tokens never appear in prompts, tool args, or StreamPayload.
Tool args contain agent keys, not URLs or bearer tokens.
Artifacts are stored in host-controlled storage and passed by URI/reference
when possible.
13. Correlation Model
Identifiers:
ID
Owner
Purpose
RunID
SDK
Parent run identity.
ParentToolCallID
Parent adapter
Local tool-call lifecycle ID for delegate_to_agent.
DelegationID
Delegation tool
Stable host ID for one remote delegation.
RemoteTaskID
A2A remote
Remote task identity.
RemoteContextID
A2A remote
Remote multi-turn context identity.
Correlation levels:
Strong: MCP request carries a run-scoped token and the parent adapter emits
a matching tool call ID or request metadata.
Medium: MCP request carries run ID; mux groups the delegation under the
active delegate_to_agent tool call by call order.
Weak: no run ID; host guesses by timestamp. This must not be production
default.
The desired implementation is strong or medium. Weak correlation is only a
diagnostic fallback.
14. Session and Context Semantics
Default behavior:
Each delegation creates a new remote A2A task.
If the remote protocol supports a context ID, the delegator may create or
reuse it according to host policy.
The parent SDK session is not the remote context ID.
Supported policies:
Policy
Behavior
Use case
ephemeral
New remote context per delegation.
Safest default.
sticky_per_parent_thread_agent
Reuse remote context for (parent SessionKey, agentKey).
Remote specialist remembers prior delegations within a chat.
sticky_per_run_agent
Reuse remote context within one parent run only.
Multiple delegations in one turn.
The tool result must always report the remote context ID actually used.
15. Cancellation Semantics
Cancellation cascade:
UI disconnects or user clicks stop.
pkg/bridges/sse cancels the parent request context.
Parent RunHandle.Cancel(ctx) is called.
The MCP tool request context is cancelled by the parent provider if the
provider supports MCP cancellation.
Delegator calls A2A cancel task for active remote task.
Delegation bus emits subagent.cancelled.
If remote cancellation cannot be confirmed before timeout, emit subagent.status with cancel_pending and mark the local tool result as
cancelled or abandoned according to host policy.
Tests must cover providers that do not propagate MCP cancellation promptly.
The host should keep a cleanup goroutine keyed by DelegationID so remote
tasks are still cancelled when the parent process exits first.
On context cancellation, cancel parent handle and active delegations.
Default rendering:
Parent events remain normal AG-UI events.
Subagent events are AG-UI CUSTOM events.
Frontends that know the custom names render nested subagent UI.
Frontends that do not know the custom names still receive valid AG-UI.
19. BDD Scenarios
Scenario 1: Successful Remote A2A Subagent Stream
Feature: A2A delegation appears as a local visual subagentScenario: Remote A2A agent streams text and an artifactGiven a parent SDK run is started with streaming enabled
And the A2A delegation tool is available through MCP
And the remote "research" agent supports streaming and artifacts
When the parent agent calls delegate_to_agent with agent "research"Then the SSE stream emits a parent tool_call.start for delegate_to_agent
And the SSE stream emits subagent.started for agent "research"And the SSE stream emits subagent.text.delta events before the tool result
And the SSE stream emits subagent.artifact for the remote artifact
And the SSE stream emits subagent.finished with the remote task ID
And the parent agent receives a structured MCP tool result
And the parent agent can continue and emit final assistant text
Scenario 2: Remote Agent Has No Streaming
Scenario: Non-streaming remote agent degrades gracefullyGiven the remote "legacy" agent does not advertise streaming
When the parent agent delegates a task to "legacy"Then the SSE stream emits subagent.started
And the SSE stream emits status updates from polling or task state
And the SSE stream does not emit fake text deltas
And the final tool result contains the remote task result
Scenario: User stops the parent run while a remote task is activeGiven the parent run has an active delegation with remote task ID "task-1"When the user cancels the parent run
Then the delegator calls A2A cancel task for "task-1"And the SSE stream emits subagent.cancelled or subagent.status cancel_pending
And the parent run terminates without leaking the remote task watcher
Scenario 4: Remote Failure Is Visible and Structured
Scenario: Remote task failsGiven the remote A2A agent accepts a delegated task
When the remote task reaches a failed terminal status
Then the SSE stream emits subagent.failed with the remote status
And the MCP tool returns a structured error result
And the parent agent can decide whether to retry or continue
Scenario 5: Input Required
Scenario: Remote task requires additional inputGiven the remote A2A task enters input-required state
And host policy allows surfacing remote input requests
When the delegator receives the input-required event
Then the SSE stream emits subagent.input_required
And the MCP tool blocks until the input is resolved or times out
Scenario 6: Multiple Concurrent Delegations
Scenario: Two remote delegations stream concurrentlyGiven the parent run starts two delegate_to_agent tool calls
When both remote agents stream events concurrently
Then every subagent event includes a distinct delegation ID
And events are grouped under the correct parent tool call when known
And terminal events are emitted for both delegations
Scenario 7: SSE Reconnect
Scenario: UI reconnects while remote delegation is activeGiven a remote delegation is active and streaming
When the browser disconnects and reconnects with Last-Event-ID
Then the mux replays buffered events after the requested cursor
And the delegator resubscribes or polls the remote A2A task if needed
And the UI receives a consistent terminal state exactly once
Scenario 8: Unauthorized Agent Key
Scenario: Model requests an unregistered remote agentGiven the remote registry only contains "research"When the model calls delegate_to_agent with agent "unknown"Then the tool returns agent_not_found
And no outbound network request is made
And no secret is exposed in the tool result or stream
20. TDD Boundaries
Tests should be written before implementation in these layers.
Delegation code compiles against GetTaskRequest, CancelTaskRequest, ArtifactAssistantOutput, and ArtifactAgentAdaptorResult.
Phase 1: Run-Scoped MCP Context
Implement the chosen production correlation mechanism for the delegation MCP
tool.
Prefer Option A: runtime-backed MCP server injection from RuntimeServiceRef metadata into the effective MCP payload before adapter
profile materialization.
Keep Option B, run-scoped header/env expansion, as the smaller fallback if
runtime-backed injection proves too invasive.
Weak temporal correlation may exist only as an explicit diagnostic/spike mode
and must refuse ambiguous concurrent calls.
Exit criteria:
The MCP tool can resolve the parent RunID without asking the model to pass
it.
A per-run token or equivalent authority gates access to the delegation MCP
endpoint.
Tests cover missing run context, invalid token, and concurrent parent runs.
go test -race passes for delegation bus, mux, and tool server packages.
25. Open Design Questions
Should run-scoped MCP context be implemented as runtime-backed MCP injection
or placeholder expansion in MCPServerSpec?
Do we want the frontend to render subagent events as AG-UI CUSTOM only, or
also provide a "nested tool lifecycle" compatibility mode?
Should sticky remote context reuse be enabled by default for named agents,
or should the default be ephemeral only?
How much remote inner tool-call visibility should the UI show? Second-level
nesting is possible but may clutter the stream.
Should remote input-required map into local HITL v2, or remain a
subagent-specific UI event with host-owned resolution?
26. Architectural Guardrails
The MCP tool is a host-controlled delegation surface, not a router.
A2A client code is localized and does not enter core SDK.
The event overlay is UI-facing; it does not mutate parent RunResult.
The parent agent sees only the final structured tool result unless the host
deliberately feeds remote progress back as tool output.
The UI may make remote execution look like a local subagent, but audit data
must preserve the remote protocol, task ID, context ID, and agent identity.
27. Required Example
The implementation must include an end-to-end example based on the existing
Copilot/CopilotKit demo path, currently examples/streaming-chat-copilotkit.
The example should demonstrate:
A Claude Code instance is the parent local agent driving the main chat.
A separate Codex instance is exposed as an A2A-compatible remote agent.
Claude Code receives an MCP tool such as delegate_to_agent that targets
the Codex A2A agent.
When Claude Code calls the tool, the CopilotKit UI renders the Codex run as
a nested visual subagent in the same SSE / AG-UI stream.
Codex's live progress is visible to the user but is not injected into
Claude Code's model context as ordinary tool output.
When Codex finishes, Claude Code receives a concise structured result and
continues the parent turn.
This example is the concrete product proof for the workstream: a Codex agent
running through A2A must be callable as a subagent-like collaborator from a
Claude Code parent while preserving the parent/remote context boundary.
Issue Proposal
Agent Adaptor already provides a useful foundation for orchestrating work
across different local agent CLIs. For example, a host can run Claude Code to
implement a change and then run Codex to review it. However, this orchestration
still happens at the host level: Claude Code cannot autonomously call Codex as
a subagent during its own run, and none of the supported CLIs can currently
call an arbitrary remote agent as a subagent-like collaborator.
This issue proposes a delegation mechanism that lets any host-curated remote
agent that speaks A2A or AG-UI appear inside a supported local agent CLI
(Claude Code, Codex, Cursor, and future adapters) as a visual subagent. The
remote agent remains remote: it keeps its own workspace, permissions, runtime,
tools, identity, and protocol semantics. The local agent only receives a final
structured summary/result through a tool call, while the UI receives the remote
agent's live progress through a side-channel stream.
The minimum product contract is:
artifacts, and terminal state are streamed in real time over the SSE /
AG-UI layer as a nested subagent. These live events must not be injected
into or otherwise pollute the parent agent's model context as ordinary tool
output.
structured tool result containing the remote task summary, status, artifact
references, and protocol identifiers needed for audit and replay.
The implementation shape is an MCP delegation tool plus a host-side stream
overlay:
delegate_to_agent.delegation event bus into the same SSE / AG-UI response as the parent run.
continues its own turn.
With this mechanism, Team Agent mode is no longer constrained to subagents
implemented inside a single agent CLI. A host can compose heterogeneous agent
technologies, for example Claude Code as the parent, Codex as a review
subagent, Cursor as an implementation subagent, and remote A2A/AG-UI agents as
specialized collaborators.
1. Target Product Effect
The target UI should look like this:
The browser or IDE receives one continuous SSE / AG-UI stream. The user should
not need to open a second socket, watch a second task screen, or wait for the
remote task to finish before seeing remote progress.
2. How Close This Gets to Native Subagents
subagent.started, progress, artifacts, terminal event can be shown under the parent tool call.Conclusion: the design can closely match native subagent visualization and
lifecycle, but it must not claim same-process execution equivalence.
3. References
docs/streaming.mddocs/streaming-adapter-contract.mdpkg/bridges/aguipkg/bridges/ssepkg/clients/a2aandpkg/bridges/a2aAssumption after issue #4 merges: this workstream consumes the A2A client and
bridge primitives from PR #6 instead of re-implementing A2A wire handling. In
particular, issue #5 should use:
pkg/clients/a2a.GetTaskRequestfor polling/recovery with tenant andHistoryLengthpreservation.pkg/clients/a2a.CancelTaskRequestfor cancellation cascade with tenant andstructured cancellation metadata.
pkg/bridges/a2a.ArtifactAssistantOutput(assistant-output) when mappingremote bridge text artifacts to
subagent.text.*.pkg/bridges/a2a.ArtifactAgentAdaptorResult(agent-adaptor-result) whenbuilding the final structured tool result.
Relevant A2A surfaces to consume from issue #4 and map into delegation events:
Relevant local SDK surfaces to reuse:
RunHandle.StreamEvents() <-chan StreamPayloadpkg/bridges/aguitranslation fromStreamPayloadto AG-UI events.pkg/bridges/ssesingle HTTP SSE stream.WithMCP/WithDefaultMCPfor tool exposure to local agents.WithRuntimeServices/RuntimeServiceManagerfor per-run service setup.4. Scope
In Scope
tool call is still executing.
RunHandle.StreamEvents()with delegationevents into one AG-UI / SSE stream.
into nested subagent events.
Out of Scope
RunResult.RawStreams.agents.
5. Architecture
sequenceDiagram participant UI as Browser / IDE participant SSE as SSE/AG-UI mux participant SDK as agent-adaptor RunHandle participant Parent as Local Codex/Claude/Cursor participant MCP as A2A delegation MCP tool participant Bus as DelegationEventBus participant Remote as Remote A2A/AG-UI agent UI->>SSE: POST chat request SSE->>SDK: sdk.Start(... WithStreaming, WithMCP) SDK->>Parent: adapter.Run(...) Parent-->>SSE: parent StreamPayloads Parent->>MCP: tool call delegate_to_agent MCP->>Bus: delegation.started Bus-->>SSE: subagent.started MCP->>Remote: A2A sendStreaming / subscribe Remote-->>MCP: task status / message / artifact events MCP->>Bus: subagent text/status/artifact events Bus-->>SSE: nested subagent stream Remote-->>MCP: terminal task status MCP->>Bus: delegation.finished MCP-->>Parent: structured tool result Parent-->>SSE: parent resumes and finishes SSE-->>UI: one ordered SSE streamThe important detail: the remote stream does not pass through the parent model
protocol. It travels through a host-side event bus and is multiplexed into the
same UI stream.
6. Package Layout
Proposed packages:
Naming can change during implementation, but the ownership should not:
a second A2A client stack.
7. Required New Capability: Run-Scoped MCP Context
This is the first hard boundary.
To render a remote task as a local subagent, the delegation MCP tool must know
which parent run it belongs to. Ideally it also knows which parent tool call it
belongs to.
Current facts:
RuntimeServiceManager.EnsurereceivesRuntimeServiceRequest.RunID.RunHandle.RunID()is available afterStart().MCPServerSpecis resolved before runtime services, so a runtime-createdURL/token cannot currently be appended to MCP config without a small SDK
enhancement.
MCPServerSpec.HeadersandMCPServerSpec.Envare static host-providedvalues today; they do not automatically receive run ID or a per-run token.
Therefore, a reliable implementation needs one of these mechanisms.
Option A: Runtime-Backed MCP Server Injection (Preferred)
Allow a runtime service to declare that it exposes an MCP server for the run.
The SDK would append this server to the effective MCP payload before adapter
profile materialization.
Sketch:
The runtime manager can create a per-run bearer token and register:
The injected MCP endpoint receives the token on every tool request and resolves
the parent run context without asking the model to pass it.
Option B: Run-Scoped MCP Header / Env Expansion
Support placeholder expansion in MCP server specs:
For stdio MCP servers:
This is smaller than runtime-backed injection but less expressive.
Option C: Weak Correlation (MVP Only)
Use a shared MCP server and correlate by temporal proximity:
tool_call.startwithName == "delegate_to_agent".tool call.
This can work for demos but is not robust enough for production because
parallel tool calls and retries can misassociate events.
Design Decision
The production design should use Option A or Option B. Option C is acceptable
only for a spike and must be guarded by tests that demonstrate its known
failure modes.
8. Required New Capability: Delegation Stream Overlay
This is the second hard boundary.
The parent adapter cannot emit remote A2A task events because those events are
produced by the MCP tool process, not by the parent CLI process. Therefore
RunHandle.StreamEvents()alone is not enough.Add a host-side event bus:
The SSE / AG-UI mux subscribes to:
handle.StreamEvents().handle.RunID().It emits one ordered stream to the UI.
9. Event Mapping
A2A to DelegationEvent
subagent.startedsubagent.statussubagent.text.deltaorsubagent.statussubagent.artifactsubagent.input_requiredsubagent.finishedsubagent.failedsubagent.cancelledRemote AG-UI to DelegationEvent
RUN_STARTEDsubagent.startedTEXT_MESSAGE_*subagent.text.*TOOL_CALL_*subagent.statusor nested raw eventCUSTOMsubagent.statusor raw customRUN_FINISHEDsubagent.finishedRUN_ERRORsubagent.failedDelegationEvent to Local StreamPayload
For the first implementation, avoid adding new core
StreamKindconstants.Use opaque custom stream payloads:
pkg/bridges/aguialready mapsKind == ""to AG-UICUSTOM. The new mux caneither:
CUSTOMevents with names likesubagent.text.delta.wants all subagent activity to live under the parent tool call.
The default should be custom subagent events because they preserve remote
semantics without overloading local tool call lifecycles.
10. UI Wire Shape
Recommended AG-UI custom event payload:
{ "type": "CUSTOM", "name": "subagent.text.delta", "value": { "runId": "run-...", "parentToolCallId": "toolu-...", "delegationId": "del-...", "agentKey": "research", "agentName": "Research Agent", "remoteTaskId": "task-...", "remoteContextId": "ctx-...", "messageId": "msg-...", "delta": "Searching official examples..." } }Recommended lifecycle names:
The UI renderer groups by:
If
parentToolCallIdis unknown, group under the run as an unclaimed remotedelegation and update the group once correlation is resolved.
11. Tool Contract
Expose one generic tool plus optional named shortcuts.
Generic Tool
Input:
{ "agent": "research", "objective": "Find the current A2A Go SDK streaming API shape.", "input": { "prompt": "Research official examples and summarize exact APIs.", "context": "We are designing agent-adaptor A2A delegation.", "artifacts": [ { "name": "current-design.md", "uri": "host://artifact/current-design.md" } ] }, "constraints": { "timeout_seconds": 180, "stream": true, "max_artifacts": 10 } }Output:
{ "delegation_id": "del-...", "agent": "research", "remote_protocol": "a2a", "remote_task_id": "task-...", "remote_context_id": "ctx-...", "status": "completed", "summary": "Official a2a-go exposes...", "artifacts": [ { "id": "artifact-...", "name": "a2a-sdk-notes.md", "mime_type": "text/markdown", "uri": "host://delegations/del-.../artifacts/a2a-sdk-notes.md" } ], "messages": [ { "role": "assistant", "text": "..." } ], "raw_task": { "provider": "a2a", "task_id": "task-..." } }Errors should return structured tool errors, not unstructured text:
{ "delegation_id": "del-...", "status": "failed", "error": { "code": "remote_timeout", "message": "Remote task did not finish before timeout.", "retryable": true, "remote_status": "working" } }Named Shortcuts
Hosts may expose curated wrappers:
These are aliases over the generic delegator with a fixed
agentkey andstricter input schema.
12. Remote Agent Registry
The model must not pass arbitrary remote URLs.
Host-owned registry:
Policy examples:
Security rules:
agentkeys, not URLs or bearer tokens.when possible.
13. Correlation Model
Identifiers:
RunIDParentToolCallIDdelegate_to_agent.DelegationIDRemoteTaskIDRemoteContextIDCorrelation levels:
a matching tool call ID or request metadata.
active
delegate_to_agenttool call by call order.default.
The desired implementation is strong or medium. Weak correlation is only a
diagnostic fallback.
14. Session and Context Semantics
Default behavior:
reuse it according to host policy.
Supported policies:
ephemeralsticky_per_parent_thread_agent(parent SessionKey, agentKey).sticky_per_run_agentThe tool result must always report the remote context ID actually used.
15. Cancellation Semantics
Cancellation cascade:
pkg/bridges/ssecancels the parent request context.RunHandle.Cancel(ctx)is called.provider supports MCP cancellation.
subagent.cancelled.subagent.statuswithcancel_pendingand mark the local tool result ascancelled or abandoned according to host policy.
Tests must cover providers that do not propagate MCP cancellation promptly.
The host should keep a cleanup goroutine keyed by
DelegationIDso remotetasks are still cancelled when the parent process exits first.
16. Failure Semantics
Failure classes:
agent_not_foundsubagent.failedoptional.agent_unavailablesubagent.failed.capability_unsupportedsubagent.status.remote_rejectedsubagent.failed.remote_failedsubagent.failed.remote_timeoutsubagent.failedorsubagent.cancelled.stream_interruptedsubagent.status.artifact_too_largesubagent.artifacterror.input_requiredsubagent.input_required.The parent model should receive enough structured information to decide
whether to retry, continue without remote output, or ask the user.
17. Streaming and Backpressure
Delegation events are UI-facing and may be high-volume. Use QoS lanes:
Coalescing rule:
(DelegationID, MessageID).subagent.statusorstream.droppedmarker if deltas are dropped.The mux assigns its own global SSE cursor so parent events and delegation
events can be replayed in emitted order.
18. SSE / AG-UI Mux
The mux should be a wrapper, not a replacement for existing bridges.
Sketch:
Flow:
WithStreaming().runStream := agui.WrapWithContext(ctx, handle).subagentStream := bus.SubscribeRun(ctx, handle.RunID()).Default rendering:
CUSTOMevents.19. BDD Scenarios
Scenario 1: Successful Remote A2A Subagent Stream
Scenario 2: Remote Agent Has No Streaming
Scenario 3: Parent Cancellation Cancels Remote Task
Scenario 4: Remote Failure Is Visible and Structured
Scenario 5: Input Required
Scenario 6: Multiple Concurrent Delegations
Scenario 7: SSE Reconnect
Scenario 8: Unauthorized Agent Key
20. TDD Boundaries
Tests should be written before implementation in these layers.
20.1 Pure Mapping Unit Tests
Package:
pkg/hosttools/a2adelegationpkg/clients/a2a.AgentCardcapability metadataconservatively.
DelegationStatus.DelegationArtifactCreated.a2a.ArtifactAssistantOutputmaps tosubagent.text.*.a2a.ArtifactAgentAdaptorResultmaps to final tool-result summary/artifacts.DelegationFinished.DelegationFailed.TASK_STATE_INPUT_REQUIREDmaps toDelegationInputRequired.subagent.startedbefore remote stream events.Package:
pkg/bridges/subagentstream20.2 Contract Tests
pkg/hosttools/a2adelegationmust not importcodex,claude, orcursor.pkg/bridges/subagentstreammust not import concrete adapters.RunResult.RawStreamsmust not contain remote A2A wire dumps.Output.Run / Startexecution path remains unchanged.20.3 Integration Tests With Fake A2A Server
Fake server capabilities:
Test cases:
20.4 MCP Tool Tests
agentandobjective.endpoint_urlfields.20.5 UI / SSE Golden Tests
Use golden event sequences.
Expected order for happy path:
Verifier assertions:
21. Robustness Test Matrix
subagent.started, then terminal.remote_timeout.cancel_pending; cleanup watcher continues.agent_unavailable/remote_rejected; redact secrets.go test -racefor mux, bus, delegator cleanup.input_required_unsupported.22. Property and Fuzz Tests
Property tests:
DelegationID, there is at most one terminal event.either remote-originated or synthesized.
(DelegationID, MessageID), text deltas preserve input order.Fuzz targets:
23. Phased Implementation
Prerequisite: Issue #4 A2A Substrate Merged
This workstream starts after PR #6 (issue #4) lands. The implementation must
reuse
pkg/clients/a2aandpkg/bridges/a2afrom that PR.Do not re-implement:
a2a-godependency selection.Exit criteria:
GetTaskRequest,CancelTaskRequest,ArtifactAssistantOutput, andArtifactAgentAdaptorResult.Phase 1: Run-Scoped MCP Context
tool.
RuntimeServiceRefmetadata into the effective MCP payload before adapterprofile materialization.
runtime-backed injection proves too invasive.
and must refuse ambiguous concurrent calls.
Exit criteria:
RunIDwithout asking the model to passit.
endpoint.
Phase 2: Delegation Core
DelegationEventBus.DelegationEventmapping using issue Add A2A protocol bridge and client primitives #4 DTOs andbridge artifact constants.
Send(ReturnImmediately)plusGetTaskRequestpolling.CancelTaskRequest.Exit criteria:
subagent.startedbefore remote progress and exactly oneterminal event.
structured metadata.
Phase 3: MCP Tool Server
delegate_to_agentMCP tool.Exit criteria:
Phase 4: SSE / AG-UI Product Path
subagentstreammux or extend existing SSE bridge through options.Exit criteria:
Phase 5: Product Example
examples/streaming-chat-copilotkit.AG-UI stream.
Exit criteria:
delegate_to_agent.Phase 6: A2A Interop Hardening
fixtures.
input-required, and cancellation cases.
Exit criteria:
Phase 7: Remote AG-UI Input
DelegationEvent.Exit criteria:
24. Acceptance Criteria
delegate_to_agentvia MCP.subagent before the MCP tool result is returned.
tool result references.
reconnect scenarios pass.
go test -racepasses for delegation bus, mux, and tool server packages.25. Open Design Questions
or placeholder expansion in
MCPServerSpec?CUSTOMonly, oralso provide a "nested tool lifecycle" compatibility mode?
or should the default be ephemeral only?
nesting is possible but may clutter the stream.
input-requiredmap into local HITL v2, or remain asubagent-specific UI event with host-owned resolution?
26. Architectural Guardrails
RunResult.deliberately feeds remote progress back as tool output.
must preserve the remote protocol, task ID, context ID, and agent identity.
27. Required Example
The implementation must include an end-to-end example based on the existing
Copilot/CopilotKit demo path, currently
examples/streaming-chat-copilotkit.The example should demonstrate:
delegate_to_agentthat targetsthe Codex A2A agent.
a nested visual subagent in the same SSE / AG-UI stream.
Claude Code's model context as ordinary tool output.
continues the parent turn.
This example is the concrete product proof for the workstream: a Codex agent
running through A2A must be callable as a subagent-like collaborator from a
Claude Code parent while preserving the parent/remote context boundary.