agents 0.14.1 → 0.14.3

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{agent-tool-types-BAJWu8s4.d.ts → agent-tool-types-V25Z_HcX.d.ts} +258 -115
  3. package/dist/agent-tool-types.d.ts +13 -11
  4. package/dist/{agent-tools-DYrkT-Kx.js → agent-tools-3zLG7MgA.js} +4 -2
  5. package/dist/{agent-tools-DYrkT-Kx.js.map → agent-tools-3zLG7MgA.js.map} +1 -1
  6. package/dist/{agent-tools-0R6KEert.d.ts → agent-tools-C-9s151X.d.ts} +2 -2
  7. package/dist/agent-tools.d.ts +13 -11
  8. package/dist/agent-tools.js +8 -3
  9. package/dist/agent-tools.js.map +1 -1
  10. package/dist/browser/ai.js +1 -1
  11. package/dist/browser/ai.js.map +1 -1
  12. package/dist/browser/index.js +1 -1
  13. package/dist/browser/tanstack-ai.js +1 -1
  14. package/dist/browser/tanstack-ai.js.map +1 -1
  15. package/dist/chat/index.d.ts +133 -11
  16. package/dist/chat/index.js +118 -29
  17. package/dist/chat/index.js.map +1 -1
  18. package/dist/chat-sdk/index.d.ts +4 -4
  19. package/dist/chat-sdk/index.js.map +1 -1
  20. package/dist/{classPrivateFieldGet2-D_obpP6O.js → classPrivateFieldGet2-Beqsfu2Z.js} +5 -5
  21. package/dist/{classPrivateMethodInitSpec-10iTYB7F.js → classPrivateMethodInitSpec-B5ko1s2R.js} +2 -2
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/client-FUizKzj2.js.map +1 -1
  24. package/dist/client.d.ts +1 -1
  25. package/dist/compaction-helpers-iiKMr2TQ.js.map +1 -1
  26. package/dist/email.js.map +1 -1
  27. package/dist/experimental/memory/session/index.js.map +1 -1
  28. package/dist/experimental/webmcp.js.map +1 -1
  29. package/dist/{index-RJ4OxMOe.d.ts → index-CPe1OtI0.d.ts} +17 -1
  30. package/dist/index.d.ts +66 -64
  31. package/dist/index.js +263 -76
  32. package/dist/index.js.map +1 -1
  33. package/dist/mcp/client.d.ts +14 -14
  34. package/dist/mcp/do-oauth-client-provider.js.map +1 -1
  35. package/dist/mcp/index.d.ts +30 -30
  36. package/dist/mcp/index.js.map +1 -1
  37. package/dist/mcp/x402.js.map +1 -1
  38. package/dist/observability/index.d.ts +1 -1
  39. package/dist/observability/index.js.map +1 -1
  40. package/dist/react.d.ts +3 -3
  41. package/dist/react.js +1 -1
  42. package/dist/react.js.map +1 -1
  43. package/dist/schedule.js.map +1 -1
  44. package/dist/serializable.d.ts +1 -1
  45. package/dist/{shared-BIpUk4G5.js → shared-wyII629d.js} +3 -3
  46. package/dist/{shared-BIpUk4G5.js.map → shared-wyII629d.js.map} +1 -1
  47. package/dist/skills/index.js +4 -4
  48. package/dist/skills/index.js.map +1 -1
  49. package/dist/sub-routing.d.ts +6 -6
  50. package/dist/sub-routing.js.map +1 -1
  51. package/dist/tool-output-truncation-CNnnGZQ3.js.map +1 -1
  52. package/dist/utils.js.map +1 -1
  53. package/dist/vite.d.ts +15 -4
  54. package/dist/vite.js +37 -7
  55. package/dist/vite.js.map +1 -1
  56. package/dist/workflows.d.ts +10 -2
  57. package/dist/workflows.js +48 -22
  58. package/dist/workflows.js.map +1 -1
  59. package/package.json +9 -17
