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
{{ message }}
Repository navigation
Commit 9e1e945
Browse filesBrowse the repository at this point in the historyBrowse files
feat(acp)!: version-neutral raw JSON-RPC request IDs (#466)
* test(acp): pin the request ID wire format of v1, v2 and HTTP connections
Drive agents over newline-delimited JSON, and an HttpClient against an
AcpHttpServer over HTTP and WebSocket, comparing each message's JSON text.
Integer, string and (v1 only) null IDs chosen by the peer are echoed
exactly, IDs the SDK chooses match the peer's responses, and IDs outside
the 64-bit signed range are rejected with id: null.
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(acp)!: version-neutral raw JSON-RPC request IDs
Add `RawJsonRpcId`, the SDK's own JSON-RPC ID (null, i64 or string), and
use it throughout the raw layer instead of `schema::v1::RequestId`: raw
requests and responses, `UntypedMessage` conversions, the incoming and
outgoing actors, protocol negotiation, and the request handles. The HTTP
transport routes responses by it, and the conductor's tracer records it.
`RawJsonRpcId` serializes exactly like the schema IDs, so the wire format
is unchanged. It converts losslessly to and from `v1::RequestId` and from
`v2::RequestId`; converting to `v2::RequestId` fails only for null, with
the Invalid Request error a v2 connection sends for a null request ID.
`RawJsonRpcMessage::Request` now holds the SDK's `RawJsonRpcRequest`, and
`RawJsonRpcResponse` is the SDK's own enum with the same variants and
constructor. The `v2_id()` accessors stay; `Responder::v2_id` is now a
plain conversion and the private `v2_request_id` helper is gone.
BREAKING CHANGE: `id()` on `Responder`, `ResponseRouter`, `SentRequest`
and `PreparedRequest` returns `&RawJsonRpcId`, `Dispatch::id()` and
`RawJsonRpcMessage::response_id()` return `Option<&RawJsonRpcId>`,
`RawJsonRpcMessage::Request` holds a `RawJsonRpcRequest` instead of
`schema::v1::Request<RawJsonRpcParams>`, and `RawJsonRpcResponse` is no
longer an alias of `schema::v1::Response`. Constructors and
`send_cancel_request` take `impl Into<RawJsonRpcId>`, so existing
`v1::RequestId` arguments still compile. See `md/migration-v4.md`.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: md/migration-v4.md
+65Lines changed: 65 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -117,6 +117,71 @@ Because the two used to be the same type, some code imported `Error` from
117
117
`schema::v1` and used it with SDK callbacks. Import
118
118
`agent_client_protocol::Error` instead, or convert with `.into()`.
119
119
120
+
## A version-neutral `RawJsonRpcId`
121
+
122
+
The raw JSON-RPC layer and the request handles used `schema::v1::RequestId`
123
+
as their ID type, even on v2 connections. They now use the SDK's own
124
+
`agent_client_protocol::RawJsonRpcId`, the same on every protocol version:
125
+
126
+
```rust,ignore
127
+
pub enum RawJsonRpcId {
128
+
Null,
129
+
Number(i64),
130
+
Str(String),
131
+
}
132
+
```
133
+
134
+
It has the same variants, serialization, `Display`, `Hash` and ordering as
135
+
`schema::v1::RequestId`, so IDs are sent on the wire exactly as before: `1`
136
+
and `"1"` stay distinct, and integers keep the full 64-bit signed range.
137
+
138
+
### What changed
139
+
140
+
| Before | After |
141
+
| --- | --- |
142
+
|`RawJsonRpcMessage::Request(schema::v1::Request<RawJsonRpcParams>)`|`RawJsonRpcMessage::Request(RawJsonRpcRequest)`, with the same `id`, `method` and `params` fields |
143
+
|`RawJsonRpcResponse`, an alias of `schema::v1::Response<Value, Box<RawJsonRpcError>>`|`RawJsonRpcResponse`, the SDK's own enum with the same `Result { id, result }` and `Error { id, error }` variants and `new`|
144
+
|`RawJsonRpcMessage::response_id()` returned `Option<&v1::RequestId>`| returns `Option<&RawJsonRpcId>`|
145
+
|`id()` on `Responder`, `ResponseRouter`, `SentRequest` and `PreparedRequest` returned `&v1::RequestId`| returns `&RawJsonRpcId`|
146
+
|`Dispatch::id()` returned `Option<&v1::RequestId>`| returns `Option<&RawJsonRpcId>`|
147
+
148
+
`RawJsonRpcMessage::request`, `RawJsonRpcMessage::response` and
149
+
`RawJsonRpcResponse::new` take `impl Into<RawJsonRpcId>`, so passing a
150
+
`v1::RequestId`, `v2::RequestId`, `i64` or `String` still compiles. So do
151
+
`ConnectionTo::send_cancel_request` and `send_cancel_request_to`. A bare
152
+
`1.into()` no longer infers its type; write `RawJsonRpcId::Number(1)`.
153
+
154
+
### Updating your code
155
+
156
+
Replace `schema::v1::RequestId` with `RawJsonRpcId` where you build or match
157
+
raw messages or store handle IDs:
158
+
159
+
```rust,ignore
160
+
use agent_client_protocol::{RawJsonRpcId, RawJsonRpcMessage};
161
+
162
+
// Before
163
+
let request = RawJsonRpcMessage::request(method, params, v1::RequestId::Number(1))?;
164
+
let pending: HashMap<v1::RequestId, Route> = HashMap::new();
165
+
166
+
// After
167
+
let request = RawJsonRpcMessage::request(method, params, RawJsonRpcId::Number(1))?;
168
+
let pending: HashMap<RawJsonRpcId, Route> = HashMap::new();
169
+
```
170
+
171
+
Where a schema type needs a request ID, such as `v1::CancelRequestNotification`
172
+
or an elicitation's `requestId`, convert. `RawJsonRpcId` converts losslessly to
173
+
and from `v1::RequestId`, and from `v2::RequestId`. Converting to
174
+
`v2::RequestId` fails only for `null`, with the same Invalid Request error
175
+
(`-32600`) a v2 connection sends for a `null` request ID:
176
+
177
+
```rust,ignore
178
+
let v1_id = v1::RequestId::from(responder.id().clone());
179
+
let raw_id = RawJsonRpcId::from(cancel.request_id);
180
+
let v2_id = v2::RequestId::try_from(responder.id().clone())?;
181
+
```
182
+
183
+
The `v2_id()` accessors are unchanged.
184
+
120
185
## Proxy method names
121
186
122
187
The proxy protocol now uses the method names defined by the
0 commit comments