Skip to content

A2A Protocol

Talon’s Agent-to-Agent (A2A) protocol lets personas delegate tasks to each other over the internal durable queue. No external orchestration needed — delegation is just a tool call.

  1. Persona A calls persona_send with a target persona name and a message
  2. Talon enqueues a collaboration queue item for persona B
  3. Persona B picks it up, runs its agent, and optionally returns a result
  4. If await_reply: true, persona A’s tool call blocks until the reply arrives

The queue handles durability, retries, and crash recovery. Collaboration items bypass the inflight lock so they can be processed even while the source persona’s run is still active.

:::tip Provider isolation Delegated A2A tasks always run on the target persona’s configured provider, regardless of which provider the source thread was using. This prevents provider affinity contamination and ensures each persona runs on its intended model. :::

All three delegation tools — persona_send, persona_task_status, and persona_list — are gated by the single persona.send:* capability. Grant it to any persona that needs to delegate work:

personas:
- name: orchestrator
capabilities:
allow:
- persona.send:*

Without this capability, the tools are hidden from the agent’s tool list entirely.

Use the persona_send host tool:

persona_send({
target_persona: "researcher",
message: "Find the latest pricing for AWS Lambda in eu-west-1",
await_reply: true
})
ParameterTypeRequiredDescription
target_personastringYesName of the target persona (must be loaded)
messagestringYesTask description sent to the target
await_replybooleanNotrue blocks until the target responds (default: false)
timeout_msnumberNoMax ms to wait when await_reply: true; default 300000 (5 minutes)

When await_reply: false (fire-and-forget), the tool returns immediately with the task ID. The target processes the task asynchronously.

When await_reply: true, the call waits up to 5 minutes (configurable with timeout_ms). If the target finishes before the timeout expires, the result is returned inline. If the timeout expires first, a structured timeout response is returned containing the task_id. Use persona_task_status to retrieve the final result later.

Use persona_list to discover available delegation targets:

persona_list()

Returns an array of persona cards:

[
{
"name": "researcher",
"description": "Research assistant with web search capabilities",
"skills": ["web-search", "file-analysis"]
},
{
"name": "writer",
"description": "Content writer for blog posts and documentation",
"skills": ["markdown-editor"]
}
]

The calling persona is excluded from the list. description is extracted from the first line of the persona’s system prompt file (# prefix stripped). skills are parsed from the persona’s configured skill list.

When await_reply: true times out before the target persona finishes, persona_send returns a structured response containing the task_id. Use persona_task_status to retrieve the result:

persona_task_status({
task_id: "abc123",
wait_ms: 60000 // optional: poll for up to 60 s
})
ParameterTypeRequiredDescription
task_idstringYesTask ID from the persona_send timeout response
wait_msnumberNoPoll until terminal state or this duration elapses

Terminal states are completed and failed. Requires persona.send:* capability.

A2A tasks follow the same lifecycle as regular queue items:

StatusMeaning
pendingEnqueued, waiting for the target persona
claimedTarget persona’s agent runner has picked it up
processingAgent is executing
completedTask finished, reply available
failedTask failed after retries

Tasks are persisted in SQLite and survive daemon restarts.

maxConcurrentPerTarget gates how many A2A tasks can target a single persona simultaneously. Additional submissions are rejected until one of the in-flight tasks finishes, preventing a single persona from being overwhelmed by concurrent delegation.

maxAttempts controls how many times a failed collaboration item is retried before it is moved to the dead-letter queue.

Each A2A task carries a hop counter. When persona A delegates to persona B, the hop count increments. Talon enforces a maximum hop depth to prevent infinite delegation chains.

The default limit is 4 hops, configurable via a2a.maxHops in talond.yaml (bounds: 1–32). When the limit is reached, the task is rejected with a clear error message so the calling persona can handle the failure.

All three A2A guardrails live under the top-level a2a: key in talond.yaml:

a2a:
maxHops: 4
maxConcurrentPerTarget: 1
maxAttempts: 3
FieldDefaultBoundsWhen to change
maxHops41–32Increase if you have deep orchestrator chains; decrease to tighten delegation depth.
maxConcurrentPerTarget11–100Raise when a single persona is a hot-spot and can safely handle multiple A2A tasks in parallel.
maxAttempts31–20Increase for transient downstream failures; decrease to fail faster.

Existing configurations that do not include an a2a: block continue to work unchanged — the defaults match the previous hardcoded values.

personas:
- name: orchestrator
model: claude-sonnet-4-6
systemPromptFile: personas/orchestrator/system.md
capabilities:
allow:
- persona.send:*
- channel.send:my-slack
- name: researcher
model: claude-sonnet-4-6
systemPromptFile: personas/researcher/system.md
skills:
- web-search
capabilities:
allow:
- net.http
- name: writer
model: claude-sonnet-4-6
systemPromptFile: personas/writer/system.md
capabilities:
allow:
- channel.send:my-slack

The orchestrator receives user requests, delegates research to researcher (with await_reply: true), then delegates writing to writer. Each persona has only the capabilities it needs.