@@ -1 +1 @@
1
- {"version":3,"file":"agent-tools-DYrkT-Kx.js","names":[],"sources":["../src/chat/message-builder.ts","../src/chat/agent-tools.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 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 p.state = \"approval-requested\";\n p.approval = { id: chunk.approvalId };\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 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 */\nexport function isReplayChunk(\n parts: MessagePart[],\n chunk: StreamChunkData\n): boolean {\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","import { applyChunkToParts } from \"./message-builder\";\nimport type {\n AgentToolEventMessage,\n AgentToolEventState,\n AgentToolRunState\n} from \"../agent-tool-types\";\n\nfunction sortRuns(runs: AgentToolRunState[]): AgentToolRunState[] {\n return [...runs].sort((a, b) => {\n if (a.order !== b.order) return a.order - b.order;\n return a.runId.localeCompare(b.runId);\n });\n}\n\nfunction rebuildIndexes(\n runsById: Record<string, AgentToolRunState>\n): Pick<AgentToolEventState, \"runsByToolCallId\" | \"unboundRuns\"> {\n const grouped: Record<string, AgentToolRunState[]> = {};\n const unboundRuns: AgentToolRunState[] = [];\n for (const run of Object.values(runsById)) {\n if (run.parentToolCallId) {\n grouped[run.parentToolCallId] = grouped[run.parentToolCallId] ?? [];\n grouped[run.parentToolCallId].push(run);\n } else {\n unboundRuns.push(run);\n }\n }\n for (const [toolCallId, runs] of Object.entries(grouped)) {\n grouped[toolCallId] = sortRuns(runs);\n }\n return { runsByToolCallId: grouped, unboundRuns: sortRuns(unboundRuns) };\n}\n\nfunction emptyRun(\n message: AgentToolEventMessage\n): AgentToolRunState | undefined {\n const { event } = message;\n if (event.kind === \"started\") {\n return {\n runId: event.runId,\n agentType: event.agentType,\n parentToolCallId: message.parentToolCallId,\n inputPreview: event.inputPreview,\n order: event.order,\n display: event.display,\n status: \"running\",\n parts: [],\n subAgent: { agent: event.agentType, name: event.runId }\n };\n }\n return undefined;\n}\n\nfunction applyToRun(\n prev: AgentToolRunState | undefined,\n message: AgentToolEventMessage\n): AgentToolRunState | undefined {\n const seeded = prev ?? emptyRun(message);\n const { event } = message;\n\n switch (event.kind) {\n case \"started\":\n if (\n seeded?.status === \"completed\" ||\n seeded?.status === \"error\" ||\n seeded?.status === \"aborted\" ||\n seeded?.status === \"interrupted\"\n ) {\n return seeded;\n }\n return {\n ...seeded,\n runId: event.runId,\n agentType: event.agentType,\n parentToolCallId: message.parentToolCallId,\n inputPreview: event.inputPreview,\n order: event.order,\n display: event.display,\n status: \"running\",\n parts: seeded?.parts ?? [],\n subAgent: { agent: event.agentType, name: event.runId }\n };\n case \"chunk\": {\n if (!seeded) return undefined;\n const parts = [...seeded.parts];\n try {\n applyChunkToParts(parts, JSON.parse(event.body));\n } catch {\n return seeded;\n }\n return { ...seeded, parts };\n }\n case \"finished\":\n if (!seeded) return undefined;\n return {\n ...seeded,\n status: \"completed\",\n summary: event.summary,\n error: undefined\n };\n case \"error\":\n if (!seeded) return undefined;\n return { ...seeded, status: \"error\", error: event.error };\n case \"aborted\":\n if (!seeded) return undefined;\n return { ...seeded, status: \"aborted\", error: event.reason };\n case \"interrupted\":\n if (!seeded) return undefined;\n return { ...seeded, status: \"interrupted\", error: event.error };\n }\n}\n\nexport function createAgentToolEventState(): AgentToolEventState {\n return {\n runsById: {},\n runsByToolCallId: {},\n unboundRuns: []\n };\n}\n\nexport function applyAgentToolEvent(\n state: AgentToolEventState,\n message: AgentToolEventMessage\n): AgentToolEventState {\n if (message.type !== \"agent-tool-event\") return state;\n const runId = message.event.runId;\n const nextRun = applyToRun(state.runsById[runId], message);\n if (!nextRun) return state;\n\n const runsById = { ...state.runsById, [runId]: nextRun };\n return { runsById, ...rebuildIndexes(runsById) };\n}\n\nexport type {\n AgentToolEvent,\n AgentToolEventMessage,\n AgentToolEventState,\n AgentToolRunState\n} from \"../agent-tool-types\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-tools-3zLG7MgA.js","names":[],"sources":["../src/chat/message-builder.ts","../src/chat/agent-tools.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 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 p.state = \"approval-requested\";\n p.approval = { id: chunk.approvalId };\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 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 */\nexport function isReplayChunk(\n parts: MessagePart[],\n chunk: StreamChunkData\n): boolean {\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","import { applyChunkToParts } from \"./message-builder\";\nimport type {\n AgentToolEventMessage,\n AgentToolEventState,\n AgentToolRunState\n} from \"../agent-tool-types\";\n\nfunction sortRuns(runs: AgentToolRunState[]): AgentToolRunState[] {\n return [...runs].sort((a, b) => {\n if (a.order !== b.order) return a.order - b.order;\n return a.runId.localeCompare(b.runId);\n });\n}\n\nfunction rebuildIndexes(\n runsById: Record<string, AgentToolRunState>\n): Pick<AgentToolEventState, \"runsByToolCallId\" | \"unboundRuns\"> {\n const grouped: Record<string, AgentToolRunState[]> = {};\n const unboundRuns: AgentToolRunState[] = [];\n for (const run of Object.values(runsById)) {\n if (run.parentToolCallId) {\n grouped[run.parentToolCallId] = grouped[run.parentToolCallId] ?? [];\n grouped[run.parentToolCallId].push(run);\n } else {\n unboundRuns.push(run);\n }\n }\n for (const [toolCallId, runs] of Object.entries(grouped)) {\n grouped[toolCallId] = sortRuns(runs);\n }\n return { runsByToolCallId: grouped, unboundRuns: sortRuns(unboundRuns) };\n}\n\nfunction emptyRun(\n message: AgentToolEventMessage\n): AgentToolRunState | undefined {\n const { event } = message;\n if (event.kind === \"started\") {\n return {\n runId: event.runId,\n agentType: event.agentType,\n parentToolCallId: message.parentToolCallId,\n inputPreview: event.inputPreview,\n order: event.order,\n display: event.display,\n status: \"running\",\n parts: [],\n subAgent: { agent: event.agentType, name: event.runId }\n };\n }\n return undefined;\n}\n\nfunction applyToRun(\n prev: AgentToolRunState | undefined,\n message: AgentToolEventMessage\n): AgentToolRunState | undefined {\n const seeded = prev ?? emptyRun(message);\n const { event } = message;\n\n switch (event.kind) {\n case \"started\":\n if (\n seeded?.status === \"completed\" ||\n seeded?.status === \"error\" ||\n seeded?.status === \"aborted\" ||\n seeded?.status === \"interrupted\"\n ) {\n return seeded;\n }\n return {\n ...seeded,\n runId: event.runId,\n agentType: event.agentType,\n parentToolCallId: message.parentToolCallId,\n inputPreview: event.inputPreview,\n order: event.order,\n display: event.display,\n status: \"running\",\n parts: seeded?.parts ?? [],\n subAgent: { agent: event.agentType, name: event.runId }\n };\n case \"chunk\": {\n if (!seeded) return undefined;\n const parts = [...seeded.parts];\n try {\n applyChunkToParts(parts, JSON.parse(event.body));\n } catch {\n return seeded;\n }\n return { ...seeded, parts };\n }\n case \"finished\":\n if (!seeded) return undefined;\n return {\n ...seeded,\n status: \"completed\",\n summary: event.summary,\n error: undefined\n };\n case \"error\":\n if (!seeded) return undefined;\n return { ...seeded, status: \"error\", error: event.error };\n case \"aborted\":\n if (!seeded) return undefined;\n return { ...seeded, status: \"aborted\", error: event.reason };\n case \"interrupted\":\n if (!seeded) return undefined;\n return {\n ...seeded,\n status: \"interrupted\",\n error: event.error,\n reason: event.reason,\n childStillRunning: event.childStillRunning\n };\n }\n}\n\nexport function createAgentToolEventState(): AgentToolEventState {\n return {\n runsById: {},\n runsByToolCallId: {},\n unboundRuns: []\n };\n}\n\nexport function applyAgentToolEvent(\n state: AgentToolEventState,\n message: AgentToolEventMessage\n): AgentToolEventState {\n if (message.type !== \"agent-tool-event\") return state;\n const runId = message.event.runId;\n const nextRun = applyToRun(state.runsById[runId], message);\n if (!nextRun) return state;\n\n const runsById = { ...state.runsById, [runId]: nextRun };\n return { runsById, ...rebuildIndexes(runsById) };\n}\n\nexport type {\n AgentToolEvent,\n AgentToolEventMessage,\n AgentToolEventState,\n AgentToolRunState\n} from \"../agent-tool-types\";\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  a as AgentToolEventState,
3
3
  i as AgentToolEventMessage
4
- } from "./agent-tool-types-BAJWu8s4.js";
4
+ } from "./agent-tool-types-V25Z_HcX.js";
5
5
 
6
6
  //#region src/chat/agent-tools.d.ts
7
7
  declare function createAgentToolEventState(): AgentToolEventState;
@@ -11,4 +11,4 @@ declare function applyAgentToolEvent(
11
11
  ): AgentToolEventState;
12
12
  //#endregion
13
13
  export { createAgentToolEventState as n, applyAgentToolEvent as t };
14
- //# sourceMappingURL=agent-tools-0R6KEert.d.ts.map
14
+ //# sourceMappingURL=agent-tools-C-9s151X.d.ts.map
@@ -1,21 +1,22 @@
1
1
  import {
2
+ _ as RunAgentToolResult,
2
3
  a as AgentToolEventState,
3
- c as AgentToolRunInfo,
4
- d as AgentToolRunStatus,
5
- f as AgentToolStoredChunk,
6
- g as RunAgentToolResult,
7
- h as RunAgentToolOptions,
4
+ c as AgentToolLifecycleResult,
5
+ d as AgentToolRunState,
6
+ f as AgentToolRunStatus,
7
+ g as RunAgentToolOptions,
8
+ h as ChatCapableAgentClass,
8
9
  i as AgentToolEventMessage,
9
- l as AgentToolRunInspection,
10
- m as ChatCapableAgentClass,
10
+ l as AgentToolRunInfo,
11
+ m as AgentToolTerminalStatus,
11
12
  n as AgentToolDisplayMetadata,
12
13
  o as AgentToolFailure,
13
- p as AgentToolTerminalStatus,
14
+ p as AgentToolStoredChunk,
14
15
  r as AgentToolEvent,
15
- s as AgentToolLifecycleResult,
16
+ s as AgentToolInterruptedReason,
16
17
  t as AgentToolChildAdapter,
17
- u as AgentToolRunState
18
- } from "./agent-tool-types-BAJWu8s4.js";
18
+ u as AgentToolRunInspection
19
+ } from "./agent-tool-types-V25Z_HcX.js";
19
20
  import { Tool } from "ai";
