Skip to content

Proposal: Multi-turn conversation semantics — STATE_INPUT_REQUIRED + context persistence #2061

Description

@kuangmi-bit

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:

  1. STATE_INPUT_REQUIRED — a new terminal task state signaling the agent expects a follow-up
  2. 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 contextIdtasks/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

  1. Should STATE_INPUT_REQUIRED be a terminal state or a sub-state of COMPLETED?
  2. Should history injection be the agent's responsibility or the client's?
  3. What's the interaction with historyLength in MessageSendConfiguration?
  4. Should continuation.accepted_history_digest use SHA-256 or a content-addressed scheme?

Reference implementation

hermes-agent PR: NousResearch/hermes-agent#64982

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions