Skip to content

feat(channels): implement A2A v1.0.0 channel adapter (channels/a2a/) #232

Description

@raykao

Summary

Build a new src/channels/a2a/ channel adapter implementing the A2A (Agent2Agent) Protocol v1.0.0, the unified open standard under the Linux Foundation (formed by the merger of IBM ACP and Google A2A, 2025).

This replaces the now-closed #214 (which targeted ACP v0.2.0, a defunct spec).

Spec: https://a2aproject.github.io/A2A/latest/specification/
Protocol binding: JSON-RPC 2.0 over HTTP + SSE (the standard JSON-RPC binding)


Problem

copilot-bridge currently has no standard-compliant external channel. The existing channels/acp/ (soon channels/copilot-bridge-ws-rpc/ per #231) is a custom internal WebSocket JSON-RPC protocol - well-suited for internal clients (kanban) but not interoperable with the broader A2A ecosystem.

An A2A channel would let any A2A-compliant client (other agents, external UIs, third-party tooling) interact with copilot-bridge agents without custom client code.


Proposed Solution

A new, clean implementation in src/channels/a2a/ that conforms to A2A v1.0.0. No shared code with channels/copilot-bridge-ws-rpc/.

Endpoints

Method Endpoint Purpose
GET /.well-known/agent.json Agent Card discovery
POST / JSON-RPC endpoint (all A2A operations)

All A2A operations are multiplexed over a single JSON-RPC endpoint. SSE streaming is handled via Content-Type: text/event-stream on the same HTTP POST.

A2A Operations to implement

Operation JSON-RPC Method Priority
Send message message/send P0
Send streaming message message/stream P0
Get task tasks/get P0
Cancel task tasks/cancel P1
Subscribe to task tasks/resubscribe P1
Set push notification config tasks/pushNotificationConfig/set P2
Get push notification config tasks/pushNotificationConfig/get P2
List tasks tasks/list P2

Agent Card (.well-known/agent.json)

Per A2A spec, served at /.well-known/agent.json per agent. Fields:

{
  "name": "...",
  "description": "...",
  "url": "https://host/agents/{name}",
  "version": "...",
  "capabilities": {
    "streaming": true,
    "pushNotifications": true,
    "stateTransitionHistory": false
  },
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"],
  "skills": [...]
}

Task Lifecycle

Maps SDK session events to A2A task states:

SDK event A2A task state
Session created submitted
session.in_progress working
session.idle completed
session.error failed
Permission requested input-required
Permission resolved back to working

Streaming (SSE)

message/stream returns Content-Type: text/event-stream. Events emitted:

  • TaskStatusUpdateEvent - task state transitions
  • TaskArtifactUpdateEvent - streaming text chunks (as Part with mediaType: text/plain)
  • Tool call tracking via Part.metadata (trajectory data: tool name, input, output)

Permission Approval (HITL)

When the SDK requests a permission:

  1. Task transitions to input-required
  2. await_request in the Task object carries the permission details
  3. Client sends message/send with the approval/denial to resume
  4. Task transitions back to working

Push Notifications

When tasks/pushNotificationConfig/set is called, bridge registers the webhook URL. On task state transitions, bridge POSTs StreamResponse to the webhook.

The client (kanban) can register a webhook and disconnect - bridge will POST when the task needs attention (input-required, completed, failed).

Multi-turn Sessions

Each A2A task has a contextId. Subsequent messages with the same contextId are routed to the same underlying copilot session. Maps directly to the bridge's existing session-per-channel model.


What This Enables for Kanban

  • Kanban sends a prompt via message/stream, SSE stream stays open
  • Agent streams thoughts/tool calls via TaskArtifactUpdateEvent with trajectory metadata
  • When agent needs approval: task goes input-required, kanban renders approval UI
  • Kanban resumes via message/send with approval
  • On disconnect: kanban calls tasks/resubscribe to reattach to stream
  • If kanban was offline: push notification fires, kanban calls tasks/get to hydrate
  • Agent can also use a write_card_update skill to push richer semantic updates directly to the card

Implementation Notes

  • Clean implementation - no shared code with channels/copilot-bridge-ws-rpc/
  • channels/copilot-bridge-ws-rpc/ remains for internal clients; A2A channel is for external/ecosystem clients
  • Auth: HTTP Authorization: Bearer <token> per agent, same pattern as WS channel
  • One HTTP server per A2A platform config entry (like the WS channel)
  • channels/copilot-bridge-ws-rpc/ is the eventual deprecation candidate once this covers all use cases

Spec References

Supersedes

Closes #214 (targeted ACP v0.2.0 - now defunct, merged into A2A)

Reported By

Agent (automated) - ACP/A2A conformance review + protocol research, 2026-05-22

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions