agents 0.16.2 → 0.17.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 (122) hide show
  1. package/README.md +11 -8
  2. package/dist/{agent-tool-types-CTw3UJUP.d.ts → agent-tool-types-Cd1TZPfB.d.ts} +587 -137
  3. package/dist/agent-tool-types.d.ts +34 -18
  4. package/dist/agent-tool-types.js +20 -1
  5. package/dist/agent-tool-types.js.map +1 -0
  6. package/dist/agent-tools-BXlsuX0d.js +304 -0
  7. package/dist/agent-tools-BXlsuX0d.js.map +1 -0
  8. package/dist/agent-tools-_E8wxUIK.d.ts +119 -0
  9. package/dist/agent-tools.d.ts +24 -18
  10. package/dist/agent-tools.js.map +1 -1
  11. package/dist/ai-chat-agent.d.ts +1 -1
  12. package/dist/ai-chat-agent.js +1 -2
  13. package/dist/ai-chat-agent.js.map +1 -1
  14. package/dist/ai-chat-v5-migration.d.ts +1 -1
  15. package/dist/ai-chat-v5-migration.js +1 -2
  16. package/dist/ai-chat-v5-migration.js.map +1 -1
  17. package/dist/ai-react.d.ts +1 -1
  18. package/dist/ai-react.js +1 -2
  19. package/dist/ai-react.js.map +1 -1
  20. package/dist/ai-types.d.ts +1 -7
  21. package/dist/ai-types.js +2 -8
  22. package/dist/ai-types.js.map +1 -1
  23. package/dist/browser/ai.js +1 -1
  24. package/dist/browser/index.js +1 -1
  25. package/dist/chat/index.d.ts +1918 -72
  26. package/dist/chat/index.js +1730 -245
  27. package/dist/chat/index.js.map +1 -1
  28. package/dist/chat/react.d.ts +602 -0
  29. package/dist/chat/react.js +1506 -0
  30. package/dist/chat/react.js.map +1 -0
  31. package/dist/chat-sdk/index.d.ts +4 -4
  32. package/dist/{classPrivateFieldGet2-CZ7QjTXN.js → classPrivateFieldGet2-DZBYAB34.js} +5 -5
  33. package/dist/{classPrivateMethodInitSpec-D-0__zd9.js → classPrivateMethodInitSpec-qMjJ6sHQ.js} +2 -2
  34. package/dist/{client-BXJ9n2f7.js → client-BZ-B3NhC.js} +2 -2
  35. package/dist/client-BZ-B3NhC.js.map +1 -0
  36. package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
  37. package/dist/client.d.ts +76 -57
  38. package/dist/client.js +33 -5
  39. package/dist/client.js.map +1 -1
  40. package/dist/{connector-CrKhowfD.js → connector-CdldGF3h.js} +5 -5
  41. package/dist/{connector-CrKhowfD.js.map → connector-CdldGF3h.js.map} +1 -1
  42. package/dist/email.js +1 -1
  43. package/dist/email.js.map +1 -1
  44. package/dist/{index-B7IbEeze.d.ts → index-CcbnKkNh.d.ts} +188 -14
  45. package/dist/index.d.ts +91 -71
  46. package/dist/index.js +562 -24
  47. package/dist/index.js.map +1 -1
  48. package/dist/mcp/client.d.ts +18 -14
  49. package/dist/mcp/client.js +1 -1
  50. package/dist/mcp/index.d.ts +30 -30
  51. package/dist/mcp/index.js +7 -7
  52. package/dist/mcp/index.js.map +1 -1
  53. package/dist/{agent-tools-3zLG7MgA.js → message-builder-BymO4N_D.js} +45 -126
  54. package/dist/message-builder-BymO4N_D.js.map +1 -0
  55. package/dist/observability/index.d.ts +1 -1
  56. package/dist/observability/index.js +4 -2
  57. package/dist/observability/index.js.map +1 -1
  58. package/dist/react.d.ts +123 -110
  59. package/dist/react.js +44 -11
  60. package/dist/react.js.map +1 -1
  61. package/dist/serializable.d.ts +1 -1
  62. package/dist/skills/compile.js +1 -1
  63. package/dist/skills/compile.js.map +1 -1
  64. package/dist/skills/index.js +5 -5
  65. package/dist/skills/index.js.map +1 -1
  66. package/dist/sub-routing.d.ts +6 -6
  67. package/dist/utils.js +1 -1
  68. package/dist/utils.js.map +1 -1
  69. package/dist/vite.js +5 -5
  70. package/dist/vite.js.map +1 -1
  71. package/dist/wire-types-nflOzNuU.js +240 -0
  72. package/dist/wire-types-nflOzNuU.js.map +1 -0
  73. package/dist/{workflow-types-SrZK_o9p.d.ts → workflow-types-Baz_PO5v.d.ts} +42 -22
  74. package/dist/workflow-types.d.ts +25 -21
  75. package/dist/workflow-types.js.map +1 -1
  76. package/dist/workflows.d.ts +22 -21
  77. package/dist/workflows.js +31 -7
  78. package/dist/workflows.js.map +1 -1
  79. package/docs/adding-to-existing-project.md +450 -0
  80. package/docs/agent-class.md +503 -0
  81. package/docs/agent-tools.md +552 -0
  82. package/docs/browse-the-web.md +430 -0
  83. package/docs/callable-methods.md +627 -0
  84. package/docs/chat-agents.md +1687 -0
  85. package/docs/chat-sdk.md +181 -0
  86. package/docs/client-sdk.md +520 -0
  87. package/docs/client-tools-continuation.md +177 -0
  88. package/docs/codemode.md +440 -0
  89. package/docs/configuration.md +775 -0
  90. package/docs/cross-domain-authentication.md +171 -0
  91. package/docs/durable-execution.md +537 -0
  92. package/docs/email.md +663 -0
  93. package/docs/get-current-agent.md +204 -0
  94. package/docs/getting-started.md +305 -0
  95. package/docs/http-websockets.md +668 -0
  96. package/docs/human-in-the-loop.md +661 -0
  97. package/docs/index.md +151 -0
  98. package/docs/long-running-agents.md +730 -0
  99. package/docs/mcp-client.md +620 -0
  100. package/docs/mcp-servers.md +526 -0
  101. package/docs/mcp-transports.md +308 -0
  102. package/docs/migration-to-ai-sdk-v5.md +96 -0
  103. package/docs/migration-to-ai-sdk-v6.md +163 -0
  104. package/docs/observability.md +261 -0
  105. package/docs/push-notifications.md +367 -0
  106. package/docs/queue.md +329 -0
  107. package/docs/readonly-connections.md +278 -0
  108. package/docs/resumable-streaming.md +127 -0
  109. package/docs/retries.md +444 -0
  110. package/docs/routing.md +749 -0
  111. package/docs/scheduling.md +898 -0
  112. package/docs/securing-mcp-servers.md +359 -0
  113. package/docs/server-driven-messages.md +477 -0
  114. package/docs/sessions.md +1024 -0
  115. package/docs/state.md +512 -0
  116. package/docs/sub-agents.md +389 -0
  117. package/docs/webhooks.md +604 -0
  118. package/docs/workflows.md +877 -0
  119. package/package.json +41 -14
  120. package/dist/agent-tools-3zLG7MgA.js.map +0 -1
  121. package/dist/agent-tools-DZhI5F6Q.d.ts +0 -14
  122. package/dist/client-BXJ9n2f7.js.map +0 -1
@@ -205,8 +205,12 @@ function applyChunkToParts(parts, chunk) {
205
205
  const toolPart = findToolPartByCallId(parts, chunk.toolCallId);
206
206
  if (toolPart) {
207
207
  const p = toolPart;
208
+ if (p.state === "approval-responded" || p.state === "output-available" || p.state === "output-error" || p.state === "output-denied") return true;
208
209
  p.state = "approval-requested";
209
- p.approval = { id: chunk.approvalId };
210
+ p.approval = {
211
+ id: chunk.approvalId,
212
+ ...chunk.approvalDescriptor !== void 0 && { descriptor: chunk.approvalDescriptor }
213
+ };
210
214
  }
211
215
  return true;
212
216
  }
