1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
//! The ACP request handler trait — the consumer's
//! extension point for ACP JSON-RPC methods.
//!
//! # What this is
//!
//! A consumer who runs an ACP agent implements
//! [`AcpRequestHandler`] to define what each ACP
//! method does. The serving adapter
//! (the agent's `AcpAgentPeer` paired with a
//! matching JSON-RPC dispatcher) decodes the
//! JSON-RPC envelope, looks up the method name
//! in the [`AgentMethod`](crate::domain::acp::method::AgentMethod) table, and calls the
//! handler with the matched method, the raw
//! params, and the per-request context. The
//! handler returns the raw result (or an
//! [`AcpError`]); the serving adapter serialises
//! the result into a JSON-RPC response.
//!
//! # Why a single dispatch method
//!
//! The ACP v1.25.0 spec defines 13 agent-side
//! methods. A trait with 13 typed methods is
//! exhaustive (a handler must implement all
//! 13) and locks the wire shape of every method
//! at the type level. A single dispatch method
//! lets a consumer implement only the methods
//! they care about and return
//! [`AcpError::MethodNotImplemented`] for the
//! rest, which is what the early-adopter story
//! looks like in practice: an agent starts with
//! the three or four methods it needs (`initialize`,
//! `session/new`, `session/prompt`, `session/cancel`),
//! and adds the others as clients ask.
//!
//! The trade-off is no per-method compile-time
//! type safety on the params. The params arrive
//! as [`serde_json::Value`]; the consumer is
//! responsible for decoding them. A future
//! per-method typed wrapper is a separate
//! commit (the [`super::typed_dispatch`] module),
//! not a replacement for this trait.
//!
//! # Why this is object-safe
//!
//! The serving adapter is generic over a concrete
//! handler in the call site, but a consumer who
//! wants to swap handlers at runtime holds an
//! `Arc<dyn AcpRequestHandler>`. Object safety
//! keeps both options open. The trade-off is the
//! boxed future on the per-call path: the JSON-RPC
//! dispatch itself is the project's hot path, not
//! the handler invocation. The
//! `Pin<Box<dyn Future + Send>>` signature
//! matches [`A2aRequestHandler`](super::super::a2a::A2aRequestHandler)
//! in spirit but is a single concrete shape,
//! not RPITIT, because `async fn` in a trait
//! does not make the resulting future
//! object-safe in 1.98.
use fmt;
use Pin;
use Future;
use Value;
use crateAgentMethod;
/// An ACP-specific error.
///
/// The variants are the JSON-RPC error codes the
/// ACP spec names. The serving adapter maps each
/// variant to a code and message on the wire; the
/// consumer never has to know the codes.
///
/// # JSON-RPC error codes
///
/// - `-32601`: `MethodNotFound` / `MethodNotImplemented`
/// - `-32602`: `InvalidParams`
/// - `-32603`: `InternalError`
///
/// The negative numbers are the JSON-RPC
/// standard's reserved range. ACP v1.25.0
/// does not extend the implementation-defined
/// `-32000..=-32099` range, so this enum has
/// the same surface as the JSON-RPC standard's
/// own.
/// The per-request context the ACP handler
/// receives.
///
/// Carries the negotiated protocol version (so
/// the handler can decode per-revision params
/// against it) and the auth context (so a
/// handler that needs to check identity has it
/// without re-parsing the wire envelope). Future
/// fields land here as the protocol grows: the
/// request id, the request deadline, the session
/// id extracted from the params (where it is
/// always present), the cancellation handle for
/// the in-flight prompt.
/// The consumer's extension point for ACP
/// methods.
///
/// The serving adapter calls [`dispatch`](Self::dispatch)
/// once per JSON-RPC request. The handler receives
/// the method (already validated against the
/// ACP vocabulary) and the raw `serde_json::Value`
/// params. The handler returns the raw result
/// value, or an [`AcpError`].
///
/// # Object safety
///
/// The trait is object-safe. A consumer who wants
/// to swap handlers at runtime holds an
/// `Arc<dyn AcpRequestHandler>`; the serving
/// adapter accepts either an `Arc<dyn>` or a
/// concrete `H: AcpRequestHandler`.
///
/// # Default impls
///
/// None of the methods have a default impl. A
/// consumer who wants to leave a method
/// unimplemented can return
/// [`AcpError::MethodNotImplemented`] from
/// `dispatch`; the serving adapter maps that to
/// JSON-RPC `-32601` and the caller's client
/// knows the method is not supported.
///
/// # Example
///
/// A minimal handler that implements only
/// `initialize` and returns
/// `AcpError::MethodNotImplemented` for the
/// other twelve methods. The full reference
/// impl is in a future `acp_agent` example.
///
/// ```no_run
/// use std::pin::Pin;
/// use std::sync::Arc;
///
/// use futures_core::Future;
/// use serde_json::{json, Value};
///
/// use protocol_droid::domain::acp::method::AgentMethod;
/// use protocol_droid::application::acp::{
/// AcpError, AcpRequestContext, AcpRequestHandler,
/// };
///
/// struct InitializeOnlyAgent;
///
/// impl AcpRequestHandler for InitializeOnlyAgent {
/// fn dispatch(
/// &self,
/// method: AgentMethod,
/// _params: Option<Value>,
/// _context: &AcpRequestContext,
/// ) -> Pin<
/// Box<dyn Future<Output = Result<Value, AcpError>> + Send + '_>,
/// > {
/// Box::pin(async move {
/// match method {
/// AgentMethod::Initialize => {
/// // Run the agent's initialize
/// // handshake, return the
/// // response as JSON.
/// Ok(json!({"protocolVersion": 1, "agentInfo": {}}))
/// }
/// other => Err(AcpError::MethodNotImplemented {
/// method: other,
/// }),
/// }
/// })
/// }
/// }
///
/// let _handler: Arc<dyn AcpRequestHandler> = Arc::new(InitializeOnlyAgent);
/// ```