You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/specification.md
+272-7Lines changed: 272 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -322,7 +322,7 @@ Specifies optional A2A protocol features supported by the agent.
322
322
--8<--"types/src/types.ts:AgentCapabilities"
323
323
```
324
324
325
-
-**`idempotencyEnforced`**: When set to `true`, indicates that the agent enforces messageId-based idempotency for new task creation. This enables the agent to detect and handle duplicate `messageId` values to prevent unintended duplicate task creation due to network failures or client retries. See [Section 7.1.2](#712-idempotency) for detailed behavior.
325
+
-**`idempotencyEnforced`**: When set to `true`, indicates that the agent enforces messageId-based idempotency for task creation and follow-up. This enables the agent to detect and handle duplicate `messageId` values to prevent unintended duplicate task creation or wrong behavior in multi turn messages due to network failures or client retries. See [Section 7.1.2](#712-idempotency) for detailed behavior.
326
326
327
327
#### 5.5.2.1. `AgentExtension` Object
328
328
@@ -727,28 +727,33 @@ The `error` response for all transports in case of failure is a [`JSONRPCError`]
727
727
728
728
A2A supports optional messageId-based idempotency to enable idempotent task creation, addressing scenarios where network failures or client crashes could result in duplicate tasks with unintended side effects.
729
729
730
-
**Scope**: Idempotency **ONLY**applies to new task creation (when `message.taskId` is not provided). Messages sent to continue existing tasks follow normal message uniqueness rules without task-level deduplication.
730
+
**Scope**: Idempotency applies to new task creation (when `message.taskId` is not provided) and on messages sent to continue existing tasks in a multi turn workflow.
731
731
732
732
**Agent Requirements:**
733
733
734
734
- Agents **MAY** enforce idempotency by declaring `idempotencyEnforced: true` in their `AgentCapabilities`.
735
-
- When `idempotencyEnforced` is `true`, agents **MUST** track `messageId` values for new task creation within the authenticated user/session scope.
735
+
- When `idempotencyEnforced` is `true`, agents **MUST** track `messageId` values for new task creation and multi-turn message continuation within the authenticated user/session scope.
736
736
- When `idempotencyEnforced` is `false` or absent, agents **MAY** not enforce idempotency and do not track `messageId` values.
737
737
738
738
**Server Behavior:**
739
739
740
740
-**MessageId scope**: MessageId tracking is scoped to the authenticated user/session to prevent cross-user conflicts.
741
-
-**Active task collision**: If a `messageId` (for new task creation) matches an existing task in a non-terminal state (`submitted`, `working`, `input-required`), the server **MUST** return a [`MessageIdAlreadyExistsError`](#82-a2a-specific-errors) (-32008).
741
+
-**MessageId collision handling**: If a `messageId` matches a value previously seen, the server **MUST**:
742
+
-**Content hash verification**: If the SHA256 hash of the request message content matches the hash of the previous message AND the matched message is the most recent message for the Task/Message, then:
743
+
- If the request is non-blocking, OR the task is in a terminal state, OR the response was a Message: immediately return the current state of the Task/Message.
744
+
- If the request is blocking and the task is in a non-terminal state: block until the Task transitions to a terminal state, and return the Task.
745
+
-**Content mismatch**: Otherwise, return a [`MessageIdAlreadyExistsError`](#82-a2a-specific-errors) (-32008) indicating the `messageId` is already associated with different content.
742
746
-**Terminal task reuse**: If a `messageId` matches a task in a terminal state (`completed`, `failed`, `canceled`, `rejected`), the server **MAY** allow creating a new task with the same messageId.
743
747
-**Time-based expiry**: Servers **MAY** implement time-based expiry for messageId tracking associated with terminal tasks.
744
748
745
749
**Client Responsibilities:**
746
750
747
751
-**Unique messageIds**: Clients **MUST** generate unique `messageId` values for each intended new task within their authenticated session (this is already required by the base A2A specification).
752
+
-**Simple retry loops**: Clients can implement simple retry loops that keep sending the same request until it succeeds, without worrying about handling collision errors for genuine retries.
748
753
-**Error handling**: When receiving `MessageIdAlreadyExistsError`, clients **SHOULD**:
749
-
1. Use the `existingTaskId` from the error data to call `tasks/get` to retrieve the existing task. This is the correct action when retrying a request that may have already succeeded (e.g., after a network error).
750
-
2.Alternatively, if the `messageId` was reused unintentionally and a new, distinct task is desired, generate a new `messageId` and retry the request.
751
-
-**Retry safety**: Clients can safely retry `message/send` requests with the same `messageId` after network failures when creating new tasks.
754
+
1. Use the `existingTaskId` from the error data to call `tasks/get` to retrieve the existing task. This indicates the client has sent a different message with the same `messageId`.
755
+
2.If a new, distinct task is desired, generate a new `messageId` and retry the request.
756
+
-**Retry safety**: Clients can safely retry `message/send` requests with identical content and the same `messageId` after network failures, as the server will return the appropriate response without creating duplicate tasks.
752
757
753
758
### 7.2. `message/stream`
754
759
@@ -2003,6 +2008,266 @@ _If the task were longer-running, the server might initially respond with `statu
2003
2008
2004
2009
This pattern ensures that duplicate operations are prevented while allowing clients to safely recover from network failures.
2005
2010
2011
+
### 9.9. Idempotent Multi-Turn Interaction
2012
+
2013
+
**Scenario:** Client wants to shop for items, and network failures cause message retransmission during the multi-turn workflow. The agent enforces idempotency to prevent duplicate orders.
2014
+
2015
+
1.**Client discovers agent supports idempotency from Agent Card:**
2016
+
2017
+
```json
2018
+
{
2019
+
"capabilities": {
2020
+
"idempotencyEnforced": true
2021
+
}
2022
+
}
2023
+
```
2024
+
2025
+
2.**Client sends initial shopping request (new task creation):**
2026
+
2027
+
```json
2028
+
{
2029
+
"jsonrpc": "2.0",
2030
+
"id": "req-001",
2031
+
"method": "message/send",
2032
+
"params": {
2033
+
"message": {
2034
+
"role": "user",
2035
+
"parts": [
2036
+
{
2037
+
"kind": "text",
2038
+
"text": "I want to go shopping"
2039
+
}
2040
+
],
2041
+
"messageId": "msg1-shopping-start"
2042
+
}
2043
+
}
2044
+
}
2045
+
```
2046
+
2047
+
3.**Server creates task and requests input:**
2048
+
2049
+
```json
2050
+
{
2051
+
"jsonrpc": "2.0",
2052
+
"id": "req-001",
2053
+
"result": {
2054
+
"id": "task1-shopping-session",
2055
+
"contextId": "ctx-shopping-001",
2056
+
"status": {
2057
+
"state": "input-required",
2058
+
"message": {
2059
+
"role": "agent",
2060
+
"parts": [
2061
+
{
2062
+
"kind": "text",
2063
+
"text": "Welcome to our store! What would you like to add to your cart?"
2064
+
}
2065
+
],
2066
+
"messageId": "agent-msg1",
2067
+
"taskId": "task1-shopping-session",
2068
+
"contextId": "ctx-shopping-001"
2069
+
}
2070
+
},
2071
+
"history": [
2072
+
{
2073
+
"role": "user",
2074
+
"parts": [{"kind": "text", "text": "I want to go shopping"}],
2075
+
"messageId": "msg1-shopping-start",
2076
+
"taskId": "task1-shopping-session",
2077
+
"contextId": "ctx-shopping-001"
2078
+
}
2079
+
],
2080
+
"kind": "task"
2081
+
}
2082
+
}
2083
+
```
2084
+
2085
+
4.**Client adds first item to cart:**
2086
+
2087
+
```json
2088
+
{
2089
+
"jsonrpc": "2.0",
2090
+
"id": "req-002",
2091
+
"method": "message/send",
2092
+
"params": {
2093
+
"message": {
2094
+
"role": "user",
2095
+
"parts": [
2096
+
{
2097
+
"kind": "text",
2098
+
"text": "Add a SuperX4 Laptop to my cart"
2099
+
}
2100
+
],
2101
+
"taskId": "task1-shopping-session",
2102
+
"contextId": "ctx-shopping-001",
2103
+
"messageId": "msg2-add-laptop"
2104
+
}
2105
+
}
2106
+
}
2107
+
```
2108
+
2109
+
5.**Server processes request and responds:**
2110
+
2111
+
```json
2112
+
{
2113
+
"jsonrpc": "2.0",
2114
+
"id": "req-002",
2115
+
"result": {
2116
+
"id": "task1-shopping-session",
2117
+
"contextId": "ctx-shopping-001",
2118
+
"status": {
2119
+
"state": "input-required",
2120
+
"message": {
2121
+
"role": "agent",
2122
+
"parts": [
2123
+
{
2124
+
"kind": "text",
2125
+
"text": "Added SuperX4 Laptop ($1,299) to your cart. Anything else?"
2126
+
}
2127
+
]
2128
+
}
2129
+
},
2130
+
"kind": "task"
2131
+
}
2132
+
}
2133
+
```
2134
+
2135
+
6.**Network failure occurs - client retries the same laptop addition:**
2136
+
2137
+
```json
2138
+
{
2139
+
"jsonrpc": "2.0",
2140
+
"id": "req-003",
2141
+
"method": "message/send",
2142
+
"params": {
2143
+
"message": {
2144
+
"role": "user",
2145
+
"parts": [
2146
+
{
2147
+
"kind": "text",
2148
+
"text": "Add a SuperX4 Laptop to my cart"
2149
+
}
2150
+
],
2151
+
"taskId": "task1-shopping-session",
2152
+
"contextId": "ctx-shopping-001",
2153
+
"messageId": "msg2-add-laptop"
2154
+
}
2155
+
}
2156
+
}
2157
+
```
2158
+
2159
+
7.**Server detects identical messageId and content hash, returns current state without duplicate action:**
2160
+
2161
+
```json
2162
+
{
2163
+
"jsonrpc": "2.0",
2164
+
"id": "req-003",
2165
+
"result": {
2166
+
"id": "task1-shopping-session",
2167
+
"contextId": "ctx-shopping-001",
2168
+
"status": {
2169
+
"state": "input-required",
2170
+
"message": {
2171
+
"role": "agent",
2172
+
"parts": [
2173
+
{
2174
+
"kind": "text",
2175
+
"text": "Added SuperX4 Laptop ($1,299) to your cart. Anything else?"
2176
+
}
2177
+
]
2178
+
}
2179
+
},
2180
+
"kind": "task"
2181
+
}
2182
+
}
2183
+
```
2184
+
2185
+
8.**Client adds second item (different messageId):**
2186
+
2187
+
```json
2188
+
{
2189
+
"jsonrpc": "2.0",
2190
+
"id": "req-004",
2191
+
"method": "message/send",
2192
+
"params": {
2193
+
"message": {
2194
+
"role": "user",
2195
+
"parts": [
2196
+
{
2197
+
"kind": "text",
2198
+
"text": "Add a XtraScreen to my cart"
2199
+
}
2200
+
],
2201
+
"taskId": "task1-shopping-session",
2202
+
"contextId": "ctx-shopping-001",
2203
+
"messageId": "msg3-add-screen"
2204
+
}
2205
+
}
2206
+
}
2207
+
```
2208
+
2209
+
9.**Client finalizes order:**
2210
+
2211
+
```json
2212
+
{
2213
+
"jsonrpc": "2.0",
2214
+
"id": "req-005",
2215
+
"method": "message/send",
2216
+
"params": {
2217
+
"message": {
2218
+
"role": "user",
2219
+
"parts": [
2220
+
{
2221
+
"kind": "text",
2222
+
"text": "I'm done let's pay"
2223
+
}
2224
+
],
2225
+
"taskId": "task1-shopping-session",
2226
+
"contextId": "ctx-shopping-001",
2227
+
"messageId": "msg4-checkout"
2228
+
}
2229
+
}
2230
+
}
2231
+
```
2232
+
2233
+
10.**Server completes transaction with correct single laptop:**
This example demonstrates how idempotency in multi-turn workflows prevents duplicate operations (only one laptop was ordered despite the retry) while allowing the conversation to continue naturally.
2270
+
2006
2271
These examples illustrate the flexibility of A2A in handling various interaction patterns and data types. Implementers should refer to the detailed object definitions for all fields and constraints.
0 commit comments