Summary
The A2A protocol defines contextId as a correlation key for grouping tasks, but it does not specify multi-turn conversation semantics — what happens when a client sends multiple message/send calls with the same contextId.
This proposal defines a convention for multi-turn conversations using two mechanisms already present in the spec:
STATE_INPUT_REQUIRED — a new terminal task state signaling the agent expects a follow-up
- History injection — a non-normative convention for prepending prior conversation context
Update (7/20): Incorporated feedback from @0xbrainkid — added the three-input distinction model, continuation hint block, and digest-based replay preimage.
Motivation
Current behavior: each message/send is an isolated stateless call. If a client wants a conversation, it must manually track and resend history. This breaks the UX of "chat with an agent across turns" — the most common human-AI interaction pattern.
Many A2A implementations (hermes-agent, ADK, crewAI) already hack around this by prepending conversation history in the caller. Standardizing the pattern would:
- Make cross-SDK multi-turn conversations interoperable
- Give SDK authors a clear contract for when to stop ("waiting for input" vs "done")
Proposal
1. STATE_INPUT_REQUIRED task state
Add a terminal task state that means: the agent completed its current work but expects a follow-up message from the user.
TaskState enum:
...
STATE_INPUT_REQUIRED = "input-required"
Different from STATE_COMPLETED: the agent wants a reply.
Different from STATE_WORKING: the agent is not currently processing.
2. Three-input distinction (credit: @0xbrainkid)
When a client sends message/send with a contextId that has prior completed tasks, the agent MUST distinguish three inputs:
| Input type |
Protocol carrier |
Authorization implication |
| New user message |
Message.message |
This is the instruction |
| Prior conversation |
contextId → tasks/list history |
Evidence for continuity, not new instruction |
| Prior artifacts |
Task.artifacts[] in completed tasks |
Receipts — verifiable, not instructional |
All three share a contextId, but the agent MUST be able to tell which is which. Otherwise "same contextId" is ambiguous about what authorized the follow-up.
3. Continuation hint block
STATE_INPUT_REQUIRED may carry an optional continuation hint to give clients and downstream systems precision:
{
"state": "input-required",
"continuation": {
"expected_input": "free_text",
"resume_scope": "same_task",
"history_policy": "agent_fetched",
"accepted_history_digest": "sha256:abc123..."
}
}
| Field |
Type |
Description |
expected_input |
string |
free_text, structured_artifact, approval, or credential |
resume_scope |
string |
same_task or new_task_in_context |
history_policy |
string |
client_supplied, agent_fetched, or none |
accepted_history_digest |
string |
Hash over (new_message + selected_history_window + relevant_artifact_ids) |
All fields optional — a bare STATE_INPUT_REQUIRED is already useful. The continuation block adds precision for agents that need it.
The accepted_history_digest gives downstream systems a stable preimage for replay verification, without making A2A a receipt protocol. SATP-style receipts or other provenance layers can bind to this preimage independently.
4. Multi-turn semantics (non-normative convention)
When a client sends message/send with a contextId that has prior completed tasks:
Agent SHOULD:
- Treat prior messages in the same
contextId as conversation history (evidence for continuity, not as new instructions)
- Use that history for continuity only
Client MAY:
- Prepend prior conversation text to the message body
- Or rely on the agent to fetch history via
tasks/list or tasks/get
5. Concurrent-call guard
An agent SHOULD reject message/send for a contextId that has an in-flight STATE_WORKING task, returning STATE_FAILED with a clear error message.
Relationship to other A2A proposals
| Concern |
Proposal |
Relationship |
| Trace context |
#2026 |
Tells an operator what execution path this belongs to; does not grant authorization |
| Conversation context |
this issue (#2061) |
Tells an agent what happened before; does not become new instruction |
| Audit records |
MCP SEP #3004 |
Records who acted, under what binding; independently verifiable |
These three compose without coupling: same contextId can carry trace continuity + conversation history + per-turn audit receipts, each independently valid, none granting authorization.
Prior art
- OpenAI Assistants API:
run status + requires_action for tool calls
- Anthropic MCP: stateless by default; conversation state is client-side
- hermes-agent A2A plugin: already implements this pattern (history injection +
STATE_INPUT_REQUIRED heuristic + concurrent guard)
Open questions
- Should
STATE_INPUT_REQUIRED be a terminal state or a sub-state of COMPLETED?
- Should history injection be the agent's responsibility or the client's?
- What's the interaction with
historyLength in MessageSendConfiguration?
- Should
continuation.accepted_history_digest use SHA-256 or a content-addressed scheme?
Reference implementation
hermes-agent PR: NousResearch/hermes-agent#64982
Summary
The A2A protocol defines
contextIdas a correlation key for grouping tasks, but it does not specify multi-turn conversation semantics — what happens when a client sends multiplemessage/sendcalls with the samecontextId.This proposal defines a convention for multi-turn conversations using two mechanisms already present in the spec:
STATE_INPUT_REQUIRED— a new terminal task state signaling the agent expects a follow-upUpdate (7/20): Incorporated feedback from @0xbrainkid — added the three-input distinction model, continuation hint block, and digest-based replay preimage.
Motivation
Current behavior: each
message/sendis an isolated stateless call. If a client wants a conversation, it must manually track and resend history. This breaks the UX of "chat with an agent across turns" — the most common human-AI interaction pattern.Many A2A implementations (hermes-agent, ADK, crewAI) already hack around this by prepending conversation history in the caller. Standardizing the pattern would:
Proposal
1.
STATE_INPUT_REQUIREDtask stateAdd a terminal task state that means: the agent completed its current work but expects a follow-up message from the user.
Different from
STATE_COMPLETED: the agent wants a reply.Different from
STATE_WORKING: the agent is not currently processing.2. Three-input distinction (credit: @0xbrainkid)
When a client sends
message/sendwith acontextIdthat has prior completed tasks, the agent MUST distinguish three inputs:Message.messagecontextId→tasks/listhistoryTask.artifacts[]in completed tasksAll three share a
contextId, but the agent MUST be able to tell which is which. Otherwise "same contextId" is ambiguous about what authorized the follow-up.3. Continuation hint block
STATE_INPUT_REQUIREDmay carry an optional continuation hint to give clients and downstream systems precision:{ "state": "input-required", "continuation": { "expected_input": "free_text", "resume_scope": "same_task", "history_policy": "agent_fetched", "accepted_history_digest": "sha256:abc123..." } }expected_inputfree_text,structured_artifact,approval, orcredentialresume_scopesame_taskornew_task_in_contexthistory_policyclient_supplied,agent_fetched, ornoneaccepted_history_digestAll fields optional — a bare
STATE_INPUT_REQUIREDis already useful. The continuation block adds precision for agents that need it.The
accepted_history_digestgives downstream systems a stable preimage for replay verification, without making A2A a receipt protocol. SATP-style receipts or other provenance layers can bind to this preimage independently.4. Multi-turn semantics (non-normative convention)
When a client sends
message/sendwith acontextIdthat has prior completed tasks:Agent SHOULD:
contextIdas conversation history (evidence for continuity, not as new instructions)Client MAY:
tasks/listortasks/get5. Concurrent-call guard
An agent SHOULD reject
message/sendfor acontextIdthat has an in-flightSTATE_WORKINGtask, returningSTATE_FAILEDwith a clear error message.Relationship to other A2A proposals
These three compose without coupling: same
contextIdcan carry trace continuity + conversation history + per-turn audit receipts, each independently valid, none granting authorization.Prior art
runstatus +requires_actionfor tool callsSTATE_INPUT_REQUIREDheuristic + concurrent guard)Open questions
STATE_INPUT_REQUIREDbe a terminal state or a sub-state ofCOMPLETED?historyLengthinMessageSendConfiguration?continuation.accepted_history_digestuse SHA-256 or a content-addressed scheme?Reference implementation
hermes-agent PR: NousResearch/hermes-agent#64982