@cogitator-ai/a2a 0.3.11 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +64 -7
  2. package/dist/adapters/express.d.ts.map +1 -1
  3. package/dist/adapters/express.js +22 -22
  4. package/dist/adapters/express.js.map +1 -1
  5. package/dist/adapters/fastify.d.ts.map +1 -1
  6. package/dist/adapters/fastify.js +23 -24
  7. package/dist/adapters/fastify.js.map +1 -1
  8. package/dist/adapters/hono.d.ts.map +1 -1
  9. package/dist/adapters/hono.js +25 -8
  10. package/dist/adapters/hono.js.map +1 -1
  11. package/dist/adapters/koa.d.ts.map +1 -1
  12. package/dist/adapters/koa.js +16 -23
  13. package/dist/adapters/koa.js.map +1 -1
  14. package/dist/adapters/next.d.ts.map +1 -1
  15. package/dist/adapters/next.js +22 -19
  16. package/dist/adapters/next.js.map +1 -1
  17. package/dist/adapters/shared.d.ts +18 -0
  18. package/dist/adapters/shared.d.ts.map +1 -0
  19. package/dist/adapters/shared.js +52 -0
  20. package/dist/adapters/shared.js.map +1 -0
  21. package/dist/adapters/sse-error-event.d.ts +3 -0
  22. package/dist/adapters/sse-error-event.d.ts.map +1 -0
  23. package/dist/adapters/sse-error-event.js +11 -0
  24. package/dist/adapters/sse-error-event.js.map +1 -0
  25. package/dist/agent-card.d.ts.map +1 -1
  26. package/dist/agent-card.js +15 -2
  27. package/dist/agent-card.js.map +1 -1
  28. package/dist/client.d.ts +24 -4
  29. package/dist/client.d.ts.map +1 -1
  30. package/dist/client.js +163 -83
  31. package/dist/client.js.map +1 -1
  32. package/dist/errors.d.ts +1 -0
  33. package/dist/errors.d.ts.map +1 -1
  34. package/dist/errors.js +3 -0
  35. package/dist/errors.js.map +1 -1
  36. package/dist/index.d.ts +6 -5
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +3 -3
  39. package/dist/index.js.map +1 -1
  40. package/dist/json-rpc.d.ts +2 -2
  41. package/dist/json-rpc.d.ts.map +1 -1
  42. package/dist/json-rpc.js +11 -7
  43. package/dist/json-rpc.js.map +1 -1
  44. package/dist/push-notifications.d.ts +8 -1
  45. package/dist/push-notifications.d.ts.map +1 -1
  46. package/dist/push-notifications.js +178 -25
  47. package/dist/push-notifications.js.map +1 -1
  48. package/dist/redis-task-store.d.ts +1 -0
  49. package/dist/redis-task-store.d.ts.map +1 -1
  50. package/dist/redis-task-store.js +44 -4
  51. package/dist/redis-task-store.js.map +1 -1
  52. package/dist/server.d.ts +19 -2
  53. package/dist/server.d.ts.map +1 -1
  54. package/dist/server.js +287 -135
  55. package/dist/server.js.map +1 -1
  56. package/dist/task-manager.d.ts +27 -2
  57. package/dist/task-manager.d.ts.map +1 -1
  58. package/dist/task-manager.js +126 -36
  59. package/dist/task-manager.js.map +1 -1
  60. package/dist/task-store.d.ts +10 -0
  61. package/dist/task-store.d.ts.map +1 -1
  62. package/dist/task-store.js +28 -0
  63. package/dist/task-store.js.map +1 -1
  64. package/dist/types.d.ts +17 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/types.js +7 -0
  67. package/dist/types.js.map +1 -1
  68. package/package.json +5 -5
package/README.md CHANGED
@@ -14,7 +14,8 @@ pnpm add @cogitator-ai/a2a
14
14
  - **A2AClient** - Connect to remote A2A agents with discovery and streaming