20
21
 
21
22
  //#region src/agent-tools.d.ts
@@ -47,6 +48,7 @@ export {
47
48
  type AgentToolEventState,
48
49
  type AgentToolFactoryOptions,
49
50
  type AgentToolFailure,
51
+ type AgentToolInterruptedReason,
50
52
  type AgentToolLifecycleResult,
51
53
  type AgentToolRunInfo,
52
54
  type AgentToolRunInspection,
@@ -6,12 +6,14 @@ function currentAgentToolRunner() {
6
6
  if (agent === null || typeof agent !== "object" || typeof agent.runAgentTool !== "function") throw new Error("agentTool() can only run inside an Agent turn. Use it from getTools() on an Agent subclass.");
7
7
  return agent;
8
8
  }
9
- function failure(status, error, retryable) {
9
+ function failure(status, error, retryable, extra) {
10
10
  return {
11
11
  ok: false,
12
12
  status,
13
13
  error,
14
- retryable
14
+ retryable,
15
+ ...extra?.reason !== void 0 ? { reason: extra.reason } : {},
16
+ ...extra?.childStillRunning !== void 0 ? { childStillRunning: extra.childStillRunning } : {}
15
17
  };
16
18
  }
17
19
  /**
@@ -44,7 +46,10 @@ function agentTool(cls, options) {
44
46
  return result.summary ?? "";
45
47
  }
46
48
  if (result.status === "aborted") return failure("aborted", result.error ?? "agent tool run was cancelled", false);
47
- if (result.status === "interrupted") return failure("interrupted", result.error ?? "agent tool run was interrupted before it finished; it can be retried", true);
49
+ if (result.status === "interrupted") return failure("interrupted", result.error ?? "agent tool run was interrupted before it finished; it can be retried", true, {
50
+ reason: result.reason,
51
+ childStillRunning: result.childStillRunning
52
+ });
48
53
  return failure("error", result.error ?? "agent tool run failed", false);
49
54
  }
50
55
  });
@@ -1 +1 @@
1
- {"version":3,"file":"agent-tools.js","names":["agentContext","createTool"],"sources":["../src/agent-tools.ts"],"sourcesContent":["import { tool, type Tool } from \"ai\";\nimport { __DO_NOT_USE_WILL_BREAK__agentContext as agentContext } from \"./internal_context\";\nimport type {\n ChatCapableAgentClass,\n RunAgentToolOptions,\n RunAgentToolResult,\n AgentToolDisplayMetadata,\n AgentToolFailure\n} from \"./agent-tool-types\";\n\ntype SchemaLike<T = unknown> = {\n parse(value: unknown): T;\n};\n\ntype AgentToolFactoryOptions<Output = unknown> = {\n description: string;\n inputSchema: unknown;\n outputSchema?: SchemaLike<Output>;\n displayName?: string;\n icon?: string;\n display?: AgentToolDisplayMetadata;\n};\n\ntype ToolExecutionOptions = {\n toolCallId?: string;\n abortSignal?: AbortSignal;\n};\n\ntype AgentToolRunner = {\n runAgentTool<Input, Output>(\n cls: ChatCapableAgentClass,\n options: RunAgentToolOptions<Input>\n ): Promise<RunAgentToolResult<Output>>;\n};\n\nfunction currentAgentToolRunner(): AgentToolRunner {\n const agent = agentContext.getStore()?.agent;\n if (\n agent === null ||\n typeof agent !== \"object\" ||\n typeof (agent as { runAgentTool?: unknown }).runAgentTool !== \"function\"\n ) {\n throw new Error(\n \"agentTool() can only run inside an Agent turn. Use it from getTools() on an Agent subclass.\"\n );\n }\n return agent as AgentToolRunner;\n}\n\nfunction failure(\n status: AgentToolFailure[\"status\"],\n error: string,\n retryable: boolean\n): AgentToolFailure {\n return { ok: false, status, error, retryable };\n}\n\n/**\n * Create an AI SDK tool that dispatches a chat-capable sub-agent through\n * `Agent.runAgentTool`.\n */\nexport function agentTool<Input = unknown, Output = unknown>(\n cls: ChatCapableAgentClass,\n options: AgentToolFactoryOptions<Output>\n): Tool<Input, string | Output | AgentToolFailure> {\n const createTool = tool as unknown as <I, O>(config: {\n description: string;\n inputSchema: unknown;\n execute: (input: I, options?: ToolExecutionOptions) => Promise<O>;\n }) => Tool<I, O>;\n\n return createTool<Input, string | Output | AgentToolFailure>({\n description: options.description,\n inputSchema: options.inputSchema,\n execute: async (input: Input, executeOptions?: ToolExecutionOptions) => {\n const display: AgentToolDisplayMetadata | undefined =\n options.displayName || options.icon || options.display\n ? {\n ...options.display,\n ...(options.displayName ? { name: options.displayName } : {}),\n ...(options.icon ? { icon: options.icon } : {})\n }\n : undefined;\n\n // Derive a STABLE runId from the tool call id (#1630). A tool call's id is\n // preserved in the transcript, so when a parent turn is re-run by chat\n // recovery after a deploy / eviction, the same `agentTool()` call resolves\n // to the same runId — turning the re-issue into a duplicate that\n // `runAgentTool` re-attaches to the still-running child, instead of a\n // fresh `nanoid` that spawns a brand-new child and re-runs already-\n // completed work (\"the agent went all the way back\"). Falls back to a\n // fresh id only when there is no tool call id (rare; preserves prior\n // behavior).\n const runId = executeOptions?.toolCallId\n ? `agent-tool:${executeOptions.toolCallId}`\n : undefined;\n\n const result = await currentAgentToolRunner().runAgentTool<Input, Output>(\n cls,\n {\n input,\n runId,\n parentToolCallId: executeOptions?.toolCallId,\n signal: executeOptions?.abortSignal,\n display\n }\n );\n\n if (result.status === \"completed\") {\n if (options.outputSchema) {\n if (result.output === undefined) {\n return failure(\n \"error\",\n \"agent tool completed without structured output required by outputSchema\",\n false\n );\n }\n return options.outputSchema.parse(result.output);\n }\n return result.summary ?? \"\";\n }\n\n if (result.status === \"aborted\") {\n // Intentional cancellation (parent/user stopped the run) — not retryable.\n return failure(\n \"aborted\",\n result.error ?? \"agent tool run was cancelled\",\n false\n );\n }\n if (result.status === \"interrupted\") {\n // The child was reset/superseded by a deploy or parent recovery before\n // it reached a logical outcome. Re-dispatching the run can succeed, so\n // surface it as retryable rather than a terminal failure the parent\n // would report to the user as final.\n return failure(\n \"interrupted\",\n result.error ??\n \"agent tool run was interrupted before it finished; it can be retried\",\n true\n );\n }\n return failure(\"error\", result.error ?? \"agent tool run failed\", false);\n }\n });\n}\n\nexport type { AgentToolFactoryOptions };\nexport type {\n AgentToolChildAdapter,\n AgentToolDisplayMetadata,\n AgentToolEvent,\n AgentToolEventMessage,\n AgentToolEventState,\n AgentToolFailure,\n AgentToolLifecycleResult,\n AgentToolRunInfo,\n AgentToolRunInspection,\n AgentToolRunState,\n AgentToolRunStatus,\n AgentToolStoredChunk,\n AgentToolTerminalStatus,\n ChatCapableAgentClass,\n RunAgentToolOptions,\n RunAgentToolResult\n} from \"./agent-tool-types\";\n"],"mappings":";;;AAmCA,SAAS,yBAA0C;CACjD,MAAM,QAAQA,sCAAa,SAAS,GAAG;CACvC,IACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAAqC,iBAAiB,YAE9D,MAAM,IAAI,MACR,6FACF;CAEF,OAAO;AACT;AAEA,SAAS,QACP,QACA,OACA,WACkB;CAClB,OAAO;EAAE,IAAI;EAAO;EAAQ;EAAO;CAAU;AAC/C;;;;;AAMA,SAAgB,UACd,KACA,SACiD;CAOjD,OAAOC,KAAsD;EAC3D,aAAa,QAAQ;EACrB,aAAa,QAAQ;EACrB,SAAS,OAAO,OAAc,mBAA0C;GACtE,MAAM,UACJ,QAAQ,eAAe,QAAQ,QAAQ,QAAQ,UAC3C;IACE,GAAG,QAAQ;IACX,GAAI,QAAQ,cAAc,EAAE,MAAM,QAAQ,YAAY,IAAI,CAAC;IAC3D,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;GAC/C,IACA,KAAA;GAWN,MAAM,QAAQ,gBAAgB,aAC1B,cAAc,eAAe,eAC7B,KAAA;GAEJ,MAAM,SAAS,MAAM,uBAAuB,EAAE,aAC5C,KACA;IACE;IACA;IACA,kBAAkB,gBAAgB;IAClC,QAAQ,gBAAgB;IACxB;GACF,CACF;GAEA,IAAI,OAAO,WAAW,aAAa;IACjC,IAAI,QAAQ,cAAc;KACxB,IAAI,OAAO,WAAW,KAAA,GACpB,OAAO,QACL,SACA,2EACA,KACF;KAEF,OAAO,QAAQ,aAAa,MAAM,OAAO,MAAM;IACjD;IACA,OAAO,OAAO,WAAW;GAC3B;GAEA,IAAI,OAAO,WAAW,WAEpB,OAAO,QACL,WACA,OAAO,SAAS,gCAChB,KACF;GAEF,IAAI,OAAO,WAAW,eAKpB,OAAO,QACL,eACA,OAAO,SACL,wEACF,IACF;GAEF,OAAO,QAAQ,SAAS,OAAO,SAAS,yBAAyB,KAAK;EACxE;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"agent-tools.js","names":["agentContext","createTool"],"sources":["../src/agent-tools.ts"],"sourcesContent":["import { tool, type Tool } from \"ai\";\nimport { __DO_NOT_USE_WILL_BREAK__agentContext as agentContext } from \"./internal_context\";\nimport type {\n ChatCapableAgentClass,\n RunAgentToolOptions,\n RunAgentToolResult,\n AgentToolDisplayMetadata,\n AgentToolFailure\n} from \"./agent-tool-types\";\n\ntype SchemaLike<T = unknown> = {\n parse(value: unknown): T;\n};\n\ntype AgentToolFactoryOptions<Output = unknown> = {\n description: string;\n inputSchema: unknown;\n outputSchema?: SchemaLike<Output>;\n displayName?: string;\n icon?: string;\n display?: AgentToolDisplayMetadata;\n};\n\ntype ToolExecutionOptions = {\n toolCallId?: string;\n abortSignal?: AbortSignal;\n};\n\ntype AgentToolRunner = {\n runAgentTool<Input, Output>(\n cls: ChatCapableAgentClass,\n options: RunAgentToolOptions<Input>\n ): Promise<RunAgentToolResult<Output>>;\n};\n\nfunction currentAgentToolRunner(): AgentToolRunner {\n const agent = agentContext.getStore()?.agent;\n if (\n agent === null ||\n typeof agent !== \"object\" ||\n typeof (agent as { runAgentTool?: unknown }).runAgentTool !== \"function\"\n ) {\n throw new Error(\n \"agentTool() can only run inside an Agent turn. Use it from getTools() on an Agent subclass.\"\n );\n }\n return agent as AgentToolRunner;\n}\n\nfunction failure(\n status: AgentToolFailure[\"status\"],\n error: string,\n retryable: boolean,\n extra?: Pick<AgentToolFailure, \"reason\" | \"childStillRunning\">\n): AgentToolFailure {\n return {\n ok: false,\n status,\n error,\n retryable,\n ...(extra?.reason !== undefined ? { reason: extra.reason } : {}),\n ...(extra?.childStillRunning !== undefined\n ? { childStillRunning: extra.childStillRunning }\n : {})\n };\n}\n\n/**\n * Create an AI SDK tool that dispatches a chat-capable sub-agent through\n * `Agent.runAgentTool`.\n */\nexport function agentTool<Input = unknown, Output = unknown>(\n cls: ChatCapableAgentClass,\n options: AgentToolFactoryOptions<Output>\n): Tool<Input, string | Output | AgentToolFailure> {\n const createTool = tool as unknown as <I, O>(config: {\n description: string;\n inputSchema: unknown;\n execute: (input: I, options?: ToolExecutionOptions) => Promise<O>;\n }) => Tool<I, O>;\n\n return createTool<Input, string | Output | AgentToolFailure>({\n description: options.description,\n inputSchema: options.inputSchema,\n execute: async (input: Input, executeOptions?: ToolExecutionOptions) => {\n const display: AgentToolDisplayMetadata | undefined =\n options.displayName || options.icon || options.display\n ? {\n ...options.display,\n ...(options.displayName ? { name: options.displayName } : {}),\n ...(options.icon ? { icon: options.icon } : {})\n }\n : undefined;\n\n // Derive a STABLE runId from the tool call id (#1630). A tool call's id is\n // preserved in the transcript, so when a parent turn is re-run by chat\n // recovery after a deploy / eviction, the same `agentTool()` call resolves\n // to the same runId — turning the re-issue into a duplicate that\n // `runAgentTool` re-attaches to the still-running child, instead of a\n // fresh `nanoid` that spawns a brand-new child and re-runs already-\n // completed work (\"the agent went all the way back\"). Falls back to a\n // fresh id only when there is no tool call id (rare; preserves prior\n // behavior).\n const runId = executeOptions?.toolCallId\n ? `agent-tool:${executeOptions.toolCallId}`\n : undefined;\n\n const result = await currentAgentToolRunner().runAgentTool<Input, Output>(\n cls,\n {\n input,\n runId,\n parentToolCallId: executeOptions?.toolCallId,\n signal: executeOptions?.abortSignal,\n display\n }\n );\n\n if (result.status === \"completed\") {\n if (options.outputSchema) {\n if (result.output === undefined) {\n return failure(\n \"error\",\n \"agent tool completed without structured output required by outputSchema\",\n false\n );\n }\n return options.outputSchema.parse(result.output);\n }\n return result.summary ?? \"\";\n }\n\n if (result.status === \"aborted\") {\n // Intentional cancellation (parent/user stopped the run) — not retryable.\n return failure(\n \"aborted\",\n result.error ?? \"agent tool run was cancelled\",\n false\n );\n }\n if (result.status === \"interrupted\") {\n // The child was reset/superseded by a deploy or parent recovery before\n // it reached a logical outcome. Re-dispatching the run can succeed, so\n // surface it as retryable rather than a terminal failure the parent\n // would report to the user as final. `retryable` is intentionally COARSE\n // (always true for `interrupted`); callers that want to distinguish a\n // self-healing child from an exhausted one branch on `reason` /\n // `childStillRunning` instead.\n return failure(\n \"interrupted\",\n result.error ??\n \"agent tool run was interrupted before it finished; it can be retried\",\n true,\n { reason: result.reason, childStillRunning: result.childStillRunning }\n );\n }\n return failure(\"error\", result.error ?? \"agent tool run failed\", false);\n }\n });\n}\n\nexport type { AgentToolFactoryOptions };\nexport type {\n AgentToolChildAdapter,\n AgentToolDisplayMetadata,\n AgentToolEvent,\n AgentToolEventMessage,\n AgentToolEventState,\n AgentToolFailure,\n AgentToolInterruptedReason,\n AgentToolLifecycleResult,\n AgentToolRunInfo,\n AgentToolRunInspection,\n AgentToolRunState,\n AgentToolRunStatus,\n AgentToolStoredChunk,\n AgentToolTerminalStatus,\n ChatCapableAgentClass,\n RunAgentToolOptions,\n RunAgentToolResult\n} from \"./agent-tool-types\";\n"],"mappings":";;;AAmCA,SAAS,yBAA0C;CACjD,MAAM,QAAQA,sCAAa,SAAS,CAAC,EAAE;CACvC,IACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAAqC,iBAAiB,YAE9D,MAAM,IAAI,MACR,6FACF;CAEF,OAAO;AACT;AAEA,SAAS,QACP,QACA,OACA,WACA,OACkB;CAClB,OAAO;EACL,IAAI;EACJ;EACA;EACA;EACA,GAAI,OAAO,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;EAC9D,GAAI,OAAO,sBAAsB,KAAA,IAC7B,EAAE,mBAAmB,MAAM,kBAAkB,IAC7C,CAAC;CACP;AACF;;;;;AAMA,SAAgB,UACd,KACA,SACiD;CAOjD,OAAOC,KAAsD;EAC3D,aAAa,QAAQ;EACrB,aAAa,QAAQ;EACrB,SAAS,OAAO,OAAc,mBAA0C;GACtE,MAAM,UACJ,QAAQ,eAAe,QAAQ,QAAQ,QAAQ,UAC3C;IACE,GAAG,QAAQ;IACX,GAAI,QAAQ,cAAc,EAAE,MAAM,QAAQ,YAAY,IAAI,CAAC;IAC3D,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;GAC/C,IACA,KAAA;GAWN,MAAM,QAAQ,gBAAgB,aAC1B,cAAc,eAAe,eAC7B,KAAA;GAEJ,MAAM,SAAS,MAAM,uBAAuB,CAAC,CAAC,aAC5C,KACA;IACE;IACA;IACA,kBAAkB,gBAAgB;IAClC,QAAQ,gBAAgB;IACxB;GACF,CACF;GAEA,IAAI,OAAO,WAAW,aAAa;IACjC,IAAI,QAAQ,cAAc;KACxB,IAAI,OAAO,WAAW,KAAA,GACpB,OAAO,QACL,SACA,2EACA,KACF;KAEF,OAAO,QAAQ,aAAa,MAAM,OAAO,MAAM;IACjD;IACA,OAAO,OAAO,WAAW;GAC3B;GAEA,IAAI,OAAO,WAAW,WAEpB,OAAO,QACL,WACA,OAAO,SAAS,gCAChB,KACF;GAEF,IAAI,OAAO,WAAW,eAQpB,OAAO,QACL,eACA,OAAO,SACL,wEACF,MACA;IAAE,QAAQ,OAAO;IAAQ,mBAAmB,OAAO;GAAkB,CACvE;GAEF,OAAO,QAAQ,SAAS,OAAO,SAAS,yBAAyB,KAAK;EACxE;CACF,CAAC;AACH"}
@@ -1,4 +1,4 @@
1
- import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-BIpUk4G5.js";
1
+ import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-wyII629d.js";
2
2
  import { tool } from "ai";
3
3
  import { z } from "zod";
4
4
  //#region src/browser/ai.ts
@@ -1 +1 @@
1
- {"version":3,"file":"ai.js","names":[],"sources":["../../src/browser/ai.ts"],"sourcesContent":["import { tool } from \"ai\";\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport {\n createBrowserToolHandlers,\n SEARCH_DESCRIPTION,\n EXECUTE_DESCRIPTION,\n type BrowserToolsOptions\n} from \"./shared\";\n\nexport type { BrowserToolsOptions } from \"./shared\";\n\n/**\n * Create AI SDK tools for browser automation via CDP code mode.\n *\n * Returns a `ToolSet` with `search` (query the CDP spec) and\n * `execute` (run CDP commands against a live browser).\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/ai\";\n * import { generateText } from \"ai\";\n *\n * const browserTools = createBrowserTools({\n * browser: env.BROWSER,\n * loader: env.LOADER,\n * });\n *\n * const result = await generateText({\n * model,\n * tools: { ...browserTools, ...otherTools },\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(options: BrowserToolsOptions): ToolSet {\n const handlers = createBrowserToolHandlers(options);\n\n return {\n browser_search: tool({\n description: SEARCH_DESCRIPTION,\n inputSchema: z.object({\n code: z\n .string()\n .describe(\"JavaScript async arrow function that queries the CDP spec\")\n }),\n execute: async ({ code }) => {\n const result = await handlers.search(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return result.text;\n }\n }),\n\n browser_execute: tool({\n description: EXECUTE_DESCRIPTION,\n inputSchema: z.object({\n code: z\n .string()\n .describe(\"JavaScript async arrow function that uses the cdp helper\")\n }),\n execute: async ({ code }) => {\n const result = await handlers.execute(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return result.text;\n }\n })\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,SAAuC;CACxE,MAAM,WAAW,0BAA0B,OAAO;CAElD,OAAO;EACL,gBAAgB,KAAK;GACnB,aAAa;GACb,aAAa,EAAE,OAAO,EACpB,MAAM,EACH,OAAO,EACP,SAAS,2DAA2D,EACzE,CAAC;GACD,SAAS,OAAO,EAAE,WAAW;IAC3B,MAAM,SAAS,MAAM,SAAS,OAAO,IAAI;IACzC,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;IAE7B,OAAO,OAAO;GAChB;EACF,CAAC;EAED,iBAAiB,KAAK;GACpB,aAAa;GACb,aAAa,EAAE,OAAO,EACpB,MAAM,EACH,OAAO,EACP,SAAS,0DAA0D,EACxE,CAAC;GACD,SAAS,OAAO,EAAE,WAAW;IAC3B,MAAM,SAAS,MAAM,SAAS,QAAQ,IAAI;IAC1C,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;IAE7B,OAAO,OAAO;GAChB;EACF,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"ai.js","names":[],"sources":["../../src/browser/ai.ts"],"sourcesContent":["import { tool } from \"ai\";\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport {\n createBrowserToolHandlers,\n SEARCH_DESCRIPTION,\n EXECUTE_DESCRIPTION,\n type BrowserToolsOptions\n} from \"./shared\";\n\nexport type { BrowserToolsOptions } from \"./shared\";\n\n/**\n * Create AI SDK tools for browser automation via CDP code mode.\n *\n * Returns a `ToolSet` with `search` (query the CDP spec) and\n * `execute` (run CDP commands against a live browser).\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/ai\";\n * import { generateText } from \"ai\";\n *\n * const browserTools = createBrowserTools({\n * browser: env.BROWSER,\n * loader: env.LOADER,\n * });\n *\n * const result = await generateText({\n * model,\n * tools: { ...browserTools, ...otherTools },\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(options: BrowserToolsOptions): ToolSet {\n const handlers = createBrowserToolHandlers(options);\n\n return {\n browser_search: tool({\n description: SEARCH_DESCRIPTION,\n inputSchema: z.object({\n code: z\n .string()\n .describe(\"JavaScript async arrow function that queries the CDP spec\")\n }),\n execute: async ({ code }) => {\n const result = await handlers.search(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return result.text;\n }\n }),\n\n browser_execute: tool({\n description: EXECUTE_DESCRIPTION,\n inputSchema: z.object({\n code: z\n .string()\n .describe(\"JavaScript async arrow function that uses the cdp helper\")\n }),\n execute: async ({ code }) => {\n const result = await handlers.execute(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return result.text;\n }\n })\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,SAAuC;CACxE,MAAM,WAAW,0BAA0B,OAAO;CAElD,OAAO;EACL,gBAAgB,KAAK;GACnB,aAAa;GACb,aAAa,EAAE,OAAO,EACpB,MAAM,EACH,OAAO,CAAC,CACR,SAAS,2DAA2D,EACzE,CAAC;GACD,SAAS,OAAO,EAAE,WAAW;IAC3B,MAAM,SAAS,MAAM,SAAS,OAAO,IAAI;IACzC,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;IAE7B,OAAO,OAAO;GAChB;EACF,CAAC;EAED,iBAAiB,KAAK;GACpB,aAAa;GACb,aAAa,EAAE,OAAO,EACpB,MAAM,EACH,OAAO,CAAC,CACR,SAAS,0DAA0D,EACxE,CAAC;GACD,SAAS,OAAO,EAAE,WAAW;IAC3B,MAAM,SAAS,MAAM,SAAS,QAAQ,IAAI;IAC1C,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;IAE7B,OAAO,OAAO;GAChB;EACF,CAAC;CACH;AACF"}
@@ -1,2 +1,2 @@
1
- import { a as connectBrowser, i as CdpSession, n as SEARCH_DESCRIPTION, o as connectUrl, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-BIpUk4G5.js";
1
+ import { a as connectBrowser, i as CdpSession, n as SEARCH_DESCRIPTION, o as connectUrl, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-wyII629d.js";
2
2
  export { CdpSession, EXECUTE_DESCRIPTION, SEARCH_DESCRIPTION, connectBrowser, connectUrl, createBrowserToolHandlers };
@@ -1,4 +1,4 @@
1
- import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-BIpUk4G5.js";
1
+ import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-wyII629d.js";
2
2
  import { z } from "zod";
3
3
  import { toolDefinition } from "@tanstack/ai";
4
4
  //#region src/browser/tanstack-ai.ts
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../src/browser/tanstack-ai.ts"],"sourcesContent":["import { toolDefinition } from \"@tanstack/ai\";\nimport type { ServerTool } from \"@tanstack/ai\";\nimport { z } from \"zod\";\nimport {\n createBrowserToolHandlers,\n SEARCH_DESCRIPTION,\n EXECUTE_DESCRIPTION,\n type BrowserToolsOptions\n} from \"./shared\";\n\nexport type { BrowserToolsOptions } from \"./shared\";\n\n/**\n * Create TanStack AI tools for browser automation via CDP code mode.\n *\n * Returns an array of `ServerTool`s: `browser_search` (query the CDP spec)\n * and `browser_execute` (run CDP commands against a live browser).\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * const browserTools = createBrowserTools({\n * browser: env.BROWSER,\n * loader: env.LOADER,\n * });\n *\n * const stream = chat({\n * adapter: openaiText(\"gpt-4o\"),\n * tools: [...browserTools, ...otherTools],\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(options: BrowserToolsOptions): ServerTool[] {\n const handlers = createBrowserToolHandlers(options);\n\n const search = toolDefinition({\n name: \"browser_search\" as const,\n description: SEARCH_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that queries the CDP spec\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.search(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n const execute = toolDefinition({\n name: \"browser_execute\" as const,\n description: EXECUTE_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that uses the cdp helper\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.execute(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n return [search, execute];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,SAA4C;CAC7E,MAAM,WAAW,0BAA0B,OAAO;CAkClD,OAAO,CAhCQ,eAAe;EAC5B,MAAM;EACN,aAAa;EACb,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,EAAE,KAAK,EACpB,aAAa,4DACf,CAAC,EACH,CAAC;CACH,CAAC,EAAE,OAAO,OAAO,EAAE,WAAW;EAC5B,MAAM,SAAS,MAAM,SAAS,OAAO,IAAI;EACzC,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;EAE7B,OAAO,EAAE,MAAM,OAAO,KAAK;CAC7B,CAkBa,GAhBG,eAAe;EAC7B,MAAM;EACN,aAAa;EACb,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,EAAE,KAAK,EACpB,aAAa,2DACf,CAAC,EACH,CAAC;CACH,CAAC,EAAE,OAAO,OAAO,EAAE,WAAW;EAC5B,MAAM,SAAS,MAAM,SAAS,QAAQ,IAAI;EAC1C,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;EAE7B,OAAO,EAAE,MAAM,OAAO,KAAK;CAC7B,CAEsB,CAAC;AACzB"}
1
+ {"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../src/browser/tanstack-ai.ts"],"sourcesContent":["import { toolDefinition } from \"@tanstack/ai\";\nimport type { ServerTool } from \"@tanstack/ai\";\nimport { z } from \"zod\";\nimport {\n createBrowserToolHandlers,\n SEARCH_DESCRIPTION,\n EXECUTE_DESCRIPTION,\n type BrowserToolsOptions\n} from \"./shared\";\n\nexport type { BrowserToolsOptions } from \"./shared\";\n\n/**\n * Create TanStack AI tools for browser automation via CDP code mode.\n *\n * Returns an array of `ServerTool`s: `browser_search` (query the CDP spec)\n * and `browser_execute` (run CDP commands against a live browser).\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * const browserTools = createBrowserTools({\n * browser: env.BROWSER,\n * loader: env.LOADER,\n * });\n *\n * const stream = chat({\n * adapter: openaiText(\"gpt-4o\"),\n * tools: [...browserTools, ...otherTools],\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(options: BrowserToolsOptions): ServerTool[] {\n const handlers = createBrowserToolHandlers(options);\n\n const search = toolDefinition({\n name: \"browser_search\" as const,\n description: SEARCH_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that queries the CDP spec\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.search(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n const execute = toolDefinition({\n name: \"browser_execute\" as const,\n description: EXECUTE_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that uses the cdp helper\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.execute(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n return [search, execute];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,SAA4C;CAC7E,MAAM,WAAW,0BAA0B,OAAO;CAkClD,OAAO,CAhCQ,eAAe;EAC5B,MAAM;EACN,aAAa;EACb,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,EACpB,aAAa,4DACf,CAAC,EACH,CAAC;CACH,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,WAAW;EAC5B,MAAM,SAAS,MAAM,SAAS,OAAO,IAAI;EACzC,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;EAE7B,OAAO,EAAE,MAAM,OAAO,KAAK;CAC7B,CAkBa,GAhBG,eAAe;EAC7B,MAAM;EACN,aAAa;EACb,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,EACpB,aAAa,2DACf,CAAC,EACH,CAAC;CACH,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,WAAW;EAC5B,MAAM,SAAS,MAAM,SAAS,QAAQ,IAAI;EAC1C,IAAI,OAAO,SACT,MAAM,IAAI,MAAM,OAAO,IAAI;EAE7B,OAAO,EAAE,MAAM,OAAO,KAAK;CAC7B,CAEsB,CAAC;AACzB"}
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  a as AgentToolEventState,
3
+ d as AgentToolRunState,
3
4
  i as AgentToolEventMessage,
4
- r as AgentToolEvent,
5
- u as AgentToolRunState
6
- } from "../agent-tool-types-BAJWu8s4.js";
5
+ r as AgentToolEvent
6
+ } from "../agent-tool-types-V25Z_HcX.js";
7
7
  import {
8
8
  n as createAgentToolEventState,
9
9
  t as applyAgentToolEvent
10
- } from "../agent-tools-0R6KEert.js";
10
+ } from "../agent-tools-C-9s151X.js";
11
11
  import { JSONSchema7, Tool, ToolSet, UIMessage } from "ai";
12
12
  import { Connection } from "agents";
13
13
 
@@ -421,18 +421,56 @@ type ChatRecoveryExhaustedContext = Pick<
421
421
  * Why recovery stopped. One of:
422
422
  * - `max_attempts_exceeded` — the per-incident attempt budget was spent.
423
423
  * - `no_progress_timeout` — no forward progress within the no-progress window.
424
- * - `max_recovery_window_exceeded` — the absolute incident-age ceiling was hit.
424
+ * - `work_budget_exceeded` — the turn kept producing content but exceeded the
425
+ * configured `maxRecoveryWork` runaway-loop budget.
426
+ * - `recovery_aborted` — the caller's `shouldKeepRecovering` hook returned `false`.
425
427
  * - `stable_timeout` — a recovery attempt kept timing out waiting for the
426
428
  * isolate to reach stable state until the budget drained (extreme churn).
429
+ * - `max_recovery_window_exceeded` — DEPRECATED. The old absolute incident-age
430
+ * ceiling. No longer emitted (a progressing turn is no longer bounded by
431
+ * wall-clock); retained only for back-compat with persisted incidents.
427
432
  *
428
433
  * Treat this as an open string: new reasons may be added.
429
434
  */
430
435
  reason: string /** The terminal message shown to the user (from the `chatRecovery` config). */;
431
436
  terminalMessage: string;
432
437
  };
438
+ /**
439
+ * Context passed to the `shouldKeepRecovering` recovery predicate on each
440
+ * attempt. Lets an integrator impose a runaway-loop guard expressed as their
441
+ * own budget (steps / tool-calls / tokens / cost) rather than wall-clock
442
+ * duration. `ctx.work` is the SDK's coarse progress signal; map it (or your own
443
+ * accounting) onto whatever budget you enforce.
444
+ */
445
+ type ChatRecoveryProgressContext = {
446
+ incidentId: string;
447
+ requestId: string;
448
+ recoveryRootRequestId: string;
449
+ attempt: number;
450
+ maxAttempts: number;
451
+ recoveryKind: "retry" | "continue";
452
+ /**
453
+ * Recovery work units produced since this incident began — a durable,
454
+ * monotonic, reconnect-immune count of produced content/tool segments (not
455
+ * tokens). The signal that distinguishes a healthy long turn from a runaway
456
+ * loop.
457
+ */
458
+ work: number /** Wall-clock ms since the incident's first interruption. */;
459
+ ageMs: number;
460
+ };
433
461
  /**
434
462
  * Configuration for durable chat recovery. `true` uses these defaults:
435
- * `maxAttempts: 6`, `stableTimeoutMs: 10_000`, and a generic terminal message.
463
+ * `maxAttempts: 10`, `stableTimeoutMs: 10_000`, `noProgressTimeoutMs: 300_000`
464
+ * (5 min), `maxRecoveryWork: Infinity`, and a generic terminal message.
465
+ *
466
+ * **Apply this as a class field or in the constructor — never assign it in
467
+ * `onStart()`.** On every wake the SDK evaluates recovery budgets (and may seal
468
+ * an interrupted turn, firing `onExhausted`) BEFORE your `onStart()` body runs.
469
+ * A config produced inside `onStart()` is therefore read as the built-in
470
+ * defaults at the moment recovery decides, so your budgets / `shouldKeepRecovering`
471
+ * / `onExhausted` silently never apply to the recovery that matters. The SDK
472
+ * logs a one-time warning if it detects `chatRecovery` being assigned during
473
+ * `onStart()`.
436
474
  */
437
475
  type ChatRecoveryConfig =
438
476
  | boolean
@@ -440,6 +478,41 @@ type ChatRecoveryConfig =
440
478
  maxAttempts?: number;
441
479
  stableTimeoutMs?: number;
442
480
  terminalMessage?: string;
481
+ /**
482
+ * How long an incident may go WITHOUT forward progress before it is
483
+ * sealed with `reason="no_progress_timeout"`. This is the primary
484
+ * stuck-turn bound. It **resets on every progress-bearing attempt**, so a
485
+ * turn that keeps producing content survives unbounded interruption while
486
+ * a genuinely idle turn is sealed within the window. Defaults to 5 min.
487
+ */
488
+ noProgressTimeoutMs?: number;
489
+ /**
490
+ * Runaway-loop guard. Maximum recovery WORK — produced content/tool units
491
+ * since the incident began — before a still-progressing turn is sealed
492
+ * with `reason="work_budget_exceeded"`. Defaults to `Infinity` (no cap):
493
+ * the SDK never terminates a progressing turn on its own. Set a finite
494
+ * value (or use `shouldKeepRecovering`) to bound a loop that keeps
495
+ * emitting content but never converges.
496
+ */
497
+ maxRecoveryWork?: number;
498
+ /**
499
+ * Caller policy consulted on each recovery attempt from the second
500
+ * onward — it is NOT called on the first detection (the attempt that
501
+ * opens the incident), and not at all once a hard bound (no-progress
502
+ * timeout, attempt cap, or `maxRecoveryWork`) has already sealed the
503
+ * incident. Return `false` to stop recovery with
504
+ * `reason="recovery_aborted"`; return `true` (or omit the hook) to keep
505
+ * recovering. A throwing hook is logged and treated as "keep recovering"
506
+ * so a buggy predicate cannot wedge a turn.
507
+ *
508
+ * This is the hook point for a token/cost/step budget, but note
509
+ * `ctx.work` is a coarse count of produced content/tool segments, not
510
+ * tokens — track real token/cost yourself (keyed by
511
+ * `ctx.recoveryRootRequestId`) and consult it here.
512
+ */
513
+ shouldKeepRecovering?(
514
+ ctx: ChatRecoveryProgressContext
515
+ ): boolean | Promise<boolean>;
443
516
  onExhausted?(ctx: ChatRecoveryExhaustedContext): void | Promise<void>;
444
517
  };
445
518
  type ResolvedChatRecoveryConfig = {
@@ -447,6 +520,11 @@ type ResolvedChatRecoveryConfig = {
447
520
  maxAttempts: number;
448
521
  stableTimeoutMs: number;
449
522
  terminalMessage: string;
523
+ noProgressTimeoutMs: number;
524
+ maxRecoveryWork: number;
525
+ shouldKeepRecovering?: (
526
+ ctx: ChatRecoveryProgressContext
527
+ ) => boolean | Promise<boolean>;
450
528
  onExhausted?: (ctx: ChatRecoveryExhaustedContext) => void | Promise<void>;
451
529
  };
452
530
  /**
@@ -585,7 +663,8 @@ declare class ResumableStream {
585
663
  private sql;
586
664
  private _activeStreamId;
587
665
  private _activeRequestId;
588
- private _streamChunkIndex;
666
+ /** Monotonic row-ordering index; one increment per flushed segment row. */
667
+ private _segmentIndex;
589
668
  /**
590
669
  * Whether the active stream was started in this instance (true) or
591
670
  * restored from SQLite after hibernation/restart (false). An orphaned
@@ -594,6 +673,7 @@ declare class ResumableStream {
594
673
  */
595
674
  private _isLive;
596
675
  private _chunkBuffer;
676
+ private _chunkBufferBytes;
597
677
  private _isFlushingChunks;
598
678
  private _lastCleanupTime;
599
679
  constructor(sql: SqlTaggedTemplate);
@@ -634,8 +714,13 @@ declare class ResumableStream {
634
714
  */
635
715
  storeChunk(streamId: string, body: string): void;
636
716
  /**
637
- * Flush buffered chunks to SQLite in a single batch.
717
+ * Flush the buffered chunks to SQLite as a single packed row.
638
718
  * Uses a lock to prevent concurrent flush operations.
719
+ *
720
+ * The whole buffer becomes one row: a single-chunk segment is stored
721
+ * unwrapped (legacy object format) so a large chunk avoids array-escaping
722
+ * inflation, while a multi-chunk segment stores a JSON array of bodies. This
723
+ * collapses N chunk rows into one, cutting rows written / stored / scanned.
639
724
  */
640
725
  flushBuffer(): void;
641
726
  /**
@@ -679,7 +764,12 @@ declare class ResumableStream {
679
764
  */
680
765
  destroy(): void;
681
766
  private _maybeCleanupOldStreams;
682
- /** @internal For testing only */
767
+ /**
768
+ * Return the stored chunks for a stream as individual chunk bodies in order,
769
+ * unpacking packed segment rows. The returned `chunk_index` is a running
770
+ * per-chunk sequence (0, 1, 2, …) — stable across calls because rows are
771
+ * append-only — so callers can use it as a monotonic chunk sequence.
772
+ */
683
773
  getStreamChunks(streamId: string): Array<{
684
774
  body: string;
685
775
  chunk_index: number;
@@ -700,6 +790,31 @@ declare class ResumableStream {
700
790
  insertStaleStream(streamId: string, requestId: string, ageMs: number): void;
701
791
  }
702
792
  //#endregion
793
+ //#region src/chat/sql-batch.d.ts
794
+ /**
795
+ * Helpers for building batched SQLite statements that run through the Agent's
796
+ * `sql` tagged template (which interleaves a `?` placeholder between every
797
+ * string fragment). Used to collapse per-row INSERT/DELETE loops into a small
798
+ * number of multi-row statements.
799
+ *
800
+ * SQLite (Durable Object / D1) caps bound parameters at 100 per query, so
801
+ * callers must chunk their inputs to stay within {@link MAX_BOUND_PARAMS}.
802
+ * See https://developers.cloudflare.com/d1/platform/limits/
803
+ */
804
+ /** Maximum bound parameters allowed in a single SQLite (DO / D1) query. */
805
+ declare const MAX_BOUND_PARAMS = 100;
806
+ /**
807
+ * Build a TemplateStringsArray for a single-column `IN (...)` clause. Produces
808
+ * fragments for:
809
+ * `${prefix}(?, ?, ...)`
810
+ *
811
+ * @throws if `count` is less than 1.
812
+ */
813
+ declare function buildInClauseStrings(
814
+ prefix: string,
815
+ count: number
816
+ ): TemplateStringsArray;
817
+ //#endregion
703
818
  //#region src/chat/protocol.d.ts
704
819
  /**
705
820
  * Wire protocol message type constants for the cf_agent_chat_* protocol.
@@ -830,8 +945,12 @@ declare class AbortRegistry {
830
945
  cancel(id: string, reason?: unknown): void;
831
946
  /** Remove a controller after the request completes. */
832
947
  remove(id: string): void;
833
- /** Abort all pending requests and clear the registry. */
834
- destroyAll(): void;
948
+ /**
949
+ * Abort all pending requests and clear the registry. Optionally propagate a
950
+ * reason — surfaces as `signal.reason` on each controller and through any
951
+ * `AbortError` it produces downstream, exactly like {@link cancel}.
952
+ */
953
+ destroyAll(reason?: unknown): void;
835
954
  /** Check if a controller exists for the given ID. */
836
955
  has(id: string): boolean;
837
956
  /** Number of tracked controllers. */
@@ -1136,6 +1255,7 @@ export {
1136
1255
  type ChatRecoveryContext,
1137
1256
  type ChatRecoveryExhaustedContext,
1138
1257
  type ChatRecoveryOptions,
1258
+ type ChatRecoveryProgressContext,
1139
1259
  type ChatResponseResult,
1140
1260
  type ChunkAction,
1141
1261
  type ChunkResult,
@@ -1145,6 +1265,7 @@ export {
1145
1265
  type ContinuationPending,
1146
1266
  ContinuationState,
1147
1267
  type EnqueueOptions,
1268
+ MAX_BOUND_PARAMS,
1148
1269
  type MessageConcurrency,
1149
1270
  type MessagePart,
1150
1271
  type MessageParts,
@@ -1168,6 +1289,7 @@ export {
1168
1289
  applyToolUpdate,
1169
1290
  assistantContentKey,
1170
1291
  transition as broadcastTransition,
1292
+ buildInClauseStrings,
1171
1293
  byteLength,
1172
1294
  createAgentToolEventState,
1173
1295
  createChatFiberSnapshot,