@@ -214,6 +218,7 @@ function applyChunkToParts(parts, chunk) {
214
218
  const toolPart = findToolPartByCallId(parts, chunk.toolCallId);
215
219
  if (toolPart) {
216
220
  const p = toolPart;
221
+ if (p.state === "output-available" || p.state === "output-error" || p.state === "output-denied" || p.state === "approval-responded") return true;
217
222
  p.state = "output-denied";
218
223
  }
219
224
  return true;
@@ -283,8 +288,29 @@ function applyChunkToParts(parts, chunk) {
283
288
  * - `tool-input-available` for a `toolCallId` whose existing part is no
284
289
  * longer `input-streaming` (i.e. has already advanced to `input-available`
285
290
  * or any terminal state).
291
+ * - `tool-output-denied` for a `toolCallId` whose existing part is already
292
+ * settled (`output-available` / `output-error` / `output-denied`) or
293
+ * user-approved (`approval-responded`). A continuation that re-validates
294
+ * the transcript can re-emit a denial for an approval the SDK now deems
295
+ * unneeded; `applyChunkToParts` already drops it server-side, and this stops
296
+ * it reaching the client (where the in-place `updateToolPart` would flip the
297
+ * part to `output-denied`) and the replay buffer. Mirrors the
298
+ * first-write-wins guard in `applyChunkToParts`.
299
+ * - `tool-approval-request` for a `toolCallId` whose existing part is already
300
+ * `approval-responded` or settled. A continuation replaying a prior tool
301
+ * round-trip can re-emit the approval request; left unfiltered it would
302
+ * revert an already-approved tool back to `approval-requested` on the client
303
+ * (re-showing Approve/Reject) and replay that regression on reconnect. Same
304
+ * pattern and rationale as `tool-output-denied`.
286
305
  */
287
306
  function isReplayChunk(parts, chunk) {
307
+ if (chunk.type === "tool-output-denied" || chunk.type === "tool-approval-request") {
308
+ if (!chunk.toolCallId) return false;
309
+ const existing = findToolPartByCallId(parts, chunk.toolCallId);
310
+ if (!existing) return false;
311
+ const state = existing.state;
312
+ return state === "output-available" || state === "output-error" || state === "output-denied" || state === "approval-responded";
313
+ }
288
314
  if (chunk.type !== "tool-input-start" && chunk.type !== "tool-input-delta" && chunk.type !== "tool-input-available") return false;
289
315
  if (!chunk.toolCallId) return false;
290
316
  const existing = findToolPartByCallId(parts, chunk.toolCallId);
@@ -335,133 +361,26 @@ function findDataPartByTypeAndId(parts, type, id) {
335
361
  if (p.type === type && "id" in p && p.id === id) return p;
336
362
  }
337
363
  }
338
- //#endregion
339
- //#region src/chat/agent-tools.ts
340
- function sortRuns(runs) {
341
- return [...runs].sort((a, b) => {
342
- if (a.order !== b.order) return a.order - b.order;
343
- return a.runId.localeCompare(b.runId);
344
- });
345
- }
346
- function rebuildIndexes(runsById) {
347
- const grouped = {};
348
- const unboundRuns = [];
349
- for (const run of Object.values(runsById)) if (run.parentToolCallId) {
350
- grouped[run.parentToolCallId] = grouped[run.parentToolCallId] ?? [];
351
- grouped[run.parentToolCallId].push(run);
352
- } else unboundRuns.push(run);
353
- for (const [toolCallId, runs] of Object.entries(grouped)) grouped[toolCallId] = sortRuns(runs);
354
- return {
355
- runsByToolCallId: grouped,
356
- unboundRuns: sortRuns(unboundRuns)
357
- };
358
- }
359
- function emptyRun(message) {
360
- const { event } = message;
361
- if (event.kind === "started") return {
362
- runId: event.runId,
363
- agentType: event.agentType,
364
- parentToolCallId: message.parentToolCallId,
365
- inputPreview: event.inputPreview,
366
- order: event.order,
367
- display: event.display,
368
- status: "running",
369
- parts: [],
370
- subAgent: {
371
- agent: event.agentType,
372
- name: event.runId
373
- }
374
- };
375
- }
376
- function applyToRun(prev, message) {
377
- const seeded = prev ?? emptyRun(message);
378
- const { event } = message;
379
- switch (event.kind) {
380
- case "started":
381
- if (seeded?.status === "completed" || seeded?.status === "error" || seeded?.status === "aborted" || seeded?.status === "interrupted") return seeded;
382
- return {
383
- ...seeded,
384
- runId: event.runId,
385
- agentType: event.agentType,
386
- parentToolCallId: message.parentToolCallId,
387
- inputPreview: event.inputPreview,
388
- order: event.order,
389
- display: event.display,
390
- status: "running",
391
- parts: seeded?.parts ?? [],
392
- subAgent: {
393
- agent: event.agentType,
394
- name: event.runId
395
- }
396
- };
397
- case "chunk": {
398
- if (!seeded) return void 0;
399
- const parts = [...seeded.parts];
400
- try {
401
- applyChunkToParts(parts, JSON.parse(event.body));
402
- } catch {
403
- return seeded;
404
- }
405
- return {
406
- ...seeded,
407
- parts
408
- };
409
- }
410
- case "finished":
411
- if (!seeded) return void 0;
412
- return {
413
- ...seeded,
414
- status: "completed",
415
- summary: event.summary,
416
- error: void 0
417
- };
418
- case "error":
419
- if (!seeded) return void 0;
420
- return {
421
- ...seeded,
422
- status: "error",
423
- error: event.error
424
- };
425
- case "aborted":
426
- if (!seeded) return void 0;
427
- return {
428
- ...seeded,
429
- status: "aborted",
430
- error: event.reason
431
- };
432
- case "interrupted":
433
- if (!seeded) return void 0;
434
- return {
435
- ...seeded,
436
- status: "interrupted",
437
- error: event.error,
438
- reason: event.reason,
439
- childStillRunning: event.childStillRunning
440
- };
441
- }
442
- }
443
- function createAgentToolEventState() {
444
- return {
445
- runsById: {},
446
- runsByToolCallId: {},
447
- unboundRuns: []
448
- };
449
- }
450
- function applyAgentToolEvent(state, message) {
451
- if (message.type !== "agent-tool-event") return state;
452
- const runId = message.event.runId;
453
- const nextRun = applyToRun(state.runsById[runId], message);
454
- if (!nextRun) return state;
455
- const runsById = {
456
- ...state.runsById,
457
- [runId]: nextRun
458
- };
364
+ /**
365
+ * Rebuild the partial text + parts of an interrupted assistant turn from its
366
+ * stored resumable-stream chunks. Replays each chunk body through
367
+ * {@link applyChunkToParts}; malformed bodies are skipped. Shared by
368
+ * `AIChatAgent` and `Think`, which read the chunks from their
369
+ * `ResumableStream` (`getStreamChunks`) and pass them in.
370
+ *
371
+ * `@internal`
372
+ */
373
+ function getPartialStreamText(chunks) {
374
+ const parts = [];
375
+ for (const chunk of chunks) try {
376
+ applyChunkToParts(parts, JSON.parse(chunk.body));
377
+ } catch {}
459
378
  return {
460
- runsById,
461
- ...rebuildIndexes(runsById)
379
+ text: parts.filter((p) => p.type === "text" && "text" in p).map((p) => p.text).join(""),
380
+ parts
462
381
  };
463
382
  }
464
383
  //#endregion
465
- export { normalizeToolInput as a, isReplayChunk as i, createAgentToolEventState as n, applyChunkToParts as r, applyAgentToolEvent as t };
384
+ export { normalizeToolInput as i, getPartialStreamText as n, isReplayChunk as r, applyChunkToParts as t };
466
385
 
467
- //# sourceMappingURL=agent-tools-3zLG7MgA.js.map
386
+ //# sourceMappingURL=message-builder-BymO4N_D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-builder-BymO4N_D.js","names":[],"sources":["../src/chat/message-builder.ts"],"sourcesContent":["/**\n * Shared message builder for reconstructing UIMessage parts from stream chunks.\n *\n * Used by both @cloudflare/ai-chat (server + client) and @cloudflare/think\n * to avoid duplicating the chunk-type switch/case logic.\n *\n * Operates on a mutable parts array for performance (avoids allocating new arrays\n * on every chunk during streaming).\n */\n\nimport type { UIMessage } from \"ai\";\n\n/** The parts array type from UIMessage */\nexport type MessageParts = UIMessage[\"parts\"];\n\n/** A single part from the UIMessage parts array */\nexport type MessagePart = MessageParts[number];\n\n/**\n * Parsed chunk data from an AI SDK stream event.\n * This is the JSON-parsed body of a CF_AGENT_USE_CHAT_RESPONSE message,\n * or the `data:` payload of an SSE line.\n */\nexport type StreamChunkData = {\n type: string;\n id?: string;\n delta?: string;\n text?: string;\n mediaType?: string;\n url?: string;\n sourceId?: string;\n title?: string;\n filename?: string;\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n inputTextDelta?: string;\n output?: unknown;\n state?: string;\n errorText?: string;\n /** When true, the output is preliminary (may be updated by a later chunk) */\n preliminary?: boolean;\n /** Approval ID for tools with needsApproval */\n approvalId?: string;\n /** Optional framework-specific metadata for the approval request. */\n approvalDescriptor?: unknown;\n providerMetadata?: Record<string, unknown>;\n /** Whether the tool was executed by the provider (e.g. Gemini code execution) */\n providerExecuted?: boolean;\n /** Payload for data-* parts (developer-defined typed JSON) */\n data?: unknown;\n /** When true, data parts are ephemeral and not persisted to message.parts */\n transient?: boolean;\n /** Message ID assigned by the server at stream start */\n messageId?: string;\n /** Per-message metadata attached by start/finish/message-metadata chunks */\n messageMetadata?: unknown;\n [key: string]: unknown;\n};\n\n/** Whether a value is a plain (non-array, non-null) object. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Coerce a tool part's `input` into a provider-acceptable object.\n *\n * The Anthropic Messages API requires `tool_use.input` to be a JSON **object** —\n * `null`, `undefined`, `\"\"`, a raw string, **or an array** are all rejected with\n * `tool_use.input: Input should be an object` (verified empirically against the\n * live API: `{}` → 200, but `\"\"`, `[]`, and `[{...}]` all → 400). A streamed\n * tool call that finishes with no `input_json_delta` events (the model called\n * the tool with no args), or whose input surfaces as a stringified JSON blob,\n * can persist one of these shapes — and because it lives in durable storage, the\n * session is then wedged across reconnects, redeploys, and DO evictions.\n * Enforcing the invariant at the write boundary (and as a read-side repair\n * backstop) keeps the transcript valid.\n *\n * - A plain (non-array) object is returned untouched (`changed: false`).\n * - A string that parses to a plain object is parsed.\n * - Everything else (`null`, `undefined`, `\"\"`, arrays, primitives, non-object\n * or unparseable JSON) collapses to `{}`.\n */\nexport function normalizeToolInput(raw: unknown): {\n input: unknown;\n changed: boolean;\n} {\n if (isPlainObject(raw)) return { input: raw, changed: false };\n if (typeof raw === \"string\" && raw.trim().startsWith(\"{\")) {\n try {\n const parsed: unknown = JSON.parse(raw);\n if (isPlainObject(parsed)) return { input: parsed, changed: true };\n } catch {\n // Unparseable / partial JSON — fall through to the empty-object default.\n }\n }\n return { input: {}, changed: true };\n}\n\n/**\n * Applies a stream chunk to a mutable parts array, building up the message\n * incrementally. Returns true if the chunk was handled, false if it was\n * an unrecognized type (caller may handle it with additional logic).\n *\n * Handles all common chunk types that both server and client need:\n * - text-start / text-delta / text-end\n * - reasoning-start / reasoning-delta / reasoning-end\n * - file\n * - source-url / source-document\n * - tool-input-start / tool-input-delta / tool-input-available / tool-input-error\n * - tool-output-available / tool-output-error\n * - step-start (aliased from start-step)\n * - data-* (developer-defined typed JSON blobs)\n *\n * @param parts - The mutable parts array to update\n * @param chunk - The parsed stream chunk data\n * @returns true if handled, false if the chunk type is not recognized\n */\nexport function applyChunkToParts(\n parts: MessagePart[],\n chunk: StreamChunkData\n): boolean {\n switch (chunk.type) {\n case \"text-start\": {\n parts.push({\n type: \"text\",\n text: \"\",\n state: \"streaming\"\n } as MessagePart);\n return true;\n }\n\n case \"text-delta\": {\n const lastTextPart = findLastPartByType(parts, \"text\");\n if (lastTextPart && lastTextPart.type === \"text\") {\n (lastTextPart as { text: string }).text += chunk.delta ?? \"\";\n } else {\n // No text-start received — create a new text part (stream resumption fallback)\n parts.push({\n type: \"text\",\n text: chunk.delta ?? \"\",\n state: \"streaming\"\n } as MessagePart);\n }\n return true;\n }\n\n case \"text-end\": {\n const lastTextPart = findLastPartByType(parts, \"text\");\n if (lastTextPart && \"state\" in lastTextPart) {\n (lastTextPart as { state: string }).state = \"done\";\n }\n return true;\n }\n\n case \"reasoning-start\": {\n parts.push({\n type: \"reasoning\",\n text: \"\",\n state: \"streaming\"\n } as MessagePart);\n return true;\n }\n\n case \"reasoning-delta\": {\n const lastReasoningPart = findLastPartByType(parts, \"reasoning\");\n if (lastReasoningPart && lastReasoningPart.type === \"reasoning\") {\n (lastReasoningPart as { text: string }).text += chunk.delta ?? \"\";\n mergeProviderMetadata(lastReasoningPart, chunk.providerMetadata);\n } else {\n // No reasoning-start received — create a new reasoning part (stream resumption fallback)\n parts.push({\n type: \"reasoning\",\n text: chunk.delta ?? \"\",\n state: \"streaming\",\n ...(chunk.providerMetadata != null\n ? { providerMetadata: chunk.providerMetadata }\n : {})\n } as MessagePart);\n }\n return true;\n }\n\n case \"reasoning-end\": {\n const lastReasoningPart = findLastPartByType(parts, \"reasoning\");\n if (lastReasoningPart && \"state\" in lastReasoningPart) {\n (lastReasoningPart as { state: string }).state = \"done\";\n mergeProviderMetadata(lastReasoningPart, chunk.providerMetadata);\n }\n return true;\n }\n\n case \"file\": {\n parts.push({\n type: \"file\",\n mediaType: chunk.mediaType,\n url: chunk.url\n } as MessagePart);\n return true;\n }\n\n case \"source-url\": {\n parts.push({\n type: \"source-url\",\n sourceId: chunk.sourceId,\n url: chunk.url,\n title: chunk.title,\n providerMetadata: chunk.providerMetadata\n } as MessagePart);\n return true;\n }\n\n case \"source-document\": {\n parts.push({\n type: \"source-document\",\n sourceId: chunk.sourceId,\n mediaType: chunk.mediaType,\n title: chunk.title,\n filename: chunk.filename,\n providerMetadata: chunk.providerMetadata\n } as MessagePart);\n return true;\n }\n\n case \"tool-input-start\": {\n // Idempotent against an existing tool part with the same toolCallId.\n // Some providers (notably the OpenAI Responses API) replay prior\n // tool calls in continuation streams as a fresh `tool-input-start`\n // → `tool-input-delta` → `tool-input-available` →\n // `tool-output-available` sequence carrying the original toolCallId\n // and original output. Without this guard a replay would push a\n // duplicate part into the streaming message *and* clobber the\n // original part's state when the AI SDK's mutate-in-place\n // `updateToolPart` processes the replay on the client (issue #1404).\n // A model that genuinely wants a fresh tool call always emits a\n // new toolCallId, so an existing match is never a legitimate\n // \"start over\".\n const existing = findToolPartByCallId(parts, chunk.toolCallId);\n if (existing) {\n return true;\n }\n parts.push({\n type: `tool-${chunk.toolName}`,\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n state: \"input-streaming\",\n input: undefined,\n ...(chunk.providerExecuted != null\n ? { providerExecuted: chunk.providerExecuted }\n : {}),\n ...(chunk.providerMetadata != null\n ? { callProviderMetadata: chunk.providerMetadata }\n : {}),\n ...(chunk.title != null ? { title: chunk.title } : {})\n } as MessagePart);\n return true;\n }\n\n case \"tool-input-delta\": {\n // Only mutate input while the tool is still actively input-streaming.\n // Deltas arriving after the tool has already advanced (input-available\n // or any terminal state) are provider replay and must not regress\n // a fully-formed input back to a partial one.\n const toolPart = findToolPartByCallId(parts, chunk.toolCallId);\n if (\n toolPart &&\n (toolPart as Record<string, unknown>).state === \"input-streaming\"\n ) {\n (toolPart as Record<string, unknown>).input = chunk.input;\n }\n return true;\n }\n\n case \"tool-input-available\": {\n const existing = findToolPartByCallId(parts, chunk.toolCallId);\n if (existing) {\n const p = existing as Record<string, unknown>;\n // Only advance from the streaming-input phase. Once the tool is\n // already at input-available or any terminal state\n // (output-available, output-error, output-denied,\n // approval-requested, approval-responded), this chunk is a\n // provider replay and must not regress state or overwrite a\n // resolved input/output. See the comment on tool-input-start.\n if (p.state === \"input-streaming\") {\n p.state = \"input-available\";\n p.input = normalizeToolInput(chunk.input).input;\n if (chunk.providerExecuted != null) {\n p.providerExecuted = chunk.providerExecuted;\n }\n if (chunk.providerMetadata != null) {\n p.callProviderMetadata = chunk.providerMetadata;\n }\n if (chunk.title != null) {\n p.title = chunk.title;\n }\n }\n return true;\n }\n parts.push({\n type: `tool-${chunk.toolName}`,\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n state: \"input-available\",\n input: normalizeToolInput(chunk.input).input,\n ...(chunk.providerExecuted != null\n ? { providerExecuted: chunk.providerExecuted }\n : {}),\n ...(chunk.providerMetadata != null\n ? { callProviderMetadata: chunk.providerMetadata }\n : {}),\n ...(chunk.title != null ? { title: chunk.title } : {})\n } as MessagePart);\n return true;\n }\n\n case \"tool-input-error\": {\n const existing = findToolPartByCallId(parts, chunk.toolCallId);\n if (existing) {\n const p = existing as Record<string, unknown>;\n // First-write-wins: a tool that's already terminal must not be\n // regressed (or re-decided as an error) by a later chunk. A\n // tool-input-error here is either provider replay or a confused\n // upstream — preserve the existing terminal state.\n if (\n p.state === \"output-available\" ||\n p.state === \"output-error\" ||\n p.state === \"output-denied\"\n ) {\n return true;\n }\n p.state = \"output-error\";\n p.errorText = chunk.errorText;\n p.input = normalizeToolInput(chunk.input).input;\n if (chunk.providerExecuted != null) {\n p.providerExecuted = chunk.providerExecuted;\n }\n if (chunk.providerMetadata != null) {\n p.callProviderMetadata = chunk.providerMetadata;\n }\n } else {\n parts.push({\n type: `tool-${chunk.toolName}`,\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n state: \"output-error\",\n input: normalizeToolInput(chunk.input).input,\n errorText: chunk.errorText,\n ...(chunk.providerExecuted != null\n ? { providerExecuted: chunk.providerExecuted }\n : {}),\n ...(chunk.providerMetadata != null\n ? { callProviderMetadata: chunk.providerMetadata }\n : {})\n } as MessagePart);\n }\n return true;\n }\n\n case \"tool-approval-request\": {\n const toolPart = findToolPartByCallId(parts, chunk.toolCallId);\n if (toolPart) {\n const p = toolPart as Record<string, unknown>;\n // First-write-wins: a continuation can replay the prior tool\n // round-trip and re-emit `tool-approval-request`. Never regress a part\n // the user has already responded to (`approval-responded`) or that has\n // otherwise settled — that would silently discard the approval\n // decision. Matches the guards on the `tool-input-*` /\n // `tool-output-denied` handlers.\n if (\n p.state === \"approval-responded\" ||\n p.state === \"output-available\" ||\n p.state === \"output-error\" ||\n p.state === \"output-denied\"\n ) {\n return true;\n }\n p.state = \"approval-requested\";\n p.approval = {\n id: chunk.approvalId,\n ...(chunk.approvalDescriptor !== undefined && {\n descriptor: chunk.approvalDescriptor\n })\n };\n }\n return true;\n }\n\n case \"tool-output-denied\": {\n const toolPart = findToolPartByCallId(parts, chunk.toolCallId);\n if (toolPart) {\n const p = toolPart as Record<string, unknown>;\n // First-write-wins: a tool that's already terminal must not be\n // regressed by a later/replayed chunk. Also preserve an\n // `approval-responded` (user-approved) part — a continuation that\n // re-validates the transcript can emit `tool-output-denied` for an\n // approval the SDK deems unneeded (e.g. a tool without\n // `needsApproval`); that must not silently flip a granted approval\n // into a denial.\n if (\n p.state === \"output-available\" ||\n p.state === \"output-error\" ||\n p.state === \"output-denied\" ||\n p.state === \"approval-responded\"\n ) {\n return true;\n }\n p.state = \"output-denied\";\n }\n return true;\n }\n\n case \"tool-output-available\": {\n const toolPart = findToolPartByCallId(parts, chunk.toolCallId);\n if (toolPart) {\n const p = toolPart as Record<string, unknown>;\n p.state = \"output-available\";\n p.output = chunk.output;\n if (chunk.preliminary !== undefined) {\n p.preliminary = chunk.preliminary;\n }\n }\n return true;\n }\n\n case \"tool-output-error\": {\n const toolPart = findToolPartByCallId(parts, chunk.toolCallId);\n if (toolPart) {\n const p = toolPart as Record<string, unknown>;\n p.state = \"output-error\";\n p.errorText = chunk.errorText;\n }\n return true;\n }\n\n // Both \"step-start\" (client convention) and \"start-step\" (server convention)\n case \"step-start\":\n case \"start-step\": {\n parts.push({ type: \"step-start\" } as MessagePart);\n return true;\n }\n\n default: {\n // https://ai-sdk.dev/docs/ai-sdk-ui/streaming-data\n if (chunk.type.startsWith(\"data-\")) {\n // Transient parts are ephemeral — the AI SDK client fires an onData\n // callback instead of adding them to message.parts. On the server we\n // still broadcast them (so connected clients see them in real time)\n // but skip persisting them into the stored message parts.\n if (chunk.transient) {\n return true;\n }\n\n // Reconciliation: if a part with the same type AND id already exists,\n // update its data in-place instead of appending a duplicate.\n if (chunk.id != null) {\n const existing = findDataPartByTypeAndId(parts, chunk.type, chunk.id);\n if (existing) {\n (existing as Record<string, unknown>).data = chunk.data;\n return true;\n }\n }\n\n // Append new data parts to the array directly.\n // Note: `chunk.data` should always be provided — if omitted, the\n // persisted part will have `data: undefined` which JSON.stringify\n // drops, so the part will have no `data` field on reload.\n // The cast is needed because UIMessage[\"parts\"] doesn't include\n // data-* types in its union because they're an open extension point.\n parts.push({\n type: chunk.type,\n ...(chunk.id != null && { id: chunk.id }),\n data: chunk.data\n } as MessagePart);\n return true;\n }\n\n return false;\n }\n }\n}\n\n/**\n * Returns true if `chunk` would be a no-op replay against the already-known\n * `parts` — i.e. some upstream is re-emitting events for a tool call that\n * the message has already advanced past.\n *\n * Used by stream broadcasters to suppress re-broadcasting these chunks to\n * connected clients. AI SDK v6's `updateToolPart` mutates an existing tool\n * part in place when a chunk arrives with a matching `toolCallId`, so a\n * replayed `tool-input-start` would clobber an `output-available` part back\n * to `input-streaming` on the client (issue #1404).\n *\n * Only returns true when re-broadcasting would *visibly regress* state on\n * a v6 client. Safe-by-construction chunk types (e.g. `tool-output-available`\n * carrying the same output the part already has) return false.\n *\n * Conditions:\n * - `tool-input-start` for a `toolCallId` that already exists in `parts`.\n * - `tool-input-delta` for a `toolCallId` whose existing part is no longer\n * `input-streaming`.\n * - `tool-input-available` for a `toolCallId` whose existing part is no\n * longer `input-streaming` (i.e. has already advanced to `input-available`\n * or any terminal state).\n * - `tool-output-denied` for a `toolCallId` whose existing part is already\n * settled (`output-available` / `output-error` / `output-denied`) or\n * user-approved (`approval-responded`). A continuation that re-validates\n * the transcript can re-emit a denial for an approval the SDK now deems\n * unneeded; `applyChunkToParts` already drops it server-side, and this stops\n * it reaching the client (where the in-place `updateToolPart` would flip the\n * part to `output-denied`) and the replay buffer. Mirrors the\n * first-write-wins guard in `applyChunkToParts`.\n * - `tool-approval-request` for a `toolCallId` whose existing part is already\n * `approval-responded` or settled. A continuation replaying a prior tool\n * round-trip can re-emit the approval request; left unfiltered it would\n * revert an already-approved tool back to `approval-requested` on the client\n * (re-showing Approve/Reject) and replay that regression on reconnect. Same\n * pattern and rationale as `tool-output-denied`.\n */\nexport function isReplayChunk(\n parts: MessagePart[],\n chunk: StreamChunkData\n): boolean {\n if (\n chunk.type === \"tool-output-denied\" ||\n chunk.type === \"tool-approval-request\"\n ) {\n if (!chunk.toolCallId) return false;\n const existing = findToolPartByCallId(parts, chunk.toolCallId);\n if (!existing) return false;\n const state = (existing as Record<string, unknown>).state;\n return (\n state === \"output-available\" ||\n state === \"output-error\" ||\n state === \"output-denied\" ||\n state === \"approval-responded\"\n );\n }\n\n if (\n chunk.type !== \"tool-input-start\" &&\n chunk.type !== \"tool-input-delta\" &&\n chunk.type !== \"tool-input-available\"\n ) {\n return false;\n }\n if (!chunk.toolCallId) return false;\n const existing = findToolPartByCallId(parts, chunk.toolCallId);\n if (!existing) return false;\n if (chunk.type === \"tool-input-start\") return true;\n const state = (existing as Record<string, unknown>).state;\n return state !== \"input-streaming\";\n}\n\n/**\n * Finds the last part in the array matching the given type.\n * Searches from the end for efficiency (the part we want is usually recent).\n */\nfunction findLastPartByType(\n parts: MessagePart[],\n type: string\n): MessagePart | undefined {\n for (let i = parts.length - 1; i >= 0; i--) {\n if (parts[i].type === type) {\n return parts[i];\n }\n }\n return undefined;\n}\n\n/**\n * Finds a tool part by its toolCallId.\n * Searches from the end since the tool part is usually recent.\n */\nfunction findToolPartByCallId(\n parts: MessagePart[],\n toolCallId: string | undefined\n): MessagePart | undefined {\n if (!toolCallId) return undefined;\n for (let i = parts.length - 1; i >= 0; i--) {\n const p = parts[i];\n if (\"toolCallId\" in p && p.toolCallId === toolCallId) {\n return p;\n }\n }\n return undefined;\n}\n\n/**\n * Shallow-merges providerMetadata from a chunk onto an existing part.\n * Preserves any metadata already on the part (e.g. from earlier deltas)\n * while adding new keys from the chunk. This is critical for providers\n * like Anthropic that emit the thinking block signature on reasoning-end.\n */\nfunction mergeProviderMetadata(\n part: MessagePart,\n metadata: Record<string, unknown> | undefined\n): void {\n if (metadata == null) return;\n const p = part as Record<string, unknown>;\n p.providerMetadata = {\n ...(p.providerMetadata as Record<string, unknown> | undefined),\n ...metadata\n };\n}\n\n/**\n * Finds a data part by its type and id for reconciliation.\n * Data parts use type+id as a composite key so when the same combination\n * is seen again, the existing part's data is updated in-place.\n */\nfunction findDataPartByTypeAndId(\n parts: MessagePart[],\n type: string,\n id: string\n): MessagePart | undefined {\n for (let i = parts.length - 1; i >= 0; i--) {\n const p = parts[i];\n if (p.type === type && \"id\" in p && (p as { id: string }).id === id) {\n return p;\n }\n }\n return undefined;\n}\n\n/**\n * Rebuild the partial text + parts of an interrupted assistant turn from its\n * stored resumable-stream chunks. Replays each chunk body through\n * {@link applyChunkToParts}; malformed bodies are skipped. Shared by\n * `AIChatAgent` and `Think`, which read the chunks from their\n * `ResumableStream` (`getStreamChunks`) and pass them in.\n *\n * `@internal`\n */\nexport function getPartialStreamText(chunks: ReadonlyArray<{ body: string }>): {\n text: string;\n parts: MessagePart[];\n} {\n const parts: MessagePart[] = [];\n\n for (const chunk of chunks) {\n try {\n const data = JSON.parse(chunk.body);\n applyChunkToParts(parts, data);\n } catch {\n // Skip malformed chunk bodies\n }\n }\n\n const text = parts\n .filter(\n (p): p is MessagePart & { type: \"text\"; text: string } =>\n p.type === \"text\" && \"text\" in p\n )\n .map((p) => p.text)\n .join(\"\");\n\n return { text, parts };\n}\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ import {
5
5
  o as subscribe,
6
6
  r as ObservabilityEvent,
7
7
  t as ChannelEventMap
8
- } from "../index-B7IbEeze.js";
8
+ } from "../index-CcbnKkNh.js";
9
9
  export {
10
10
  ChannelEventMap,
11
11
  Observability,
@@ -27,7 +27,8 @@ const channels = {
27
27
  lifecycle: channel("agents:lifecycle"),
28
28
  workflow: channel("agents:workflow"),
29
29
  mcp: channel("agents:mcp"),
30
- email: channel("agents:email")
30
+ email: channel("agents:email"),
31
+ channel: channel("agents:channel")
31
32
  };
32
33
  /**
33
34
  * Channel keys whose diagnostics channel name differs from `agents:${key}`.
@@ -46,10 +47,11 @@ function getChannel(type) {
46
47
  if (type.startsWith("chat:")) return channels.chat;
47
48
  if (type.startsWith("agent_tool:")) return channels.agentTool;
48
49
  if (type.startsWith("schedule:") || type.startsWith("queue:")) return channels.schedule;
49
- if (type.startsWith("message:") || type.startsWith("tool:") || type.startsWith("submission:")) return channels.message;
50
+ if (type.startsWith("message:") || type.startsWith("tool:") || type.startsWith("submission:") || type.startsWith("action:")) return channels.message;
50
51
  if (type === "rpc" || type.startsWith("rpc:")) return channels.rpc;
51
52
  if (type.startsWith("state:")) return channels.state;
52
53
  if (type.startsWith("email:")) return channels.email;
54
+ if (type.startsWith("channel:") || type.startsWith("notice:")) return channels.channel;
53
55
  return channels.lifecycle;
54
56
  }
55
57
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["dcUnsubscribe"],"sources":["../../src/observability/index.ts"],"sourcesContent":["import {\n channel,\n subscribe as dcSubscribe,\n unsubscribe as dcUnsubscribe,\n type Channel\n} from \"node:diagnostics_channel\";\nimport type { AgentObservabilityEvent } from \"./agent\";\nimport type { MCPObservabilityEvent } from \"./mcp\";\n\n/**\n * Union of all observability event types from different domains\n */\nexport type ObservabilityEvent =\n | AgentObservabilityEvent\n | MCPObservabilityEvent;\n\nexport interface Observability {\n /**\n * Emit an event for the Agent's observability implementation to handle.\n * @param event - The event to emit\n */\n emit(event: ObservabilityEvent): void;\n}\n\n/**\n * Diagnostics channels for agent observability.\n *\n * Events are published to named channels using the Node.js diagnostics_channel API.\n * By default, publishing to a channel with no subscribers is a no-op (zero overhead).\n *\n * To observe events, subscribe to the channels you care about:\n * ```ts\n * import { subscribe } from \"node:diagnostics_channel\";\n * subscribe(\"agents:rpc\", (event) => console.log(event));\n * ```\n *\n * In production, all published messages are automatically forwarded to\n * Tail Workers via `event.diagnosticsChannelEvents` — no subscription needed.\n */\nexport const channels = {\n state: channel(\"agents:state\"),\n rpc: channel(\"agents:rpc\"),\n message: channel(\"agents:message\"),\n chat: channel(\"agents:chat\"),\n transcript: channel(\"agents:transcript\"),\n fiber: channel(\"agents:fiber\"),\n agentTool: channel(\"agents:agent_tool\"),\n schedule: channel(\"agents:schedule\"),\n lifecycle: channel(\"agents:lifecycle\"),\n workflow: channel(\"agents:workflow\"),\n mcp: channel(\"agents:mcp\"),\n email: channel(\"agents:email\")\n} as const;\n\n/**\n * Channel keys whose diagnostics channel name differs from `agents:${key}`.\n * Keep this in sync with {@link channels} for any camelCase key that maps to a\n * snake_case diagnostics channel.\n */\nconst CHANNEL_DIAGNOSTIC_NAME_OVERRIDES: Partial<Record<string, string>> = {\n agentTool: \"agents:agent_tool\"\n};\n\n/**\n * Map event type prefixes to their diagnostics channel.\n */\nfunction getChannel(type: string): Channel {\n if (type.startsWith(\"mcp:\")) return channels.mcp;\n if (type.startsWith(\"workflow:\")) return channels.workflow;\n if (type.startsWith(\"fiber:\")) return channels.fiber;\n if (type.startsWith(\"transcript:\") || type.startsWith(\"chat:transcript:\"))\n return channels.transcript;\n if (type.startsWith(\"chat:\")) return channels.chat;\n if (type.startsWith(\"agent_tool:\")) return channels.agentTool;\n if (type.startsWith(\"schedule:\") || type.startsWith(\"queue:\"))\n return channels.schedule;\n if (\n type.startsWith(\"message:\") ||\n type.startsWith(\"tool:\") ||\n type.startsWith(\"submission:\")\n )\n return channels.message;\n if (type === \"rpc\" || type.startsWith(\"rpc:\")) return channels.rpc;\n if (type.startsWith(\"state:\")) return channels.state;\n if (type.startsWith(\"email:\")) return channels.email;\n // connect, disconnect, destroy\n return channels.lifecycle;\n}\n\n/**\n * The default observability implementation.\n *\n * Publishes events to diagnostics_channel. Events are silent unless\n * a subscriber is registered or a Tail Worker is attached.\n */\nexport const genericObservability: Observability = {\n emit(event) {\n getChannel(event.type).publish(event);\n }\n};\n\n/**\n * Maps each channel key to the observability events it carries.\n */\nexport type ChannelEventMap = {\n state: Extract<ObservabilityEvent, { type: `state:${string}` }>;\n rpc: Extract<ObservabilityEvent, { type: \"rpc\" | `rpc:${string}` }>;\n message: Extract<\n ObservabilityEvent,\n { type: `message:${string}` | `tool:${string}` | `submission:${string}` }\n >;\n chat: Exclude<\n Extract<ObservabilityEvent, { type: `chat:${string}` }>,\n { type: `chat:transcript:${string}` }\n >;\n transcript: Extract<\n ObservabilityEvent,\n { type: `transcript:${string}` | `chat:transcript:${string}` }\n >;\n fiber: Extract<ObservabilityEvent, { type: `fiber:${string}` }>;\n agentTool: Extract<ObservabilityEvent, { type: `agent_tool:${string}` }>;\n schedule: Extract<\n ObservabilityEvent,\n { type: `schedule:${string}` | `queue:${string}` }\n >;\n lifecycle: Extract<\n ObservabilityEvent,\n { type: \"connect\" | \"disconnect\" | \"destroy\" }\n >;\n workflow: Extract<ObservabilityEvent, { type: `workflow:${string}` }>;\n mcp: Extract<ObservabilityEvent, { type: `mcp:${string}` }>;\n email: Extract<ObservabilityEvent, { type: `email:${string}` }>;\n};\n\n/**\n * Subscribe to a typed observability channel.\n *\n * ```ts\n * import { subscribe } from \"agents/observability\";\n *\n * const unsub = subscribe(\"rpc\", (event) => {\n * console.log(event.payload.method); // fully typed\n * });\n * ```\n *\n * @returns A function that unsubscribes the callback.\n */\nexport function subscribe<K extends keyof ChannelEventMap>(\n channelKey: K,\n callback: (event: ChannelEventMap[K]) => void\n): () => void {\n const name =\n CHANNEL_DIAGNOSTIC_NAME_OVERRIDES[channelKey] ?? `agents:${channelKey}`;\n const handler = (message: unknown, _name: string | symbol) =>\n callback(message as ChannelEventMap[K]);\n dcSubscribe(name, handler);\n return () => dcUnsubscribe(name, handler);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuCA,MAAa,WAAW;CACtB,OAAO,QAAQ,cAAc;CAC7B,KAAK,QAAQ,YAAY;CACzB,SAAS,QAAQ,gBAAgB;CACjC,MAAM,QAAQ,aAAa;CAC3B,YAAY,QAAQ,mBAAmB;CACvC,OAAO,QAAQ,cAAc;CAC7B,WAAW,QAAQ,mBAAmB;CACtC,UAAU,QAAQ,iBAAiB;CACnC,WAAW,QAAQ,kBAAkB;CACrC,UAAU,QAAQ,iBAAiB;CACnC,KAAK,QAAQ,YAAY;CACzB,OAAO,QAAQ,cAAc;AAC/B;;;;;;AAOA,MAAM,oCAAqE,EACzE,WAAW,oBACb;;;;AAKA,SAAS,WAAW,MAAuB;CACzC,IAAI,KAAK,WAAW,MAAM,GAAG,OAAO,SAAS;CAC7C,IAAI,KAAK,WAAW,WAAW,GAAG,OAAO,SAAS;CAClD,IAAI,KAAK,WAAW,QAAQ,GAAG,OAAO,SAAS;CAC/C,IAAI,KAAK,WAAW,aAAa,KAAK,KAAK,WAAW,kBAAkB,GACtE,OAAO,SAAS;CAClB,IAAI,KAAK,WAAW,OAAO,GAAG,OAAO,SAAS;CAC9C,IAAI,KAAK,WAAW,aAAa,GAAG,OAAO,SAAS;CACpD,IAAI,KAAK,WAAW,WAAW,KAAK,KAAK,WAAW,QAAQ,GAC1D,OAAO,SAAS;CAClB,IACE,KAAK,WAAW,UAAU,KAC1B,KAAK,WAAW,OAAO,KACvB,KAAK,WAAW,aAAa,GAE7B,OAAO,SAAS;CAClB,IAAI,SAAS,SAAS,KAAK,WAAW,MAAM,GAAG,OAAO,SAAS;CAC/D,IAAI,KAAK,WAAW,QAAQ,GAAG,OAAO,SAAS;CAC/C,IAAI,KAAK,WAAW,QAAQ,GAAG,OAAO,SAAS;CAE/C,OAAO,SAAS;AAClB;;;;;;;AAQA,MAAa,uBAAsC,EACjD,KAAK,OAAO;CACV,WAAW,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK;AACtC,EACF;;;;;;;;;;;;;;AAgDA,SAAgB,UACd,YACA,UACY;CACZ,MAAM,OACJ,kCAAkC,eAAe,UAAU;CAC7D,MAAM,WAAW,SAAkB,UACjC,SAAS,OAA6B;CACxC,YAAY,MAAM,OAAO;CACzB,aAAaA,YAAc,MAAM,OAAO;AAC1C"}
1
+ {"version":3,"file":"index.js","names":["dcUnsubscribe"],"sources":["../../src/observability/index.ts"],"sourcesContent":["import {\n channel,\n subscribe as dcSubscribe,\n unsubscribe as dcUnsubscribe,\n type Channel\n} from \"node:diagnostics_channel\";\nimport type { AgentObservabilityEvent } from \"./agent\";\nimport type { MCPObservabilityEvent } from \"./mcp\";\n\n/**\n * Union of all observability event types from different domains\n */\nexport type ObservabilityEvent =\n | AgentObservabilityEvent\n | MCPObservabilityEvent;\n\nexport interface Observability {\n /**\n * Emit an event for the Agent's observability implementation to handle.\n * @param event - The event to emit\n */\n emit(event: ObservabilityEvent): void;\n}\n\n/**\n * Diagnostics channels for agent observability.\n *\n * Events are published to named channels using the Node.js diagnostics_channel API.\n * By default, publishing to a channel with no subscribers is a no-op (zero overhead).\n *\n * To observe events, subscribe to the channels you care about:\n * ```ts\n * import { subscribe } from \"node:diagnostics_channel\";\n * subscribe(\"agents:rpc\", (event) => console.log(event));\n * ```\n *\n * In production, all published messages are automatically forwarded to\n * Tail Workers via `event.diagnosticsChannelEvents` — no subscription needed.\n */\nexport const channels = {\n state: channel(\"agents:state\"),\n rpc: channel(\"agents:rpc\"),\n message: channel(\"agents:message\"),\n chat: channel(\"agents:chat\"),\n transcript: channel(\"agents:transcript\"),\n fiber: channel(\"agents:fiber\"),\n agentTool: channel(\"agents:agent_tool\"),\n schedule: channel(\"agents:schedule\"),\n lifecycle: channel(\"agents:lifecycle\"),\n workflow: channel(\"agents:workflow\"),\n mcp: channel(\"agents:mcp\"),\n email: channel(\"agents:email\"),\n channel: channel(\"agents:channel\")\n} as const;\n\n/**\n * Channel keys whose diagnostics channel name differs from `agents:${key}`.\n * Keep this in sync with {@link channels} for any camelCase key that maps to a\n * snake_case diagnostics channel.\n */\nconst CHANNEL_DIAGNOSTIC_NAME_OVERRIDES: Partial<Record<string, string>> = {\n agentTool: \"agents:agent_tool\"\n};\n\n/**\n * Map event type prefixes to their diagnostics channel.\n */\nfunction getChannel(type: string): Channel {\n if (type.startsWith(\"mcp:\")) return channels.mcp;\n if (type.startsWith(\"workflow:\")) return channels.workflow;\n if (type.startsWith(\"fiber:\")) return channels.fiber;\n if (type.startsWith(\"transcript:\") || type.startsWith(\"chat:transcript:\"))\n return channels.transcript;\n if (type.startsWith(\"chat:\")) return channels.chat;\n if (type.startsWith(\"agent_tool:\")) return channels.agentTool;\n if (type.startsWith(\"schedule:\") || type.startsWith(\"queue:\"))\n return channels.schedule;\n if (\n type.startsWith(\"message:\") ||\n type.startsWith(\"tool:\") ||\n type.startsWith(\"submission:\") ||\n type.startsWith(\"action:\")\n )\n return channels.message;\n if (type === \"rpc\" || type.startsWith(\"rpc:\")) return channels.rpc;\n if (type.startsWith(\"state:\")) return channels.state;\n if (type.startsWith(\"email:\")) return channels.email;\n if (type.startsWith(\"channel:\") || type.startsWith(\"notice:\"))\n return channels.channel;\n // connect, disconnect, destroy\n return channels.lifecycle;\n}\n\n/**\n * The default observability implementation.\n *\n * Publishes events to diagnostics_channel. Events are silent unless\n * a subscriber is registered or a Tail Worker is attached.\n */\nexport const genericObservability: Observability = {\n emit(event) {\n getChannel(event.type).publish(event);\n }\n};\n\n/**\n * Maps each channel key to the observability events it carries.\n */\nexport type ChannelEventMap = {\n state: Extract<ObservabilityEvent, { type: `state:${string}` }>;\n rpc: Extract<ObservabilityEvent, { type: \"rpc\" | `rpc:${string}` }>;\n message: Extract<\n ObservabilityEvent,\n {\n type:\n | `message:${string}`\n | `tool:${string}`\n | `submission:${string}`\n | `action:${string}`;\n }\n >;\n chat: Exclude<\n Extract<ObservabilityEvent, { type: `chat:${string}` }>,\n { type: `chat:transcript:${string}` }\n >;\n transcript: Extract<\n ObservabilityEvent,\n { type: `transcript:${string}` | `chat:transcript:${string}` }\n >;\n fiber: Extract<ObservabilityEvent, { type: `fiber:${string}` }>;\n agentTool: Extract<ObservabilityEvent, { type: `agent_tool:${string}` }>;\n schedule: Extract<\n ObservabilityEvent,\n { type: `schedule:${string}` | `queue:${string}` }\n >;\n lifecycle: Extract<\n ObservabilityEvent,\n { type: \"connect\" | \"disconnect\" | \"destroy\" }\n >;\n workflow: Extract<ObservabilityEvent, { type: `workflow:${string}` }>;\n mcp: Extract<ObservabilityEvent, { type: `mcp:${string}` }>;\n email: Extract<ObservabilityEvent, { type: `email:${string}` }>;\n channel: Extract<\n ObservabilityEvent,\n { type: `channel:${string}` | `notice:${string}` }\n >;\n};\n\n/**\n * Subscribe to a typed observability channel.\n *\n * ```ts\n * import { subscribe } from \"agents/observability\";\n *\n * const unsub = subscribe(\"rpc\", (event) => {\n * console.log(event.payload.method); // fully typed\n * });\n * ```\n *\n * @returns A function that unsubscribes the callback.\n */\nexport function subscribe<K extends keyof ChannelEventMap>(\n channelKey: K,\n callback: (event: ChannelEventMap[K]) => void\n): () => void {\n const name =\n CHANNEL_DIAGNOSTIC_NAME_OVERRIDES[channelKey] ?? `agents:${channelKey}`;\n const handler = (message: unknown, _name: string | symbol) =>\n callback(message as ChannelEventMap[K]);\n dcSubscribe(name, handler);\n return () => dcUnsubscribe(name, handler);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuCA,MAAa,WAAW;CACtB,OAAO,QAAQ,cAAc;CAC7B,KAAK,QAAQ,YAAY;CACzB,SAAS,QAAQ,gBAAgB;CACjC,MAAM,QAAQ,aAAa;CAC3B,YAAY,QAAQ,mBAAmB;CACvC,OAAO,QAAQ,cAAc;CAC7B,WAAW,QAAQ,mBAAmB;CACtC,UAAU,QAAQ,iBAAiB;CACnC,WAAW,QAAQ,kBAAkB;CACrC,UAAU,QAAQ,iBAAiB;CACnC,KAAK,QAAQ,YAAY;CACzB,OAAO,QAAQ,cAAc;CAC7B,SAAS,QAAQ,gBAAgB;AACnC;;;;;;AAOA,MAAM,oCAAqE,EACzE,WAAW,oBACb;;;;AAKA,SAAS,WAAW,MAAuB;CACzC,IAAI,KAAK,WAAW,MAAM,GAAG,OAAO,SAAS;CAC7C,IAAI,KAAK,WAAW,WAAW,GAAG,OAAO,SAAS;CAClD,IAAI,KAAK,WAAW,QAAQ,GAAG,OAAO,SAAS;CAC/C,IAAI,KAAK,WAAW,aAAa,KAAK,KAAK,WAAW,kBAAkB,GACtE,OAAO,SAAS;CAClB,IAAI,KAAK,WAAW,OAAO,GAAG,OAAO,SAAS;CAC9C,IAAI,KAAK,WAAW,aAAa,GAAG,OAAO,SAAS;CACpD,IAAI,KAAK,WAAW,WAAW,KAAK,KAAK,WAAW,QAAQ,GAC1D,OAAO,SAAS;CAClB,IACE,KAAK,WAAW,UAAU,KAC1B,KAAK,WAAW,OAAO,KACvB,KAAK,WAAW,aAAa,KAC7B,KAAK,WAAW,SAAS,GAEzB,OAAO,SAAS;CAClB,IAAI,SAAS,SAAS,KAAK,WAAW,MAAM,GAAG,OAAO,SAAS;CAC/D,IAAI,KAAK,WAAW,QAAQ,GAAG,OAAO,SAAS;CAC/C,IAAI,KAAK,WAAW,QAAQ,GAAG,OAAO,SAAS;CAC/C,IAAI,KAAK,WAAW,UAAU,KAAK,KAAK,WAAW,SAAS,GAC1D,OAAO,SAAS;CAElB,OAAO,SAAS;AAClB;;;;;;;AAQA,MAAa,uBAAsC,EACjD,KAAK,OAAO;CACV,WAAW,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK;AACtC,EACF;;;;;;;;;;;;;;AA0DA,SAAgB,UACd,YACA,UACY;CACZ,MAAM,OACJ,kCAAkC,eAAe,UAAU;CAC7D,MAAM,WAAW,SAAkB,UACjC,SAAS,OAA6B;CACxC,YAAY,MAAM,OAAO;CACzB,aAAaA,YAAc,MAAM,OAAO;AAC1C"}
package/dist/react.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import {
2
- V as MCPServersState,
3
- d as AgentToolRunState
4
- } from "./agent-tool-types-CTw3UJUP.js";
2
+ Y as MCPServersState,
3
+ _ as AgentToolRunState,
4
+ g as AgentToolRunPart
5
+ } from "./agent-tool-types-Cd1TZPfB.js";
5
6
  import { ClientParameters } from "./serializable.js";
6
7
  import {
8
+ AgentConnectionError,
7
9
  AgentPromiseReturnType,
8
10
  AgentStub,
9
11
  CallOptions,
@@ -18,6 +20,9 @@ import { usePartySocket } from "partysocket/react";
18
20
 
19
21
  //#region src/react.d.ts
20
22
  type QueryObject = Record<string, string | null>;
23
+ type TerminalReconnectOptions = {
24
+ shouldReconnectOnClose?: (event: CloseEvent) => boolean;
25
+ };
21
26
  interface CacheEntry {
22
27
  promise: Promise<QueryObject>;
23
28
  expiresAt: number;
@@ -56,108 +61,110 @@ declare const _testUtils: {
56
61
  type UseAgentOptions<State = unknown> = Omit<
57
62
  Parameters<typeof usePartySocket>[0],
58
63
  "party" | "room" | "query"
59
- > & {
60
- /** Name of the agent to connect to (ignored if basePath is set) */ agent: string /** Name of the specific Agent instance (ignored if basePath is set) */;
61
- name?: string;
62
- /**
63
- * Full URL path - bypasses agent/name URL construction.
64
- * When set, the client connects to this path directly.
65
- * Server must handle routing manually (e.g., with getAgentByName + fetch).
66
- * @example
67
- * // Client connects to /user, server routes based on session
68
- * useAgent({ agent: "UserAgent", basePath: "user" })
69
- */
70
- basePath?: string /** Query parameters - can be static object or async function */;
71
- query?:
72
- | QueryObject
73
- | (() => Promise<QueryObject>) /** Dependencies for async query caching */;
74
- queryDeps?: unknown[] /** Cache TTL in milliseconds for auth tokens/time-sensitive data */;
75
- cacheTtl?: number /** Called when the Agent's state is updated */;
76
- onStateUpdate?: (
77
- state: State,
78
- source: "server" | "client"
79
- ) => void /** Called when a state update fails (e.g., connection is readonly) */;
80
- onStateUpdateError?: (
81
- error: string
82
- ) => void /** Called when MCP server state is updated */;
83
- onMcpUpdate?: (mcpServers: MCPServersState) => void;
84
- /**
85
- * Called when the server sends the agent's identity on connect.
86
- * Useful when using basePath, as the actual instance name is determined server-side.
87
- * @param name The actual agent instance name
88
- * @param agent The agent class name (kebab-case)
89
- */
90
- onIdentity?: (name: string, agent: string) => void;
91
- /**
92
- * Called when identity changes on reconnect (different instance than before).
93
- * If not provided and identity changes, a warning will be logged.
94
- * @param oldName Previous instance name
95
- * @param newName New instance name
96
- * @param oldAgent Previous agent class name
97
- * @param newAgent New agent class name
98
- */
99
- onIdentityChange?: (
100
- oldName: string,
101
- newName: string,
102
- oldAgent: string,
103
- newAgent: string
104
- ) => void;
105
- /**
106
- * Additional path to append to the URL.
107
- * Works with both standard routing and basePath.
108
- * @example
109
- * // With basePath: /user/settings
110
- * { basePath: "user", path: "settings" }
111
- * // Standard: /agents/my-agent/room/settings
112
- * { agent: "MyAgent", name: "room", path: "settings" }
113
- */
114
- path?: string;
115
- /**
116
- * Connect to a sub-agent (facet) via its parent. Flat array,
117
- * root-first. Each step addresses one parent↔child hop.
118
- *
119
- * The hook's returned `.agent` / `.name` report the **leaf**
120
- * identity (the deepest entry in `sub`), so downstream hooks
121
- * like `useAgentChat` see the child they actually talk to.
122
- * `.path` exposes the full chain for observability, deep links,
123
- * and reconnect keying.
124
- *
125
- * @example
126
- * ```ts
127
- * // Two-level nesting: Inbox (Alice) → Chat (abc)
128
- * useAgent({
129
- * agent: "inbox", name: userId,
130
- * sub: [{ agent: "chat", name: chatId }]
131
- * });
132
- *
133
- * // Three-level: tenant → inbox → chat
134
- * useAgent({
135
- * agent: "tenant", name: tenantId,
136
- * sub: [
137
- * { agent: "inbox", name: userId },
138
- * { agent: "chat", name: chatId }
139
- * ]
140
- * });
141
- * ```
142
- *
143
- * @experimental The API surface may change before stabilizing.
144
- */
145
- sub?: ReadonlyArray<{
146
- agent: string;
147
- name: string;
148
- }>;
149
- /**
150
- * Default timeout (in milliseconds) applied to non-streaming `call()`s
151
- * that don't pass an explicit `timeout`. Acts as a backstop so calls
152
- * whose response is lost (e.g. the connection is replaced mid-flight)
153
- * reject instead of hanging forever.
154
- *
155
- * Defaults to 30 000 ms. Set to `0` to disable the default timeout.
156
- * Streaming calls never get a default timeout (long-lived streams are
157
- * legitimate); pass an explicit `timeout` to bound them.
158
- */
159
- defaultCallTimeout?: number;
160
- };
64
+ > &
65
+ TerminalReconnectOptions & {
66
+ /** Name of the agent to connect to (ignored if basePath is set) */ agent: string /** Name of the specific Agent instance (ignored if basePath is set) */;
67
+ name?: string;
68
+ /**
69
+ * Full URL path - bypasses agent/name URL construction.
70
+ * When set, the client connects to this path directly.
71
+ * Server must handle routing manually (e.g., with getAgentByName + fetch).
72
+ * @example
73
+ * // Client connects to /user, server routes based on session
74
+ * useAgent({ agent: "UserAgent", basePath: "user" })
75
+ */
76
+ basePath?: string /** Query parameters - can be static object or async function */;
77
+ query?:
78
+ | QueryObject
79
+ | (() => Promise<QueryObject>) /** Dependencies for async query caching */;
80
+ queryDeps?: unknown[] /** Cache TTL in milliseconds for auth tokens/time-sensitive data */;
81
+ cacheTtl?: number /** Called when the Agent's state is updated */;
82
+ onStateUpdate?: (
83
+ state: State,
84
+ source: "server" | "client"
85
+ ) => void /** Called when a state update fails (e.g., connection is readonly) */;
86
+ onStateUpdateError?: (
87
+ error: string
88
+ ) => void /** Called when MCP server state is updated */;
89
+ onMcpUpdate?: (mcpServers: MCPServersState) => void;
90
+ /**
91
+ * Called when the server sends the agent's identity on connect.
92
+ * Useful when using basePath, as the actual instance name is determined server-side.
93
+ * @param name The actual agent instance name
94
+ * @param agent The agent class name (kebab-case)
95
+ */
96
+ onIdentity?: (name: string, agent: string) => void;
97
+ /**
98
+ * Called when identity changes on reconnect (different instance than before).
99
+ * If not provided and identity changes, a warning will be logged.
100
+ * @param oldName Previous instance name
101
+ * @param newName New instance name
102
+ * @param oldAgent Previous agent class name
103
+ * @param newAgent New agent class name
104
+ */
105
+ onIdentityChange?: (
106
+ oldName: string,
107
+ newName: string,
108
+ oldAgent: string,
109
+ newAgent: string
110
+ ) => void;
111
+ /**
112
+ * Additional path to append to the URL.
113
+ * Works with both standard routing and basePath.
114
+ * @example
115
+ * // With basePath: /user/settings
116
+ * { basePath: "user", path: "settings" }
117
+ * // Standard: /agents/my-agent/room/settings
118
+ * { agent: "MyAgent", name: "room", path: "settings" }
119
+ */
120
+ path?: string;
121
+ /**
122
+ * Connect to a sub-agent (facet) via its parent. Flat array,
123
+ * root-first. Each step addresses one parent↔child hop.
124
+ *
125
+ * The hook's returned `.agent` / `.name` report the **leaf**
126
+ * identity (the deepest entry in `sub`), so downstream hooks
127
+ * like `useAgentChat` see the child they actually talk to.
128
+ * `.path` exposes the full chain for observability, deep links,
129
+ * and reconnect keying.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * // Two-level nesting: Inbox (Alice) → Chat (abc)
134
+ * useAgent({
135
+ * agent: "inbox", name: userId,
136
+ * sub: [{ agent: "chat", name: chatId }]
137
+ * });
138
+ *
139
+ * // Three-level: tenant → inbox → chat
140
+ * useAgent({
141
+ * agent: "tenant", name: tenantId,
142
+ * sub: [
143
+ * { agent: "inbox", name: userId },
144
+ * { agent: "chat", name: chatId }
145
+ * ]
146
+ * });
147
+ * ```
148
+ *
149
+ * @experimental The API surface may change before stabilizing.
150
+ */
151
+ sub?: ReadonlyArray<{
152
+ agent: string;
153
+ name: string;
154
+ }>;
155
+ /**
156
+ * Default timeout (in milliseconds) applied to non-streaming `call()`s
157
+ * that don't pass an explicit `timeout`. Acts as a backstop so calls
158
+ * whose response is lost (e.g. the connection is replaced mid-flight)
159
+ * reject instead of hanging forever.
160
+ *
161
+ * Defaults to 30 000 ms. Set to `0` to disable the default timeout.
162
+ * Streaming calls never get a default timeout (long-lived streams are
163
+ * legitimate); pass an explicit `timeout` to bound them.
164
+ */
165
+ defaultCallTimeout?: number /** Called when the connection closes with a terminal code and will not reconnect. */;
166
+ onConnectionError?: (error: AgentConnectionError) => void;
167
+ };
161
168
  type OptionalArgsAgentMethodCall<AgentT> = <
162
169
  K extends keyof OptionalAgentMethods<AgentT>
163
170
  >(
@@ -194,6 +201,7 @@ declare function useAgent<State = unknown>(
194
201
  identified: boolean;
195
202
  ready: Promise<void>;
196
203
  state: State | undefined;
204
+ connectionError: AgentConnectionError | null;
197
205
  setState: (state: State) => void;
198
206
  call: UntypedAgentMethodCall;
199
207
  stub: UntypedAgentStub;
@@ -216,6 +224,7 @@ declare function useAgent<
216
224
  identified: boolean;
217
225
  ready: Promise<void>;
218
226
  state: State | undefined;
227
+ connectionError: AgentConnectionError | null;
219
228
  setState: (state: State) => void;
220
229
  call: AgentMethodCall<AgentT>;
221
230
  stub: AgentStub<AgentT>;
@@ -225,11 +234,15 @@ type AgentToolEventAgent = Pick<
225
234
  PartySocket,
226
235
  "addEventListener" | "removeEventListener"
227
236
  >;
228
- declare function useAgentToolEvents(options: { agent: AgentToolEventAgent }): {
229
- runsById: Record<string, AgentToolRunState>;
230
- runsByToolCallId: Record<string, AgentToolRunState[]>;
231
- unboundRuns: AgentToolRunState[];
232
- getRunsForToolCall(toolCallId: string): AgentToolRunState[];
237
+ declare function useAgentToolEvents<
238
+ Part extends AgentToolRunPart = AgentToolRunPart
239
+ >(options: {
240
+ agent: AgentToolEventAgent;
241
+ }): {
242
+ runsById: Record<string, AgentToolRunState<Part>>;
243
+ runsByToolCallId: Record<string, AgentToolRunState<Part>[]>;
244
+ unboundRuns: AgentToolRunState<Part>[];
245
+ getRunsForToolCall(toolCallId: string): AgentToolRunState<Part>[];
233
246
  resetLocalState(): void;
234
247
  };
235
248
  //#endregion