15
15
  - **asTool() Bridge** - Wrap remote A2A agents as local Cogitator tools
16
16
  - **Agent Card** - Auto-generate A2A Agent Cards from agent metadata
17
- - **Task Management** - Full task lifecycle (working, completed, failed, canceled)
17
+ - **Task Management** - Full task lifecycle (working, input-required, completed, failed, canceled)
18
+ - **Authentication** - Bearer / API-key auth enforced by every framework adapter and advertised on the Agent Card
18
19
  - **Multi-Turn Conversations** - Stateful conversations with contextId and continueTask
19
20
  - **SSE Streaming** - Real-time streaming with token-level events
20
21
  - **Push Notifications** - Webhook-based task event notifications
@@ -102,6 +103,54 @@ const result = await cogitator.run(orchestrator, {
102
103
  });
103
104
  ```
104
105
 
106
+ The tool returns `{ output, success, error?, taskId?, state? }`. `output` is the remote agent's latest answer. When the remote task needs more input (`state: 'input-required'`), `success` is `false`, `output` carries the remote agent's question and `taskId` lets you continue the task with `client.continueTask()`. The run's abort signal and the tool timeout are forwarded to the HTTP request.
107
+
108
+ ## Authentication
109
+
110
+ Protect the JSON-RPC endpoint with a bearer token or an API key. Every framework adapter extracts the credential from the request headers (`Authorization: Bearer <token>` or the API-key header) and the server validates it before routing; the public Agent Card advertises the scheme in `securitySchemes`.
111
+
112
+ ```typescript
113
+ const a2aServer = new A2AServer({
114
+ agents: { researcher },
115
+ cogitator,
116
+ auth: {
117
+ type: 'bearer', // or 'apiKey' (header: 'x-api-key' unless headerName is set)
118
+ validate: async (token) => token === process.env.A2A_TOKEN,
119
+ },
120
+ });
121
+
122
+ const client = new A2AClient('https://remote-agent.example.com', {
123
+ headers: { Authorization: `Bearer ${process.env.A2A_TOKEN}` },
124
+ });
125
+ ```
126
+
127
+ Custom integrations can call `a2aServer.getAuthToken((name) => headers.get(name))` and pass the result to `handleJsonRpc(body, token)` / `handleJsonRpcStream(body, token, signal)`.
128
+
129
+ ## Send Configuration
130
+
131
+ `sendMessage(message, configuration)` supports the A2A `SendMessageConfiguration`:
132
+
133
+ | Option | Effect |
134
+ | ------------------------ | ------------------------------------------------------------------------------ |
135
+ | `blocking: false` | Return the task immediately in `working` state; poll `getTask` or use webhooks |
136
+ | `historyLength` | Return only the last N history messages (`0` = none); also on `getTask` |
137
+ | `acceptedOutputModes` | Only return artifacts with these MIME types |
138
+ | `timeout` | Maximum agent run time in ms |
139
+ | `pushNotificationConfig` | Register a webhook before the task starts executing |
140
+
141
+ ```typescript
142
+ const task = await client.sendMessage(
143
+ { role: 'user', parts: [{ type: 'text', text: 'Long running analysis' }] },
144
+ {
145
+ blocking: false,
146
+ pushNotificationConfig: { webhookUrl: 'https://my-app.com/webhooks/a2a' },
147
+ }
148
+ );
149
+ // task.status.state === 'working'; completion arrives at the webhook
150
+ ```
151
+
152
+ Client calls also accept request options: `client.sendMessage(message, config, { signal, timeout })`.
153
+
105
154
  ## Multi-Turn Conversations
106
155
 
107
156
  Continue tasks within the same conversation context using `contextId` and `continueTask`:
@@ -122,7 +171,7 @@ const updated = await client.continueTask(task.id, 'Now compare it with classica
122
171
  console.log(task.contextId === updated.contextId); // true
123
172
  ```
