-
Notifications
You must be signed in to change notification settings - Fork 955
Description
Summary
Build out the A2A bridge so AG-UI can project artifacts/status/activity into shared state and activity events from A2A tasks/status updates, handle the AG-UI config/control lane for shared-state updates, and support input-required/interrupt flows while preserving existing text-only agent behavior.
Status
Work is already underway even though the guidelines ask contributors to reach out before starting; these features are required for my own usage whether or not they merge upstream.
Missing AG-UI <> A2A bridge features
integrations/a2a/typescript/
- Config/control lane: support structured AG-UI shared-state/config updates via a dedicated extension, replace the unused extension header, and keep conversational messaging unchanged.
- Task-aware reconnect/resubscribe: use task snapshots/historyLength, subscribe-only mode, and resume payloads without reopening closed runs.
- Shared-state/activity projection: emit task/status/context snapshots and deltas; project artifacts with default
/view/artifacts/<id>paths, append/lastChunk handling, and metadata-driven paths; include activity for surfaces and input requests. - Input-required interrupts/resume: emit RUN_FINISHED outcome
interruptwith pending interrupt state/activity; surface A2Ainput_requiredtask states through the AG-UI Human in the Loop (HITL) experience so reviewers see the request context; resume viaa2a.input.response, update activity/state, and clear pending interrupts when resolved. - Context/config forwarding (no full transcript): send the current message plus opt-in system/developer/config cues and AG-UI tool results when needed; rely on A2A task history for the full log; preserve existing text-only agents unchanged.
- Metadata hygiene: avoid leaking threadId/runId to A2A; prefer contextId/taskId in payloads and metadata.
Fix A2A Compliance
Currently the AG-UI threadId is used as the A2A contextId which is non-compliant with the A2A Protocol. The A2A contextId is meant to be generated by the A2A server when not supplied by the A2A client. The A2A client should never send a contextId that wasn't obtained by the A2A server.
- AG-UI gives every agent a threadId up front: if the caller supplies one it’s used;
otherwise the AbstractAgent constructor auto-generates a UUID and stores it on the
instance (sdks/typescript/packages/client/src/agent/agent.ts:50-66). - Each run reuses that agent threadId untouched; it’s copied straight into RunAgentInput
(sdks/typescript/packages/client/src/agent/agent.ts:287-299). - In the A2A bridge, that same threadId is forwarded as the A2A contextId when converting
history and when sending the message (integrations/a2a/typescript/src/agent.ts:126-142 and
integrations/a2a/typescript/src/utils.ts:190-238). There is no additional ID generation or
remapping inside the bridge. - The bridge never reads a server-generated contextId from A2A responses, so AG-UI’s
threadId remains the source of truth; A2A events are just streamed back as AG-UI events
and don’t mutate it (integrations/a2a/typescript/src/agent.ts:163-188).
Proposed Solution
Keep threadId optional and late-bind threadId to the first server contextId for A2A agents; no bridge cache; non-A2A agents unchanged.
- Event semantics:
- For deferred A2A agents, RUN_STARTED is delayed until contextId is received; after that, event order remains RUN_STARTED → snapshot/deltas/messages.
- Non-A2A agents: no change.
- Caller contract:
- ThreadId and contextId are the same for A2A agents after the first binding; callers just keep using threadId. No extra contextId parameter is required in the public API.
Scope / Acceptance
- Add run options (send/stream, taskId) plus:
subscribe-onlyto reconnect without sending a new message (fetch snapshot, then resubscribe).resumeto send input responses to the existing task without reopening the prior run.
- Use A2A server task snapshots (
getTask) plus resubscribe for reconnects (no new message send). - Send the current payload with opt-in system/developer/config cues (no full transcript); use a dedicated config/control extension for AG-UI shared-state updates and gate the legacy extension header; rely on A2A task storage for full history.
- Project A2A messages/status/artifacts into AG-UI text/tool/activity and shared-state events (snapshots + deltas), with default artifact base paths and append/lastChunk semantics.
- Handle
input_requiredby emittingRUN_FINISHEDwith outcomeinterrupt, keeping pending interrupts in shared state/activity, surfacing them via the AG-UI Human in the Loop (HITL) flow for context, and resuming viaa2a.input.responsewithout reviving closed runs. - Keep metadata layering so threadId/runId stay internal; prefer contextId/taskId externally.
Testing plan
Add or update unit, integration, and e2e coverage as needed; run pnpm --filter @ag-ui/a2a test (A2A agent/util e2e + integration Jest suites), pnpm --filter @ag-ui/client test (client agent/subscriber Jest suites), plus pnpm lint and pnpm build; follow docs/testing-strategy.md.