a2aproject / a2aproject/A2A

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

オープン
#2,061 コメント 6 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
Shell
スター
25.7k
フォーク
2.6k
平均マージ
3日 6時間
マージ済み PR(30日)
16

説明

## 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 | `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:

```json
{
"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](https://github.com/a2aproject/A2A/issues/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](https://github.com/kuangmi-bit/audit-record-go) | 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: https://github.com/NousResearch/hermes-agent/pull/64982

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。