124
173
 
125
- On the server side, multi-turn is handled automatically. When a message includes a `taskId`, the server calls `continueTask` which appends to the existing task history and re-runs the agent with full context.
174
+ On the server side, multi-turn is handled automatically. When a message includes a `taskId`, the server calls `continueTask` which appends to the existing task history and re-runs the agent with the task transcript as context, so follow-ups work even without a memory adapter. Runs are threaded by `contextId` (`threadId`), and artifacts accumulate across turns. A task that is still running cannot be continued concurrently.
126
175
 
127
176
  ## Listing Tasks
128
177
 
@@ -172,6 +221,8 @@ for await (const event of client.sendMessageStream({
172
221
 
173
222
  The server emits `TokenStreamEvent` for each token generated by the LLM via the `onToken` callback, giving clients real-time character-by-character output.
174
223
 
224
+ Artifact events are sent before the final status event, and the stream closes on a terminal state or on `input-required`. The client `timeout` is an idle timeout between events (long runs are fine as long as events keep flowing); pass `{ signal }` as the third argument to cancel. When the client disconnects, the adapters abort the agent run.
225
+
175
226
  ## RedisTaskStore
176
227
 
177
228
  For production deployments, use `RedisTaskStore` instead of the default in-memory store:
@@ -193,7 +244,9 @@ const a2aServer = new A2AServer({
193
244
  });
194
245
  ```
195
246
 
196
- `RedisTaskStore` implements the full `TaskStore` interface including `list()` with filtering by `contextId`, `state`, pagination, and timestamp-based sorting.
247
+ `RedisTaskStore` implements the full `TaskStore` interface including `list()` with filtering by `contextId`, `state`, pagination, and timestamp-based sorting. Updates are atomic when the client supports `eval` (Lua).
248
+
249
+ `InMemoryTaskStore` keeps at most `maxSize` tasks (default 10 000) and evicts the oldest finished tasks first.
197
250
 
198
251
  ## Push Notifications
199
252
 
@@ -236,7 +289,9 @@ const a2aServer = new A2AServer({
236
289
  });
237
290
  ```
238
291
 
239
- When configured, the server automatically sends POST requests with `A2AStreamEvent` payloads to registered webhook URLs on task status and artifact updates. The Agent Card will advertise `pushNotifications: true`.
292
+ When configured, the server automatically sends POST requests with `A2AStreamEvent` payloads to registered webhook URLs on task status and artifact updates. The Agent Card will advertise `pushNotifications: true`. Configs can only be created for existing tasks (unknown ids return `TaskNotFound`); use `configuration.pushNotificationConfig` with `blocking: false` to subscribe before execution starts.
293
+
294
+ Webhook delivery is SSRF-hardened unless `allowPrivateUrls: true`: loopback, private, link-local, CGNAT, multicast and IPv4-mapped IPv6 targets are rejected, the check is applied to the address the socket actually connects to (no DNS-rebinding window), and redirects are not followed. `isPrivateAddress()` is exported for reuse.
240
295
 
241
296
  ## Agent Card Signing
242
297
 
@@ -307,9 +362,9 @@ The `agent/extendedCard` method is only available when `extendedCardGenerator` i
307
362
  import { a2aExpress } from '@cogitator-ai/a2a/express';
308
363
  app.use(a2aExpress(server));
309
364
 
310
- // Hono
365
+ // Hono (routes are /.well-known/agent.json and /a2a)
311
366
  import { a2aHono } from '@cogitator-ai/a2a/hono';
312
- app.route('/a2a', a2aHono(server));
367
+ app.route('/', a2aHono(server));
313
368
 
314
369
  // Fastify
315
370
  import { a2aFastify } from '@cogitator-ai/a2a/fastify';
@@ -324,11 +379,13 @@ import { a2aNext } from '@cogitator-ai/a2a/next';
324
379
  export const { GET, POST } = a2aNext(server);
325
380
  ```
326
381
 
382
+ All adapters stream only for `message/stream` (an `Accept: text/event-stream` header alone does not switch `message/send` to SSE), answer JSON-RPC notifications with `204`, and pass the request credentials to the server. Structurally invalid JSON-RPC requests get `-32600 Invalid Request`; only unparseable JSON gets `-32700 Parse error`.
383
+
327
384
  ## A2A Protocol
328
385
 
329
386
  | Method | Description |
330
387
  | ------------------------------- | ----------------------------------------- |
331
- | `message/send` | Send a message, get completed task |
388
+ | `message/send` | Send a message (blocking or background) |
332
389
  | `message/stream` | Send with SSE streaming (incl. tokens) |
333
390
  | `tasks/get` | Retrieve task by ID |
334
391
  | `tasks/cancel` | Cancel a running task |
@@ -1 +1 @@
1
- {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqC,MAAM,SAAS,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAkDpD"}
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAwD,MAAM,SAAS,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAK9C,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAmDpD"}
@@ -1,6 +1,7 @@
1
1
  import { Router, json } from 'express';
2
2
  import { createErrorResponse } from '../json-rpc.js';
3
3
  import * as errors from '../errors.js';
4
+ import { isStreamRequest, pipeJsonRpcStream, SSE_HEADERS } from './shared.js';
4
5
  export function a2aExpress(server) {
5
6
  const router = Router();
6
7
  router.get('/.well-known/agent.json', (_req, res) => {
@@ -9,44 +10,43 @@ export function a2aExpress(server) {
9
10
  });
10
11
  router.post('/a2a', json(), async (req, res) => {
11
12
  const contentType = req.headers['content-type'];
12
- if (contentType && !contentType.includes('application/json')) {
13
+ if (contentType && !contentType.startsWith('application/json')) {
13
14
  res.json(createErrorResponse(null, errors.contentTypeNotSupported(contentType)));
14
15
  return;
15
16
  }
16
17
  const body = req.body;
17
- const isStreaming = req.headers.accept?.includes('text/event-stream') || body?.method === 'message/stream';
18
- if (isStreaming) {
19
- res.writeHead(200, {
20
- 'Content-Type': 'text/event-stream',
21
- 'Cache-Control': 'no-cache',
22
- Connection: 'keep-alive',
18
+ const authToken = server.getAuthToken((name) => req.get(name));
19
+ if (isStreamRequest(body)) {
20
+ const controller = new AbortController();
21
+ res.on('close', () => controller.abort());
22
+ res.writeHead(200, SSE_HEADERS);
23
+ await pipeJsonRpcStream(server, body, authToken, controller.signal, (frame) => {
24
+ if (!res.writableEnded && !res.destroyed)
25
+ res.write(frame);
23
26
  });
24
- try {
25
- for await (const event of server.handleJsonRpcStream(body)) {
26
- if (res.writableEnded)
27
- break;
28
- res.write(`data: ${JSON.stringify(event)}\n\n`);
29
- }
30
- if (!res.writableEnded)
31
- res.write('data: [DONE]\n\n');
32
- }
33
- catch (error) {
34
- if (!res.writableEnded) {
35
- res.write(`data: ${JSON.stringify({ error: String(error) })}\n\n`);
36
- }
37
- }
38
27
  if (!res.writableEnded)
39
28
  res.end();
40
29
  return;
41
30
  }
42
31
  try {
43
- const response = await server.handleJsonRpc(body);
32
+ const response = await server.handleJsonRpc(body, authToken);
33
+ if (response === null) {
34
+ res.status(204).end();
35
+ return;
36
+ }
44
37
  res.json(response);
45
38
  }
46
39
  catch (error) {
47
40
  res.json(createErrorResponse(null, errors.internalError(String(error))));
48
41
  }
49
42
  });
43
+ router.use((err, _req, res, next) => {
44
+ if (err instanceof SyntaxError) {
45
+ res.json(createErrorResponse(null, errors.parseError('Invalid JSON body')));
46
+ return;
47
+ }
48
+ next(err);
49
+ });
50
50
  return router;
51
51
  }
52
52
  //# sourceMappingURL=express.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAA+B,MAAM,SAAS,CAAC;AAEpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAEvC,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,yBAAyB,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QACrE,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;QACrC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAChE,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAChD,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC7D,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACtB,MAAM,WAAW,GACf,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,mBAAmB,CAAC,IAAI,IAAI,EAAE,MAAM,KAAK,gBAAgB,CAAC;QAEzF,IAAI,WAAW,EAAE,CAAC;YAChB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;gBACjB,cAAc,EAAE,mBAAmB;gBACnC,eAAe,EAAE,UAAU;gBAC3B,UAAU,EAAE,YAAY;aACzB,CAAC,CAAC;YAEH,IAAI,CAAC;gBACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3D,IAAI,GAAG,CAAC,aAAa;wBAAE,MAAM;oBAC7B,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAClD,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,aAAa;oBAAE,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACxD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;oBACvB,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,aAAa;gBAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAClD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAgDhE"}
1
+ {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAK9C,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAkDhE"}
@@ -1,45 +1,44 @@
1
1
  import { createErrorResponse } from '../json-rpc.js';
2
2
  import * as errors from '../errors.js';
3
+ import { firstHeader, isStreamRequest, pipeJsonRpcStream, SSE_HEADERS } from './shared.js';
3
4
  export function a2aFastify(server) {
4
5
  return async (fastify) => {
6
+ fastify.setErrorHandler((error, _request, reply) => {
7
+ if (error instanceof SyntaxError || error.statusCode === 400) {
8
+ return reply.send(createErrorResponse(null, errors.parseError('Invalid JSON body')));
9
+ }
10
+ return reply.send(createErrorResponse(null, errors.internalError(error.message)));
11
+ });
5
12
  fastify.get('/.well-known/agent.json', async (_request, reply) => {
6
13
  const cards = server.getAgentCards();
7
14
  return reply.send(cards.length === 1 ? cards[0] : cards);
8
15
  });
9
16
  fastify.post('/a2a', async (request, reply) => {
10
17
  const contentType = request.headers['content-type'];
11
- if (contentType && !contentType.includes('application/json')) {
18
+ if (contentType && !contentType.startsWith('application/json')) {
12
19
  return reply.send(createErrorResponse(null, errors.contentTypeNotSupported(contentType)));
13
20
  }
14
21
  const body = request.body;
15
- const isStreaming = request.headers.accept?.includes('text/event-stream') || body?.method === 'message/stream';
16
- if (isStreaming) {
22
+ const authToken = server.getAuthToken((name) => firstHeader(request.headers[name.toLowerCase()]));
23
+ if (isStreamRequest(body)) {
17
24
  void reply.hijack();
18
- reply.raw.writeHead(200, {
19
- 'Content-Type': 'text/event-stream',
20
- 'Cache-Control': 'no-cache',
21
- Connection: 'keep-alive',
25
+ const raw = reply.raw;
26
+ const controller = new AbortController();
27
+ raw.on('close', () => controller.abort());
28
+ raw.writeHead(200, SSE_HEADERS);
29
+ await pipeJsonRpcStream(server, body, authToken, controller.signal, (frame) => {
30
+ if (!raw.writableEnded && !raw.destroyed)
31
+ raw.write(frame);
22
32
  });
23
- try {
24
- for await (const event of server.handleJsonRpcStream(body)) {
25
- if (reply.raw.writableEnded)
26
- break;
27
- reply.raw.write(`data: ${JSON.stringify(event)}\n\n`);
28
- }
29
- if (!reply.raw.writableEnded)
30
- reply.raw.write('data: [DONE]\n\n');
31
- }
32
- catch (error) {
33
- if (!reply.raw.writableEnded) {
34
- reply.raw.write(`data: ${JSON.stringify({ error: String(error) })}\n\n`);
35
- }
36
- }
37
- if (!reply.raw.writableEnded)
38
- reply.raw.end();
33
+ if (!raw.writableEnded)
34
+ raw.end();
39
35
  return;
40
36
  }
41
37
  try {
42
- const response = await server.handleJsonRpc(body);
38
+ const response = await server.handleJsonRpc(body, authToken);
39
+ if (response === null) {
40
+ return reply.code(204).send();
41
+ }
43
42
  return reply.send(response);
44
43
  }
45
44
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.js","sourceRoot":"","sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fastify.js","sourceRoot":"","sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+C/C"}
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAM9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6D/C"}
@@ -2,6 +2,8 @@ import { Hono } from 'hono';
2
2
  import { streamSSE } from 'hono/streaming';
3
3
  import { createErrorResponse } from '../json-rpc.js';
4
4
  import * as errors from '../errors.js';
5
+ import { buildSseErrorEvent } from './sse-error-event.js';
6
+ import { isStreamRequest } from './shared.js';
5
7
  export function a2aHono(server) {
6
8
  const app = new Hono();
7
9
  app.get('/.well-known/agent.json', (c) => {
@@ -10,7 +12,7 @@ export function a2aHono(server) {
10
12
  });
11
13
  app.post('/a2a', async (c) => {
12
14
  const contentType = c.req.header('content-type');
13
- if (contentType && !contentType.includes('application/json')) {
15
+ if (contentType && !contentType.startsWith('application/json')) {
14
16
  return c.json(createErrorResponse(null, errors.contentTypeNotSupported(contentType)));
15
17
  }
16
18
  let body;
@@ -20,23 +22,38 @@ export function a2aHono(server) {
20
22
  catch {
21
23
  return c.json(createErrorResponse(null, errors.parseError('Invalid JSON body')));
22
24
  }
23
- const isStreaming = c.req.header('accept')?.includes('text/event-stream') ||
24
- body?.method === 'message/stream';
25
- if (isStreaming) {
25
+ const authToken = server.getAuthToken((name) => c.req.header(name));
26
+ if (isStreamRequest(body)) {
27
+ c.header('X-Accel-Buffering', 'no');
26
28
  return streamSSE(c, async (stream) => {
29
+ const controller = new AbortController();
30
+ stream.onAbort(() => controller.abort());
27
31
  try {
28
- for await (const event of server.handleJsonRpcStream(body)) {
32
+ for await (const event of server.handleJsonRpcStream(body, authToken, controller.signal)) {
33
+ if (controller.signal.aborted)
34
+ return;
29
35
  await stream.writeSSE({ data: JSON.stringify(event) });
30
36
  }
31
- await stream.writeSSE({ data: '[DONE]' });
37
+ if (!controller.signal.aborted)
38
+ await stream.writeSSE({ data: '[DONE]' });
32
39
  }
33
40
  catch (error) {
34
- await stream.writeSSE({ data: JSON.stringify({ error: String(error) }) });
41
+ if (controller.signal.aborted)
42
+ return;
43
+ try {
44
+ await stream.writeSSE({ data: JSON.stringify(buildSseErrorEvent(error)) });
45
+ }
46
+ catch {
47
+ return;
48
+ }
35
49
  }
36
50
  });
37
51
  }
38
52
  try {
39
- const response = await server.handleJsonRpc(body);
53
+ const response = await server.handleJsonRpc(body, authToken);
54
+ if (response === null) {
55
+ return c.body(null, 204);
56
+ }
40
57
  return c.json(response);
41
58
  }
42
59
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"koa.d.ts","sourceRoot":"","sources":["../../src/adapters/koa.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,KAAK,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CA6DpD"}
1
+ {"version":3,"file":"koa.d.ts","sourceRoot":"","sources":["../../src/adapters/koa.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,KAAK,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAK9C,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAsDpD"}
@@ -1,5 +1,6 @@
1
1
  import { createErrorResponse } from '../json-rpc.js';
2
2
  import * as errors from '../errors.js';
3
+ import { isStreamRequest, pipeJsonRpcStream, SSE_HEADERS } from './shared.js';
3
4
  export function a2aKoa(server) {
4
5
  return async (ctx, next) => {
5
6
  if (ctx.path === '/.well-known/agent.json' && ctx.method === 'GET') {
@@ -9,44 +10,36 @@ export function a2aKoa(server) {
9
10
  }
10
11
  if (ctx.path === '/a2a' && ctx.method === 'POST') {
11
12
  const contentType = ctx.headers['content-type'];
12
- if (contentType && !contentType.includes('application/json')) {
13
+ if (contentType && !contentType.startsWith('application/json')) {
13
14
  ctx.body = createErrorResponse(null, errors.contentTypeNotSupported(contentType));
14
15
  return;
15
16
  }
16
17
  const body = ctx.request.body;
17
- if (!body) {
18
+ if (body === undefined) {
18
19
  ctx.body = createErrorResponse(null, errors.parseError('Request body not parsed. Ensure body-parsing middleware is applied.'));
19
20
  return;
20
21
  }
21
- const isStreaming = ctx.headers.accept?.includes('text/event-stream') || body.method === 'message/stream';
22
- if (isStreaming) {
22
+ const authToken = server.getAuthToken((name) => ctx.get(name) || undefined);
23
+ if (isStreamRequest(body)) {
23
24
  ctx.respond = false;
24
25
  const res = ctx.res;
25
- res.writeHead(200, {
26
- 'Content-Type': 'text/event-stream',
27
- 'Cache-Control': 'no-cache',
28
- Connection: 'keep-alive',
26
+ const controller = new AbortController();
27
+ res.on('close', () => controller.abort());
28
+ res.writeHead(200, SSE_HEADERS);
29
+ await pipeJsonRpcStream(server, body, authToken, controller.signal, (frame) => {
30
+ if (!res.writableEnded && !res.destroyed)
31
+ res.write(frame);
29
32
  });
30
- try {
31
- for await (const event of server.handleJsonRpcStream(body)) {
32
- if (res.writableEnded)
33
- break;
34
- res.write(`data: ${JSON.stringify(event)}\n\n`);
35
- }
36
- if (!res.writableEnded)
37
- res.write('data: [DONE]\n\n');
38
- }
39
- catch (error) {
40
- if (!res.writableEnded) {
41
- res.write(`data: ${JSON.stringify({ error: String(error) })}\n\n`);
42
- }
43
- }
44
33
  if (!res.writableEnded)
45
34
  res.end();
46
35
  return;
47
36
  }
48
37
  try {
49
- const response = await server.handleJsonRpc(body);
38
+ const response = await server.handleJsonRpc(body, authToken);
39
+ if (response === null) {
40
+ ctx.status = 204;
41
+ return;
42
+ }
50
43
  ctx.body = response;
51
44
  }
52
45
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"koa.js","sourceRoot":"","sources":["../../src/adapters/koa.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"koa.js","sourceRoot":"","sources":["../../src/adapters/koa.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS;WAExB,OAAO,CAAC,QAAQ,CAAC;kBAKV,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;EAsDlD"}
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAK9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS;WAExB,OAAO,CAAC,QAAQ,CAAC;kBAKV,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;EAsDlD"}
@@ -1,5 +1,6 @@
1
1
  import { createErrorResponse } from '../json-rpc.js';
2
2
  import * as errors from '../errors.js';
3
+ import { isStreamRequest, pipeJsonRpcStream, SSE_HEADERS } from './shared.js';
3
4
  export function a2aNext(server) {
4
5
  return {
5
6
  async GET() {
@@ -8,7 +9,7 @@ export function a2aNext(server) {
8
9
  },
9
10
  async POST(request) {
10
11
  const contentType = request.headers.get('content-type');
11
- if (contentType && !contentType.includes('application/json')) {
12
+ if (contentType && !contentType.startsWith('application/json')) {
12
13
  return Response.json(createErrorResponse(null, errors.contentTypeNotSupported(contentType)));
13
14
  }
14
15
  let body;
@@ -18,34 +19,36 @@ export function a2aNext(server) {
18
19
  catch {
19
20
  return Response.json(createErrorResponse(null, errors.parseError('Invalid JSON body')));
20
21
  }
21
- const isStreaming = request.headers.get('accept')?.includes('text/event-stream') ||
22
- body?.method === 'message/stream';
23
- if (isStreaming) {
22
+ const authToken = server.getAuthToken((name) => request.headers.get(name));
23
+ if (isStreamRequest(body)) {
24
24
  const encoder = new TextEncoder();
25
+ const controller = new AbortController();
26
+ const abort = () => controller.abort();
27
+ request.signal?.addEventListener('abort', abort, { once: true });
25
28
  const stream = new ReadableStream({
26
- async start(controller) {
29
+ async start(streamController) {
30
+ await pipeJsonRpcStream(server, body, authToken, controller.signal, (frame) => {
31
+ streamController.enqueue(encoder.encode(frame));
32
+ });
33
+ request.signal?.removeEventListener('abort', abort);
27
34
  try {
28
- for await (const event of server.handleJsonRpcStream(body)) {
29
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
30
- }
31
- controller.enqueue(encoder.encode('data: [DONE]\n\n'));
35
+ streamController.close();
32
36
  }
33
- catch (error) {
34
- controller.enqueue(encoder.encode(`data: ${JSON.stringify({ error: String(error) })}\n\n`));
37
+ catch {
38
+ return;
35
39
  }
36
- controller.close();
37
40
  },
38
- });
39
- return new Response(stream, {
40
- headers: {
41
- 'Content-Type': 'text/event-stream',
42
- 'Cache-Control': 'no-cache',
43
- Connection: 'keep-alive',
41
+ cancel() {
42
+ controller.abort();
44
43
  },
45
44
  });
45
+ return new Response(stream, { headers: SSE_HEADERS });
46
46
  }
47
47
  try {
48
- const response = await server.handleJsonRpc(body);
48
+ const response = await server.handleJsonRpc(body, authToken);
49
+ if (response === null) {
50
+ return new Response(null, { status: 204 });
51
+ }
49
52
  return Response.json(response);
50
53
  }
51
54
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { A2AServer } from '../server.js';
2
+ import type { A2AStreamEvent } from '../types.js';
3
+ export type HeaderValue = string | string[] | null | undefined;
4
+ /**
5
+ * A request streams only for the `message/stream` method; the Accept header
6
+ * alone does not switch a `message/send` request to SSE.
7
+ */
8
+ export declare function isStreamRequest(body: unknown): boolean;
9
+ export declare function firstHeader(value: HeaderValue): string | undefined;
10
+ export declare function sseFrame(data: A2AStreamEvent | '[DONE]'): string;
11
+ export declare const SSE_HEADERS: Record<string, string>;
12
+ /**
13
+ * Drive a JSON-RPC stream and write SSE frames until it ends, the client
14
+ * disconnects (signal aborted) or a write fails. Errors are reported as a
15
+ * final failed status-update frame.
16
+ */
17
+ export declare function pipeJsonRpcStream(server: A2AServer, body: unknown, authToken: string | undefined, signal: AbortSignal, write: (frame: string) => void | Promise<void>): Promise<void>;
18
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/adapters/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAOtD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAGlE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,QAAQ,GAAG,MAAM,CAEhE;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9C,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAC7C,OAAO,CAAC,IAAI,CAAC,CAef"}