@tanstack/ai 0.59.0 → 0.63.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 (123) hide show
  1. package/README.md +1 -0
  2. package/dist/esm/activities/chat/adapter.d.ts +9 -0
  3. package/dist/esm/activities/chat/adapter.js +1 -0
  4. package/dist/esm/activities/chat/adapter.js.map +1 -1
  5. package/dist/esm/activities/chat/agents/define-agent.d.ts +17 -5
  6. package/dist/esm/activities/chat/agents/define-agent.js.map +1 -1
  7. package/dist/esm/activities/chat/agents/spawn.d.ts +2 -0
  8. package/dist/esm/activities/chat/agents/spawn.js +8 -5
  9. package/dist/esm/activities/chat/agents/spawn.js.map +1 -1
  10. package/dist/esm/activities/chat/index.js +289 -78
  11. package/dist/esm/activities/chat/index.js.map +1 -1
  12. package/dist/esm/activities/chat/messages.js +35 -19
  13. package/dist/esm/activities/chat/messages.js.map +1 -1
  14. package/dist/esm/activities/chat/middleware/types.d.ts +1 -0
  15. package/dist/esm/activities/chat/middleware/types.js.map +1 -1
  16. package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
  17. package/dist/esm/activities/chat/stream/message-updaters.js +11 -3
  18. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  19. package/dist/esm/activities/chat/stream/processor.d.ts +11 -10
  20. package/dist/esm/activities/chat/stream/processor.js +51 -28
  21. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  22. package/dist/esm/activities/chat/tools/tool-calls.d.ts +16 -2
  23. package/dist/esm/activities/chat/tools/tool-calls.js +57 -16
  24. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  25. package/dist/esm/activities/chat/tools/tool-definition.d.ts +4 -0
  26. package/dist/esm/activities/chat/tools/tool-definition.js +4 -0
  27. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  28. package/dist/esm/activities/embed/adapter.d.ts +7 -0
  29. package/dist/esm/activities/embed/adapter.js +1 -0
  30. package/dist/esm/activities/embed/adapter.js.map +1 -1
  31. package/dist/esm/activities/embed/index.js +2 -0
  32. package/dist/esm/activities/embed/index.js.map +1 -1
  33. package/dist/esm/activities/evaluate/adapter.d.ts +4 -0
  34. package/dist/esm/activities/evaluate/adapter.js.map +1 -1
  35. package/dist/esm/activities/evaluate/index.d.ts +4 -0
  36. package/dist/esm/activities/evaluate/index.js +3 -1
  37. package/dist/esm/activities/evaluate/index.js.map +1 -1
  38. package/dist/esm/activities/files/adapter.d.ts +97 -0
  39. package/dist/esm/activities/files/adapter.js +45 -0
  40. package/dist/esm/activities/files/adapter.js.map +1 -0
  41. package/dist/esm/activities/files/index.d.ts +66 -0
  42. package/dist/esm/activities/files/index.js +78 -0
  43. package/dist/esm/activities/files/index.js.map +1 -0
  44. package/dist/esm/activities/generateImage/adapter.d.ts +8 -0
  45. package/dist/esm/activities/generateImage/adapter.js +1 -0
  46. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  47. package/dist/esm/activities/generateImage/index.js +2 -0
  48. package/dist/esm/activities/generateImage/index.js.map +1 -1
  49. package/dist/esm/activities/generateVideo/adapter.d.ts +8 -0
  50. package/dist/esm/activities/generateVideo/adapter.js +1 -0
  51. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  52. package/dist/esm/activities/generateVideo/index.js +3 -0
  53. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  54. package/dist/esm/activities/generateWorld/adapter.d.ts +4 -2
  55. package/dist/esm/activities/generateWorld/adapter.js.map +1 -1
  56. package/dist/esm/activities/generateWorld/index.d.ts +4 -3
  57. package/dist/esm/activities/generateWorld/index.js +5 -4
  58. package/dist/esm/activities/generateWorld/index.js.map +1 -1
  59. package/dist/esm/activities/index.d.ts +6 -3
  60. package/dist/esm/activities/index.js +13 -11
  61. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +2 -0
  62. package/dist/esm/activities/summarize/chat-stream-summarize.js +8 -8
  63. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  64. package/dist/esm/client.d.ts +3 -1
  65. package/dist/esm/client.js +2 -1
  66. package/dist/esm/client.js.map +1 -1
  67. package/dist/esm/index.d.ts +4 -3
  68. package/dist/esm/index.js +5 -3
  69. package/dist/esm/interrupt-resume.js +29 -4
  70. package/dist/esm/interrupt-resume.js.map +1 -1
  71. package/dist/esm/middlewares/otel.d.ts +5 -2
  72. package/dist/esm/middlewares/otel.js +114 -0
  73. package/dist/esm/middlewares/otel.js.map +1 -1
  74. package/dist/esm/types.d.ts +114 -14
  75. package/dist/esm/utilities/ag-ui-wire.js +39 -16
  76. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  77. package/dist/esm/utilities/content-source.d.ts +60 -0
  78. package/dist/esm/utilities/content-source.js +85 -0
  79. package/dist/esm/utilities/content-source.js.map +1 -0
  80. package/dist/esm/utilities/provider-executed.d.ts +7 -0
  81. package/dist/esm/utilities/provider-executed.js +10 -1
  82. package/dist/esm/utilities/provider-executed.js.map +1 -1
  83. package/dist/esm/utilities/tool-result.d.ts +14 -3
  84. package/dist/esm/utilities/tool-result.js +26 -3
  85. package/dist/esm/utilities/tool-result.js.map +1 -1
  86. package/package.json +4 -4
  87. package/skills/ai-core/adapter-configuration/SKILL.md +62 -0
  88. package/skills/ai-core/chat-experience/SKILL.md +134 -0
  89. package/skills/ai-core/media-generation/SKILL.md +8 -0
  90. package/skills/ai-core/tool-calling/SKILL.md +103 -0
  91. package/src/activities/chat/adapter.ts +10 -0
  92. package/src/activities/chat/agents/define-agent.ts +20 -3
  93. package/src/activities/chat/agents/spawn.ts +20 -12
  94. package/src/activities/chat/index.ts +458 -99
  95. package/src/activities/chat/messages.ts +52 -6
  96. package/src/activities/chat/middleware/types.ts +1 -0
  97. package/src/activities/chat/stream/message-updaters.ts +27 -2
  98. package/src/activities/chat/stream/processor.ts +81 -49
  99. package/src/activities/chat/tools/tool-calls.ts +104 -9
  100. package/src/activities/chat/tools/tool-definition.ts +8 -0
  101. package/src/activities/embed/adapter.ts +7 -0
  102. package/src/activities/embed/index.ts +5 -0
  103. package/src/activities/evaluate/adapter.ts +4 -0
  104. package/src/activities/evaluate/index.ts +6 -0
  105. package/src/activities/files/adapter.ts +120 -0
  106. package/src/activities/files/index.ts +113 -0
  107. package/src/activities/generateImage/adapter.ts +8 -0
  108. package/src/activities/generateImage/index.ts +4 -0
  109. package/src/activities/generateVideo/adapter.ts +8 -0
  110. package/src/activities/generateVideo/index.ts +7 -0
  111. package/src/activities/generateWorld/adapter.ts +4 -2
  112. package/src/activities/generateWorld/index.ts +7 -6
  113. package/src/activities/index.ts +25 -1
  114. package/src/activities/summarize/chat-stream-summarize.ts +22 -12
  115. package/src/client.ts +8 -0
  116. package/src/index.ts +17 -0
  117. package/src/interrupt-resume.ts +55 -4
  118. package/src/middlewares/otel.ts +161 -3
  119. package/src/types.ts +114 -14
  120. package/src/utilities/ag-ui-wire.ts +72 -17
  121. package/src/utilities/content-source.ts +138 -0
  122. package/src/utilities/provider-executed.ts +13 -0
  123. package/src/utilities/tool-result.ts +45 -3
@@ -1 +1 @@
1
- {"version":3,"file":"interrupt-resume.js","names":[],"sources":["../../src/interrupt-resume.ts"],"sourcesContent":["import {\n INTERRUPT_BINDING_VERSION,\n canonicalizeInterruptResolutions,\n} from './interrupts'\nimport {\n canonicalInterruptJson,\n digestInterruptJson,\n} from './interrupt-serialization'\nimport {\n hashSchemaInput,\n normalizeApprovalSchema,\n} from './activities/chat/tools/approval-schema'\nimport {\n isStandardSchema,\n validateWithStandardSchema,\n} from './activities/chat/tools/schema-converter'\nimport type {\n InterruptBinding,\n InterruptSubmissionError,\n ItemInterruptErrorCode,\n ToolApprovalResolution,\n UnopenedInterruptBinding,\n} from './interrupts'\nimport type {\n ChatMiddlewareConfig,\n ChatResumeToolState,\n} from './activities/chat/middleware/types'\nimport type {\n GenericInterruptRequest,\n InterruptDefinition,\n} from './interrupt-definition'\nimport type { Interrupt, RunAgentResumeItem } from './types'\n\n/**\n * The `Interrupt.metadata` key under which this package's resume binding\n * travels.\n *\n * Exported so anything that produces an interrupt this package must later\n * resume — an application middleware raising a generic pause, a future\n * workflow-to-AG-UI projection — attaches the binding through\n * {@link withInterruptBinding} rather than copying the string. Everything\n * outside this key is the plain AG-UI envelope and is left untouched.\n */\nexport const INTERRUPT_BINDING_METADATA_KEY = 'tanstack:interruptBinding'\n\nconst interruptBindingMetadataKey = INTERRUPT_BINDING_METADATA_KEY\n\n/** The persistence-neutral shape required to validate an interrupt resume. */\nexport interface PendingInterruptResumeRecord {\n interruptId: string\n payload: unknown\n binding: InterruptBinding\n /** Present for a first-party generic interrupt. */\n genericRequest?: GenericInterruptRequest<\n InterruptDefinition<any, any, any, any>\n >\n}\n\nexport interface ValidateInterruptResumeBatchInput {\n threadId: string\n interruptedRunId: string\n generation: number\n pending: ReadonlyArray<PendingInterruptResumeRecord>\n resume?: ReadonlyArray<RunAgentResumeItem>\n tools: ChatMiddlewareConfig['tools']\n now?: number\n}\n\nexport interface ValidatedInterruptResumeBatch {\n errors: ReadonlyArray<InterruptSubmissionError>\n resolutions?: ReadonlyArray<RunAgentResumeItem>\n canonicalResolutions?: string\n fingerprint?: string\n resumeToolState?: ChatResumeToolState\n}\n\nexport class InterruptResumeValidationError extends Error {\n override readonly name = 'InterruptResumeValidationError'\n\n constructor(readonly errors: ReadonlyArray<InterruptSubmissionError>) {\n super(errors.map((error) => error.message).join(' '))\n }\n}\n\nfunction objectValue(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object'\n ? (value as Record<string, unknown>)\n : null\n}\n\nfunction stringField(\n value: Record<string, unknown>,\n key: string,\n): string | undefined {\n return typeof value[key] === 'string' ? value[key] : undefined\n}\n\nfunction normalizeIssuePath(\n path: ReadonlyArray<unknown> | undefined,\n): ReadonlyArray<string | number> | undefined {\n if (!path) return undefined\n return path.map((segment) => {\n if (typeof segment === 'string' || typeof segment === 'number') {\n return segment\n }\n const record = objectValue(segment)\n const key = record?.key\n return typeof key === 'number' ? key : String(key ?? segment)\n })\n}\n\nexport function interruptItemError(\n input: Pick<\n ValidateInterruptResumeBatchInput,\n 'threadId' | 'interruptedRunId' | 'generation'\n >,\n interruptId: string,\n code: ItemInterruptErrorCode,\n message: string,\n options?: {\n path?: ReadonlyArray<string | number>\n source?: 'client' | 'server'\n retryable?: boolean\n },\n): InterruptSubmissionError {\n return {\n scope: 'item',\n threadId: input.threadId,\n interruptedRunId: input.interruptedRunId,\n generation: input.generation,\n interruptId,\n code,\n message,\n source: options?.source ?? 'client',\n retryable: options?.retryable ?? false,\n ...(options?.path ? { path: options.path } : {}),\n }\n}\n\nasync function validateSchemaValue(input: {\n schema: unknown\n value: unknown\n onIssue: (message: string, path?: ReadonlyArray<string | number>) => void\n}): Promise<void> {\n if (isStandardSchema(input.schema)) {\n const result = await validateWithStandardSchema<unknown>(\n input.schema,\n input.value,\n )\n if (!result.success) {\n for (const issue of result.issues) {\n input.onIssue(issue.message, normalizeIssuePath(issue.path))\n }\n }\n return\n }\n\n // A non-Standard-Schema value (a raw JSON Schema, e.g. a generic interrupt's\n // wire responseSchema) is not validated by the library. The application\n // validates the resume value itself if it needs to; otherwise it flows\n // through as-is.\n}\n\ntype RuntimeTool = ChatMiddlewareConfig['tools'][number] & {\n approvalSchema?: Parameters<typeof normalizeApprovalSchema>[0]\n}\n\nfunction runtimeTool(\n tools: ChatMiddlewareConfig['tools'],\n name: string,\n): RuntimeTool | undefined {\n return tools.find((tool) => tool.name === name) as RuntimeTool | undefined\n}\n\nasync function parseSchemaValue(\n schema: unknown,\n value: unknown,\n): Promise<{ success: true; data: unknown } | { success: false }> {\n if (!isStandardSchema(schema)) return { success: true, data: value }\n const result = await validateWithStandardSchema<unknown>(schema, value)\n return result.success\n ? { success: true, data: result.data }\n : { success: false }\n}\n\nfunction descriptorResponseSchema(\n record: PendingInterruptResumeRecord,\n): unknown {\n return objectValue(record.payload)?.responseSchema\n}\n\nfunction schemaHash(schema: unknown): string {\n return digestInterruptJson(canonicalInterruptJson(schema))\n}\n\nasync function pushSchemaIssues(input: {\n request: ValidateInterruptResumeBatchInput\n errors: Array<InterruptSubmissionError>\n interruptId: string\n schema: unknown\n value: unknown\n code: ItemInterruptErrorCode\n label: string\n}): Promise<void> {\n try {\n await validateSchemaValue({\n schema: input.schema,\n value: input.value,\n onIssue: (message, path) => {\n input.errors.push(\n interruptItemError(\n input.request,\n input.interruptId,\n input.code,\n `${input.label}: ${message}`,\n { path },\n ),\n )\n },\n })\n } catch (error) {\n input.errors.push(\n interruptItemError(\n input.request,\n input.interruptId,\n 'invalid-response-schema',\n `${input.label} could not be validated: ${error instanceof Error ? error.message : String(error)}`,\n { source: 'server' },\n ),\n )\n }\n}\n\nfunction validateDescriptorSchema(\n input: ValidateInterruptResumeBatchInput,\n record: PendingInterruptResumeRecord,\n binding: InterruptBinding,\n errors: Array<InterruptSubmissionError>,\n): unknown {\n const schema = descriptorResponseSchema(record)\n const responseSchemaHash = binding.responseSchemaHash\n if (schema === undefined && responseSchemaHash === undefined) {\n return undefined\n }\n if (\n schema === undefined ||\n responseSchemaHash === undefined ||\n schemaHash(schema) !== responseSchemaHash\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-response-schema',\n `Interrupt ${record.interruptId} response schema no longer matches its binding.`,\n { source: 'server' },\n ),\n )\n }\n return schema\n}\n\n/**\n * Validate and translate a complete interrupt batch before any tool executes.\n * Used by ephemeral chat resume; a durable layer may share the same validator.\n */\nexport async function validateInterruptResumeBatch(\n input: ValidateInterruptResumeBatchInput,\n): Promise<ValidatedInterruptResumeBatch> {\n const grouped = new Map<string, Array<InterruptSubmissionError>>()\n const batchErrors: Array<InterruptSubmissionError> = []\n const group = (interruptId: string): Array<InterruptSubmissionError> => {\n const existing = grouped.get(interruptId)\n if (existing) return existing\n const created: Array<InterruptSubmissionError> = []\n grouped.set(interruptId, created)\n return created\n }\n const pendingById = new Map(\n input.pending.map((record) => [record.interruptId, record]),\n )\n const resumeById = new Map<string, RunAgentResumeItem>()\n const counts = new Map<string, number>()\n for (const entry of input.resume ?? []) {\n counts.set(entry.interruptId, (counts.get(entry.interruptId) ?? 0) + 1)\n if (!resumeById.has(entry.interruptId))\n resumeById.set(entry.interruptId, entry)\n }\n\n for (const [interruptId, count] of counts) {\n if (count > 1) {\n group(interruptId).push(\n interruptItemError(\n input,\n interruptId,\n 'conflict',\n `Interrupt ${interruptId} has duplicate resume entries.`,\n ),\n )\n }\n }\n\n const pendingGenerics = input.pending.filter(\n (record) => record.binding.kind === 'generic',\n )\n const genericBatchSatisfied =\n pendingGenerics.length > 0 &&\n pendingGenerics.every((record) => resumeById.has(record.interruptId))\n\n let incomplete = false\n for (const record of input.pending) {\n const errors = group(record.interruptId)\n const entry = resumeById.get(record.interruptId)\n const binding = record.binding\n if (!entry) {\n // Client tools that share a generic interrupt batch wait for\n // `toolResume`. `continue` re-emits them; `cancel` / `stop` skip them.\n if (genericBatchSatisfied && binding.kind === 'client-tool-execution') {\n continue\n }\n incomplete = true\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'unknown-interrupt',\n `Missing resume entry for interrupt ${record.interruptId}.`,\n ),\n )\n }\n if (\n binding.interruptedRunId !== input.interruptedRunId ||\n binding.generation !== input.generation ||\n binding.interruptId !== record.interruptId\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Interrupt ${record.interruptId} has stale correlation metadata.`,\n { source: 'server' },\n ),\n )\n }\n if (binding.expiresAt !== undefined) {\n const expiresAt = Date.parse(binding.expiresAt)\n if (!Number.isFinite(expiresAt)) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Interrupt ${record.interruptId} has an invalid expiresAt.`,\n { source: 'server' },\n ),\n )\n } else if (expiresAt <= (input.now ?? Date.now())) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'expired',\n `Interrupt ${record.interruptId} has expired.`,\n { source: 'server' },\n ),\n )\n }\n }\n\n const responseSchema = validateDescriptorSchema(\n input,\n record,\n binding,\n errors,\n )\n if (!entry) continue\n const entryStatus: unknown = entry.status\n if (entryStatus !== 'resolved' && entryStatus !== 'cancelled') {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Interrupt ${record.interruptId} has invalid status ${String(entryStatus)}.`,\n ),\n )\n continue\n }\n if (binding.kind === 'generic') {\n const genericRequest = record.genericRequest\n if (genericRequest !== undefined) {\n const batchIndex = binding.batchIndex\n if (\n binding.definitionId !== genericRequest.definition.id ||\n binding.key !== genericRequest.key ||\n binding.interruptId !== record.interruptId ||\n batchIndex === undefined ||\n !Number.isInteger(batchIndex) ||\n batchIndex < 0\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Generic interrupt ${record.interruptId} has stale definition metadata.`,\n { source: 'server' },\n ),\n )\n }\n }\n if (entry.status === 'cancelled') {\n if (entry.payload !== undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Cancelled interrupt ${record.interruptId} must not include a payload.`,\n ),\n )\n }\n } else if (genericRequest !== undefined) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: genericRequest.definition.responseSchema,\n value: entry.payload,\n code: 'invalid-payload',\n label: `Interrupt ${record.interruptId} payload is invalid`,\n })\n } else if (responseSchema !== undefined) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: responseSchema,\n value: entry.payload,\n code: 'invalid-payload',\n label: `Interrupt ${record.interruptId} payload is invalid`,\n })\n }\n continue\n }\n\n if (entry.status === 'cancelled') {\n if (entry.payload !== undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Cancelled interrupt ${record.interruptId} must not include a payload.`,\n ),\n )\n }\n continue\n }\n\n const tool = runtimeTool(input.tools, binding.toolName)\n if (!tool) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} is unavailable for interrupt ${record.interruptId}.`,\n { source: 'server' },\n ),\n )\n continue\n }\n\n let approval: ReturnType<typeof normalizeApprovalSchema> | undefined\n let schemaDrifted = false\n if (binding.kind === 'client-tool-execution') {\n if (hashSchemaInput(tool.outputSchema) !== binding.outputSchemaHash) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} output schema has changed.`,\n { source: 'server' },\n ),\n )\n schemaDrifted = true\n }\n } else {\n try {\n approval = normalizeApprovalSchema(\n tool.approvalSchema,\n tool.inputSchema,\n )\n } catch {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} approval schema is unavailable.`,\n { source: 'server' },\n ),\n )\n schemaDrifted = true\n }\n if (\n approval !== undefined &&\n (hashSchemaInput(tool.inputSchema) !== binding.inputSchemaHash ||\n approval.approvalSchemaHash !== binding.approvalSchemaHash ||\n approval.responseSchemaHash !== binding.responseSchemaHash)\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} approval schema has changed.`,\n { source: 'server' },\n ),\n )\n schemaDrifted = true\n }\n }\n\n if (schemaDrifted) continue\n\n if (binding.kind === 'client-tool-execution') {\n if (responseSchema !== undefined) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: responseSchema,\n value: entry.payload,\n code: 'invalid-tool-output',\n label: `Tool ${binding.toolName} output is invalid`,\n })\n }\n if (tool.outputSchema !== undefined) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: tool.outputSchema,\n value: entry.payload,\n code: 'invalid-tool-output',\n label: `Tool ${binding.toolName} output is invalid`,\n })\n }\n continue\n }\n\n if (approval === undefined) continue\n const envelope = objectValue(entry.payload)\n const approved =\n typeof entry.payload === 'boolean'\n ? entry.payload\n : typeof envelope?.approved === 'boolean'\n ? envelope.approved\n : undefined\n if (approved === undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Approval ${record.interruptId} must be a boolean or decision envelope.`,\n ),\n )\n continue\n }\n if (envelope) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: approval.responseSchema,\n value: entry.payload,\n code: 'invalid-payload',\n label: `Approval ${record.interruptId} envelope is invalid`,\n })\n }\n if (approved && envelope?.editedArgs !== undefined) {\n if (tool.inputSchema === undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-edited-args',\n `Approval ${record.interruptId} cannot edit arguments without an input schema.`,\n ),\n )\n } else {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: tool.inputSchema,\n value: envelope.editedArgs,\n code: 'invalid-edited-args',\n label: `Approval ${record.interruptId} edited arguments are invalid`,\n })\n }\n }\n const branch = approved\n ? approval.branches.approve\n : approval.branches.reject\n if (branch) {\n if (!envelope) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Approval ${record.interruptId} requires a payload for the ${approved ? 'approve' : 'reject'} decision.`,\n ),\n )\n } else {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: branch.source,\n value: envelope.payload,\n code: 'invalid-payload',\n label: `Approval ${record.interruptId} payload is invalid`,\n })\n }\n }\n }\n\n for (const entry of input.resume ?? []) {\n if (!pendingById.has(entry.interruptId)) {\n incomplete = true\n group(entry.interruptId).push(\n interruptItemError(\n input,\n entry.interruptId,\n 'unknown-interrupt',\n `Resume entry references unknown interrupt ${entry.interruptId}.`,\n ),\n )\n }\n }\n\n if (incomplete) {\n batchErrors.push({\n scope: 'batch',\n threadId: input.threadId,\n interruptedRunId: input.interruptedRunId,\n generation: input.generation,\n code: 'incomplete-batch',\n message:\n 'Resume entries must resolve or cancel the complete interrupt batch.',\n source: 'client',\n retryable: false,\n interruptIds: input.pending.map((record) => record.interruptId),\n })\n }\n\n const itemErrors = [...grouped.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .flatMap(([, errors]) => errors)\n if (itemErrors.length > 0) {\n batchErrors.push({\n scope: 'batch',\n threadId: input.threadId,\n interruptedRunId: input.interruptedRunId,\n generation: input.generation,\n code: 'item-validation-failed',\n message: 'One or more interrupt resolutions are invalid.',\n source: 'client',\n retryable: false,\n interruptIds: input.pending.map((record) => record.interruptId),\n })\n return { errors: [...itemErrors, ...batchErrors] }\n }\n\n const canonical = canonicalizeInterruptResolutions(input.resume ?? [])\n const approvals = new Map<string, ToolApprovalResolution>()\n const clientToolResults = new Map<string, unknown>()\n const genericInterrupts = new Map<\n string,\n | { interruptId: string; status: 'resolved'; payload: unknown }\n | { interruptId: string; status: 'cancelled' }\n >()\n const deniedToolResults = new Map<string, unknown>()\n const cancelledToolCallIds = new Set<string>()\n\n for (const record of input.pending) {\n const entry = resumeById.get(record.interruptId)\n if (!entry) continue\n const binding = record.binding\n if (binding.kind === 'generic') {\n if (entry.status !== 'resolved') {\n genericInterrupts.set(record.interruptId, {\n interruptId: record.interruptId,\n status: 'cancelled',\n })\n continue\n }\n if (record.genericRequest === undefined) {\n genericInterrupts.set(record.interruptId, {\n interruptId: record.interruptId,\n status: 'resolved',\n payload: entry.payload,\n })\n continue\n }\n const parsed = await parseSchemaValue(\n record.genericRequest.definition.responseSchema,\n entry.payload,\n )\n if (!parsed.success) {\n return {\n errors: [\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Interrupt ${record.interruptId} payload is invalid.`,\n ),\n ],\n }\n }\n genericInterrupts.set(record.interruptId, {\n interruptId: record.interruptId,\n status: 'resolved',\n payload: parsed.data,\n })\n continue\n }\n if (entry.status === 'cancelled') {\n cancelledToolCallIds.add(binding.toolCallId)\n continue\n }\n if (binding.kind === 'client-tool-execution') {\n clientToolResults.set(binding.toolCallId, entry.payload)\n continue\n }\n const envelope = objectValue(entry.payload)\n const resolution: ToolApprovalResolution =\n typeof entry.payload === 'boolean'\n ? entry.payload\n : envelope?.approved === true\n ? {\n approved: true,\n ...(envelope.editedArgs !== undefined\n ? { editedArgs: envelope.editedArgs }\n : {}),\n ...(envelope.payload !== undefined\n ? { payload: envelope.payload }\n : {}),\n }\n : {\n approved: false,\n ...(envelope?.payload !== undefined\n ? { payload: envelope.payload }\n : {}),\n }\n approvals.set(binding.toolCallId, resolution)\n if (\n resolution === false ||\n (typeof resolution === 'object' && !resolution.approved)\n ) {\n deniedToolResults.set(\n binding.toolCallId,\n typeof resolution === 'object' ? resolution.payload : undefined,\n )\n }\n }\n\n return {\n errors: [],\n resolutions: canonical.resolutions,\n canonicalResolutions: canonical.canonicalResolutions,\n fingerprint: canonical.fingerprint,\n resumeToolState: {\n approvals,\n clientToolResults,\n genericInterrupts,\n deniedToolResults,\n cancelledToolCallIds,\n },\n }\n}\n\n/**\n * Is this a binding written by a version of the protocol we understand?\n *\n * A missing `v` is read as {@link INTERRUPT_BINDING_VERSION} so bindings\n * written before the field existed still resume. A `v` we don't recognise is\n * rejected outright — a newer or foreign producer's binding must not be\n * duck-typed into ours.\n */\nfunction isSupportedBindingVersion(raw: Record<string, unknown>): boolean {\n const version = raw['v']\n if (version === undefined) return true\n return version === INTERRUPT_BINDING_VERSION\n}\n\nexport function readUnopenedInterruptBinding(\n descriptor: Interrupt,\n): UnopenedInterruptBinding | undefined {\n const metadata = objectValue(descriptor.metadata)\n const raw = metadata\n ? objectValue(metadata[interruptBindingMetadataKey])\n : null\n if (!raw || stringField(raw, 'interruptId') !== descriptor.id)\n return undefined\n if (!isSupportedBindingVersion(raw)) return undefined\n const kind = stringField(raw, 'kind')\n const interruptId = stringField(raw, 'interruptId')\n const responseSchemaHash = stringField(raw, 'responseSchemaHash')\n const expiresAt = stringField(raw, 'expiresAt')\n if (!interruptId || responseSchemaHash === '') return undefined\n if (expiresAt !== undefined && !Number.isFinite(Date.parse(expiresAt))) {\n return undefined\n }\n const v = INTERRUPT_BINDING_VERSION\n if (kind === 'generic') {\n const definitionId = stringField(raw, 'definitionId')\n const key = stringField(raw, 'key')\n const batchIndex = raw['batchIndex']\n const payloadSchemaHash = stringField(raw, 'payloadSchemaHash')\n const hasFirstPartyFields =\n definitionId !== undefined ||\n key !== undefined ||\n batchIndex !== undefined ||\n payloadSchemaHash !== undefined\n if (\n hasFirstPartyFields &&\n (!definitionId ||\n !key ||\n typeof batchIndex !== 'number' ||\n !Number.isInteger(batchIndex) ||\n batchIndex < 0)\n ) {\n return undefined\n }\n return {\n v,\n kind,\n interruptId,\n ...(responseSchemaHash ? { responseSchemaHash } : {}),\n ...(expiresAt ? { expiresAt } : {}),\n ...(definitionId ? { definitionId } : {}),\n ...(key ? { key } : {}),\n ...(typeof batchIndex === 'number' ? { batchIndex } : {}),\n ...(payloadSchemaHash ? { payloadSchemaHash } : {}),\n }\n }\n if (!responseSchemaHash) return undefined\n const toolName = stringField(raw, 'toolName')\n const toolCallId = stringField(raw, 'toolCallId')\n if (!toolName || !toolCallId) return undefined\n if (kind === 'client-tool-execution') {\n const outputSchemaHash = stringField(raw, 'outputSchemaHash')\n if (!outputSchemaHash) return undefined\n return {\n v,\n kind,\n interruptId,\n toolName,\n toolCallId,\n outputSchemaHash,\n responseSchemaHash,\n ...(expiresAt ? { expiresAt } : {}),\n }\n }\n if (kind === 'tool-approval') {\n const inputSchemaHash = stringField(raw, 'inputSchemaHash')\n const approvalSchemaHash = stringField(raw, 'approvalSchemaHash')\n if (!inputSchemaHash || !approvalSchemaHash) return undefined\n return {\n v,\n kind,\n interruptId,\n toolName,\n toolCallId,\n originalArgs: raw.originalArgs,\n inputSchemaHash,\n approvalSchemaHash,\n responseSchemaHash,\n ...(expiresAt ? { expiresAt } : {}),\n }\n }\n return undefined\n}\n\n/**\n * Attach a resume binding to an interrupt descriptor, under\n * {@link INTERRUPT_BINDING_METADATA_KEY}.\n *\n * This is the supported way to make an interrupt resumable by this package.\n * The descriptor keeps its AG-UI shape; only `metadata` gains the namespaced\n * key. Pass the unopened form (no `interruptedRunId` / `generation`) when\n * emitting from inside a run — those fields are stamped as the run finishes.\n */\nexport function withInterruptBinding(\n descriptor: Interrupt,\n binding: UnopenedInterruptBinding | InterruptBinding,\n): Interrupt {\n return {\n ...descriptor,\n metadata: {\n ...descriptor.metadata,\n [interruptBindingMetadataKey]: {\n ...binding,\n v: INTERRUPT_BINDING_VERSION,\n interruptId: descriptor.id,\n },\n },\n }\n}\n\n/**\n * Read the opened resume binding off a descriptor, or `undefined` when the\n * descriptor carries no binding of a version we understand.\n *\n * `undefined` means \"this interrupt is not ours to resume\" — it is not a\n * failure to recover from by inventing a binding.\n */\nexport function readInterruptBinding(\n descriptor: Interrupt,\n): InterruptBinding | undefined {\n const unopened = readUnopenedInterruptBinding(descriptor)\n if (!unopened) return undefined\n const metadata = objectValue(descriptor.metadata)\n const raw = metadata\n ? objectValue(metadata[interruptBindingMetadataKey])\n : null\n if (!raw) return undefined\n const interruptedRunId = stringField(raw, 'interruptedRunId')\n const generation = raw['generation']\n if (\n !interruptedRunId ||\n typeof generation !== 'number' ||\n !Number.isInteger(generation) ||\n generation < 0\n ) {\n return undefined\n }\n return { ...unopened, interruptedRunId, generation }\n}\n\nexport function withoutInterruptBinding(descriptor: Interrupt): Interrupt {\n const metadata = objectValue(descriptor.metadata)\n if (!metadata || !(interruptBindingMetadataKey in metadata)) return descriptor\n const publicMetadata = { ...metadata }\n delete publicMetadata[interruptBindingMetadataKey]\n return { ...descriptor, metadata: publicMetadata }\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2CA,IAAa,iCAAiC;AAE9C,IAAM,8BAA8B;AA+BpC,IAAa,iCAAb,cAAoD,MAAM;CAGnC;CAFrB,OAAyB;CAEzB,YAAY,QAA0D;EACpE,MAAM,OAAO,KAAK,UAAU,MAAM,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC;EADjC,KAAA,SAAA;CAErB;AACF;AAEA,SAAS,YAAY,OAAgD;CACnE,OAAO,SAAS,OAAO,UAAU,WAC5B,QACD;AACN;AAEA,SAAS,YACP,OACA,KACoB;CACpB,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;AACvD;AAEA,SAAS,mBACP,MAC4C;CAC5C,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,OAAO,KAAK,KAAK,YAAY;EAC3B,IAAI,OAAO,YAAY,YAAY,OAAO,YAAY,UACpD,OAAO;EAGT,MAAM,MADS,YAAY,OACf,CAAA,EAAQ;EACpB,OAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,OAAO,OAAO;CAC9D,CAAC;AACH;AAEA,SAAgB,mBACd,OAIA,aACA,MACA,SACA,SAK0B;CAC1B,OAAO;EACL,OAAO;EACP,UAAU,MAAM;EAChB,kBAAkB,MAAM;EACxB,YAAY,MAAM;EAClB;EACA;EACA;EACA,QAAQ,SAAS,UAAU;EAC3B,WAAW,SAAS,aAAa;EACjC,GAAI,SAAS,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;CAChD;AACF;AAEA,eAAe,oBAAoB,OAIjB;CAChB,IAAI,iBAAiB,MAAM,MAAM,GAAG;EAClC,MAAM,SAAS,MAAM,2BACnB,MAAM,QACN,MAAM,KACR;EACA,IAAI,CAAC,OAAO,SACV,KAAK,MAAM,SAAS,OAAO,QACzB,MAAM,QAAQ,MAAM,SAAS,mBAAmB,MAAM,IAAI,CAAC;EAG/D;CACF;AAMF;AAMA,SAAS,YACP,OACA,MACyB;CACzB,OAAO,MAAM,MAAM,SAAS,KAAK,SAAS,IAAI;AAChD;AAEA,eAAe,iBACb,QACA,OACgE;CAChE,IAAI,CAAC,iBAAiB,MAAM,GAAG,OAAO;EAAE,SAAS;EAAM,MAAM;CAAM;CACnE,MAAM,SAAS,MAAM,2BAAoC,QAAQ,KAAK;CACtE,OAAO,OAAO,UACV;EAAE,SAAS;EAAM,MAAM,OAAO;CAAK,IACnC,EAAE,SAAS,MAAM;AACvB;AAEA,SAAS,yBACP,QACS;CACT,OAAO,YAAY,OAAO,OAAO,CAAC,EAAE;AACtC;AAEA,SAAS,WAAW,QAAyB;CAC3C,OAAO,oBAAoB,uBAAuB,MAAM,CAAC;AAC3D;AAEA,eAAe,iBAAiB,OAQd;CAChB,IAAI;EACF,MAAM,oBAAoB;GACxB,QAAQ,MAAM;GACd,OAAO,MAAM;GACb,UAAU,SAAS,SAAS;IAC1B,MAAM,OAAO,KACX,mBACE,MAAM,SACN,MAAM,aACN,MAAM,MACN,GAAG,MAAM,MAAM,IAAI,WACnB,EAAE,KAAK,CACT,CACF;GACF;EACF,CAAC;CACH,SAAS,OAAO;EACd,MAAM,OAAO,KACX,mBACE,MAAM,SACN,MAAM,aACN,2BACA,GAAG,MAAM,MAAM,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAC/F,EAAE,QAAQ,SAAS,CACrB,CACF;CACF;AACF;AAEA,SAAS,yBACP,OACA,QACA,SACA,QACS;CACT,MAAM,SAAS,yBAAyB,MAAM;CAC9C,MAAM,qBAAqB,QAAQ;CACnC,IAAI,WAAW,KAAA,KAAa,uBAAuB,KAAA,GACjD;CAEF,IACE,WAAW,KAAA,KACX,uBAAuB,KAAA,KACvB,WAAW,MAAM,MAAM,oBAEvB,OAAO,KACL,mBACE,OACA,OAAO,aACP,2BACA,aAAa,OAAO,YAAY,kDAChC,EAAE,QAAQ,SAAS,CACrB,CACF;CAEF,OAAO;AACT;;;;;AAMA,eAAsB,6BACpB,OACwC;CACxC,MAAM,0BAAU,IAAI,IAA6C;CACjE,MAAM,cAA+C,CAAC;CACtD,MAAM,SAAS,gBAAyD;EACtE,MAAM,WAAW,QAAQ,IAAI,WAAW;EACxC,IAAI,UAAU,OAAO;EACrB,MAAM,UAA2C,CAAC;EAClD,QAAQ,IAAI,aAAa,OAAO;EAChC,OAAO;CACT;CACA,MAAM,cAAc,IAAI,IACtB,MAAM,QAAQ,KAAK,WAAW,CAAC,OAAO,aAAa,MAAM,CAAC,CAC5D;CACA,MAAM,6BAAa,IAAI,IAAgC;CACvD,MAAM,yBAAS,IAAI,IAAoB;CACvC,KAAK,MAAM,SAAS,MAAM,UAAU,CAAC,GAAG;EACtC,OAAO,IAAI,MAAM,cAAc,OAAO,IAAI,MAAM,WAAW,KAAK,KAAK,CAAC;EACtE,IAAI,CAAC,WAAW,IAAI,MAAM,WAAW,GACnC,WAAW,IAAI,MAAM,aAAa,KAAK;CAC3C;CAEA,KAAK,MAAM,CAAC,aAAa,UAAU,QACjC,IAAI,QAAQ,GACV,MAAM,WAAW,CAAC,CAAC,KACjB,mBACE,OACA,aACA,YACA,aAAa,YAAY,+BAC3B,CACF;CAIJ,MAAM,kBAAkB,MAAM,QAAQ,QACnC,WAAW,OAAO,QAAQ,SAAS,SACtC;CACA,MAAM,wBACJ,gBAAgB,SAAS,KACzB,gBAAgB,OAAO,WAAW,WAAW,IAAI,OAAO,WAAW,CAAC;CAEtE,IAAI,aAAa;CACjB,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,SAAS,MAAM,OAAO,WAAW;EACvC,MAAM,QAAQ,WAAW,IAAI,OAAO,WAAW;EAC/C,MAAM,UAAU,OAAO;EACvB,IAAI,CAAC,OAAO;GAGV,IAAI,yBAAyB,QAAQ,SAAS,yBAC5C;GAEF,aAAa;GACb,OAAO,KACL,mBACE,OACA,OAAO,aACP,qBACA,sCAAsC,OAAO,YAAY,EAC3D,CACF;EACF;EACA,IACE,QAAQ,qBAAqB,MAAM,oBACnC,QAAQ,eAAe,MAAM,cAC7B,QAAQ,gBAAgB,OAAO,aAE/B,OAAO,KACL,mBACE,OACA,OAAO,aACP,SACA,aAAa,OAAO,YAAY,mCAChC,EAAE,QAAQ,SAAS,CACrB,CACF;EAEF,IAAI,QAAQ,cAAc,KAAA,GAAW;GACnC,MAAM,YAAY,KAAK,MAAM,QAAQ,SAAS;GAC9C,IAAI,CAAC,OAAO,SAAS,SAAS,GAC5B,OAAO,KACL,mBACE,OACA,OAAO,aACP,mBACA,aAAa,OAAO,YAAY,6BAChC,EAAE,QAAQ,SAAS,CACrB,CACF;QACK,IAAI,cAAc,MAAM,OAAO,KAAK,IAAI,IAC7C,OAAO,KACL,mBACE,OACA,OAAO,aACP,WACA,aAAa,OAAO,YAAY,gBAChC,EAAE,QAAQ,SAAS,CACrB,CACF;EAEJ;EAEA,MAAM,iBAAiB,yBACrB,OACA,QACA,SACA,MACF;EACA,IAAI,CAAC,OAAO;EACZ,MAAM,cAAuB,MAAM;EACnC,IAAI,gBAAgB,cAAc,gBAAgB,aAAa;GAC7D,OAAO,KACL,mBACE,OACA,OAAO,aACP,mBACA,aAAa,OAAO,YAAY,sBAAsB,OAAO,WAAW,EAAE,EAC5E,CACF;GACA;EACF;EACA,IAAI,QAAQ,SAAS,WAAW;GAC9B,MAAM,iBAAiB,OAAO;GAC9B,IAAI,mBAAmB,KAAA,GAAW;IAChC,MAAM,aAAa,QAAQ;IAC3B,IACE,QAAQ,iBAAiB,eAAe,WAAW,MACnD,QAAQ,QAAQ,eAAe,OAC/B,QAAQ,gBAAgB,OAAO,eAC/B,eAAe,KAAA,KACf,CAAC,OAAO,UAAU,UAAU,KAC5B,aAAa,GAEb,OAAO,KACL,mBACE,OACA,OAAO,aACP,SACA,qBAAqB,OAAO,YAAY,kCACxC,EAAE,QAAQ,SAAS,CACrB,CACF;GAEJ;GACA,IAAI,MAAM,WAAW,aACf;QAAA,MAAM,YAAY,KAAA,GACpB,OAAO,KACL,mBACE,OACA,OAAO,aACP,mBACA,uBAAuB,OAAO,YAAY,6BAC5C,CACF;GAAA,OAEG,IAAI,mBAAmB,KAAA,GAC5B,MAAM,iBAAiB;IACrB,SAAS;IACT;IACA,aAAa,OAAO;IACpB,QAAQ,eAAe,WAAW;IAClC,OAAO,MAAM;IACb,MAAM;IACN,OAAO,aAAa,OAAO,YAAY;GACzC,CAAC;QACI,IAAI,mBAAmB,KAAA,GAC5B,MAAM,iBAAiB;IACrB,SAAS;IACT;IACA,aAAa,OAAO;IACpB,QAAQ;IACR,OAAO,MAAM;IACb,MAAM;IACN,OAAO,aAAa,OAAO,YAAY;GACzC,CAAC;GAEH;EACF;EAEA,IAAI,MAAM,WAAW,aAAa;GAChC,IAAI,MAAM,YAAY,KAAA,GACpB,OAAO,KACL,mBACE,OACA,OAAO,aACP,mBACA,uBAAuB,OAAO,YAAY,6BAC5C,CACF;GAEF;EACF;EAEA,MAAM,OAAO,YAAY,MAAM,OAAO,QAAQ,QAAQ;EACtD,IAAI,CAAC,MAAM;GACT,OAAO,KACL,mBACE,OACA,OAAO,aACP,SACA,QAAQ,QAAQ,SAAS,gCAAgC,OAAO,YAAY,IAC5E,EAAE,QAAQ,SAAS,CACrB,CACF;GACA;EACF;EAEA,IAAI;EACJ,IAAI,gBAAgB;EACpB,IAAI,QAAQ,SAAS,yBACf;OAAA,gBAAgB,KAAK,YAAY,MAAM,QAAQ,kBAAkB;IACnE,OAAO,KACL,mBACE,OACA,OAAO,aACP,SACA,QAAQ,QAAQ,SAAS,8BACzB,EAAE,QAAQ,SAAS,CACrB,CACF;IACA,gBAAgB;GAClB;SACK;GACL,IAAI;IACF,WAAW,wBACT,KAAK,gBACL,KAAK,WACP;GACF,QAAQ;IACN,OAAO,KACL,mBACE,OACA,OAAO,aACP,SACA,QAAQ,QAAQ,SAAS,mCACzB,EAAE,QAAQ,SAAS,CACrB,CACF;IACA,gBAAgB;GAClB;GACA,IACE,aAAa,KAAA,MACZ,gBAAgB,KAAK,WAAW,MAAM,QAAQ,mBAC7C,SAAS,uBAAuB,QAAQ,sBACxC,SAAS,uBAAuB,QAAQ,qBAC1C;IACA,OAAO,KACL,mBACE,OACA,OAAO,aACP,SACA,QAAQ,QAAQ,SAAS,gCACzB,EAAE,QAAQ,SAAS,CACrB,CACF;IACA,gBAAgB;GAClB;EACF;EAEA,IAAI,eAAe;EAEnB,IAAI,QAAQ,SAAS,yBAAyB;GAC5C,IAAI,mBAAmB,KAAA,GACrB,MAAM,iBAAiB;IACrB,SAAS;IACT;IACA,aAAa,OAAO;IACpB,QAAQ;IACR,OAAO,MAAM;IACb,MAAM;IACN,OAAO,QAAQ,QAAQ,SAAS;GAClC,CAAC;GAEH,IAAI,KAAK,iBAAiB,KAAA,GACxB,MAAM,iBAAiB;IACrB,SAAS;IACT;IACA,aAAa,OAAO;IACpB,QAAQ,KAAK;IACb,OAAO,MAAM;IACb,MAAM;IACN,OAAO,QAAQ,QAAQ,SAAS;GAClC,CAAC;GAEH;EACF;EAEA,IAAI,aAAa,KAAA,GAAW;EAC5B,MAAM,WAAW,YAAY,MAAM,OAAO;EAC1C,MAAM,WACJ,OAAO,MAAM,YAAY,YACrB,MAAM,UACN,OAAO,UAAU,aAAa,YAC5B,SAAS,WACT,KAAA;EACR,IAAI,aAAa,KAAA,GAAW;GAC1B,OAAO,KACL,mBACE,OACA,OAAO,aACP,mBACA,YAAY,OAAO,YAAY,yCACjC,CACF;GACA;EACF;EACA,IAAI,UACF,MAAM,iBAAiB;GACrB,SAAS;GACT;GACA,aAAa,OAAO;GACpB,QAAQ,SAAS;GACjB,OAAO,MAAM;GACb,MAAM;GACN,OAAO,YAAY,OAAO,YAAY;EACxC,CAAC;EAEH,IAAI,YAAY,UAAU,eAAe,KAAA,GAAW;GAClD,IAAI,KAAK,gBAAgB,KAAA,GACvB,OAAO,KACL,mBACE,OACA,OAAO,aACP,uBACA,YAAY,OAAO,YAAY,gDACjC,CACF;QAEA,MAAM,iBAAiB;IACrB,SAAS;IACT;IACA,aAAa,OAAO;IACpB,QAAQ,KAAK;IACb,OAAO,SAAS;IAChB,MAAM;IACN,OAAO,YAAY,OAAO,YAAY;GACxC,CAAC;EAEL;EACA,MAAM,SAAS,WACX,SAAS,SAAS,UAClB,SAAS,SAAS;EACtB,IAAI,QAAQ;GACV,IAAI,CAAC,UACH,OAAO,KACL,mBACE,OACA,OAAO,aACP,mBACA,YAAY,OAAO,YAAY,8BAA8B,WAAW,YAAY,SAAS,WAC/F,CACF;QAEA,MAAM,iBAAiB;IACrB,SAAS;IACT;IACA,aAAa,OAAO;IACpB,QAAQ,OAAO;IACf,OAAO,SAAS;IAChB,MAAM;IACN,OAAO,YAAY,OAAO,YAAY;GACxC,CAAC;EAEL;CACF;CAEA,KAAK,MAAM,SAAS,MAAM,UAAU,CAAC,GACnC,IAAI,CAAC,YAAY,IAAI,MAAM,WAAW,GAAG;EACvC,aAAa;EACb,MAAM,MAAM,WAAW,CAAC,CAAC,KACvB,mBACE,OACA,MAAM,aACN,qBACA,6CAA6C,MAAM,YAAY,EACjE,CACF;CACF;CAGF,IAAI,YACF,YAAY,KAAK;EACf,OAAO;EACP,UAAU,MAAM;EAChB,kBAAkB,MAAM;EACxB,YAAY,MAAM;EAClB,MAAM;EACN,SACE;EACF,QAAQ;EACR,WAAW;EACX,cAAc,MAAM,QAAQ,KAAK,WAAW,OAAO,WAAW;CAChE,CAAC;CAGH,MAAM,aAAa,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC,CACtC,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,KAAK,CAAC,CAAC,CACpD,SAAS,GAAG,YAAY,MAAM;CACjC,IAAI,WAAW,SAAS,GAAG;EACzB,YAAY,KAAK;GACf,OAAO;GACP,UAAU,MAAM;GAChB,kBAAkB,MAAM;GACxB,YAAY,MAAM;GAClB,MAAM;GACN,SAAS;GACT,QAAQ;GACR,WAAW;GACX,cAAc,MAAM,QAAQ,KAAK,WAAW,OAAO,WAAW;EAChE,CAAC;EACD,OAAO,EAAE,QAAQ,CAAC,GAAG,YAAY,GAAG,WAAW,EAAE;CACnD;CAEA,MAAM,YAAY,iCAAiC,MAAM,UAAU,CAAC,CAAC;CACrE,MAAM,4BAAY,IAAI,IAAoC;CAC1D,MAAM,oCAAoB,IAAI,IAAqB;CACnD,MAAM,oCAAoB,IAAI,IAI5B;CACF,MAAM,oCAAoB,IAAI,IAAqB;CACnD,MAAM,uCAAuB,IAAI,IAAY;CAE7C,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,QAAQ,WAAW,IAAI,OAAO,WAAW;EAC/C,IAAI,CAAC,OAAO;EACZ,MAAM,UAAU,OAAO;EACvB,IAAI,QAAQ,SAAS,WAAW;GAC9B,IAAI,MAAM,WAAW,YAAY;IAC/B,kBAAkB,IAAI,OAAO,aAAa;KACxC,aAAa,OAAO;KACpB,QAAQ;IACV,CAAC;IACD;GACF;GACA,IAAI,OAAO,mBAAmB,KAAA,GAAW;IACvC,kBAAkB,IAAI,OAAO,aAAa;KACxC,aAAa,OAAO;KACpB,QAAQ;KACR,SAAS,MAAM;IACjB,CAAC;IACD;GACF;GACA,MAAM,SAAS,MAAM,iBACnB,OAAO,eAAe,WAAW,gBACjC,MAAM,OACR;GACA,IAAI,CAAC,OAAO,SACV,OAAO,EACL,QAAQ,CACN,mBACE,OACA,OAAO,aACP,mBACA,aAAa,OAAO,YAAY,qBAClC,CACF,EACF;GAEF,kBAAkB,IAAI,OAAO,aAAa;IACxC,aAAa,OAAO;IACpB,QAAQ;IACR,SAAS,OAAO;GAClB,CAAC;GACD;EACF;EACA,IAAI,MAAM,WAAW,aAAa;GAChC,qBAAqB,IAAI,QAAQ,UAAU;GAC3C;EACF;EACA,IAAI,QAAQ,SAAS,yBAAyB;GAC5C,kBAAkB,IAAI,QAAQ,YAAY,MAAM,OAAO;GACvD;EACF;EACA,MAAM,WAAW,YAAY,MAAM,OAAO;EAC1C,MAAM,aACJ,OAAO,MAAM,YAAY,YACrB,MAAM,UACN,UAAU,aAAa,OACrB;GACE,UAAU;GACV,GAAI,SAAS,eAAe,KAAA,IACxB,EAAE,YAAY,SAAS,WAAW,IAClC,CAAC;GACL,GAAI,SAAS,YAAY,KAAA,IACrB,EAAE,SAAS,SAAS,QAAQ,IAC5B,CAAC;EACP,IACA;GACE,UAAU;GACV,GAAI,UAAU,YAAY,KAAA,IACtB,EAAE,SAAS,SAAS,QAAQ,IAC5B,CAAC;EACP;EACR,UAAU,IAAI,QAAQ,YAAY,UAAU;EAC5C,IACE,eAAe,SACd,OAAO,eAAe,YAAY,CAAC,WAAW,UAE/C,kBAAkB,IAChB,QAAQ,YACR,OAAO,eAAe,WAAW,WAAW,UAAU,KAAA,CACxD;CAEJ;CAEA,OAAO;EACL,QAAQ,CAAC;EACT,aAAa,UAAU;EACvB,sBAAsB,UAAU;EAChC,aAAa,UAAU;EACvB,iBAAiB;GACf;GACA;GACA;GACA;GACA;EACF;CACF;AACF;;;;;;;;;AAUA,SAAS,0BAA0B,KAAuC;CACxE,MAAM,UAAU,IAAI;CACpB,IAAI,YAAY,KAAA,GAAW,OAAO;CAClC,OAAO,YAAA;AACT;AAEA,SAAgB,6BACd,YACsC;CACtC,MAAM,WAAW,YAAY,WAAW,QAAQ;CAChD,MAAM,MAAM,WACR,YAAY,SAAS,4BAA4B,IACjD;CACJ,IAAI,CAAC,OAAO,YAAY,KAAK,aAAa,MAAM,WAAW,IACzD,OAAO,KAAA;CACT,IAAI,CAAC,0BAA0B,GAAG,GAAG,OAAO,KAAA;CAC5C,MAAM,OAAO,YAAY,KAAK,MAAM;CACpC,MAAM,cAAc,YAAY,KAAK,aAAa;CAClD,MAAM,qBAAqB,YAAY,KAAK,oBAAoB;CAChE,MAAM,YAAY,YAAY,KAAK,WAAW;CAC9C,IAAI,CAAC,eAAe,uBAAuB,IAAI,OAAO,KAAA;CACtD,IAAI,cAAc,KAAA,KAAa,CAAC,OAAO,SAAS,KAAK,MAAM,SAAS,CAAC,GACnE;CAEF,MAAM,IAAA;CACN,IAAI,SAAS,WAAW;EACtB,MAAM,eAAe,YAAY,KAAK,cAAc;EACpD,MAAM,MAAM,YAAY,KAAK,KAAK;EAClC,MAAM,aAAa,IAAI;EACvB,MAAM,oBAAoB,YAAY,KAAK,mBAAmB;EAM9D,KAJE,iBAAiB,KAAA,KACjB,QAAQ,KAAA,KACR,eAAe,KAAA,KACf,sBAAsB,KAAA,OAGrB,CAAC,gBACA,CAAC,OACD,OAAO,eAAe,YACtB,CAAC,OAAO,UAAU,UAAU,KAC5B,aAAa,IAEf;EAEF,OAAO;GACL;GACA;GACA;GACA,GAAI,qBAAqB,EAAE,mBAAmB,IAAI,CAAC;GACnD,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;GACjC,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;GACvC,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;GACrB,GAAI,OAAO,eAAe,WAAW,EAAE,WAAW,IAAI,CAAC;GACvD,GAAI,oBAAoB,EAAE,kBAAkB,IAAI,CAAC;EACnD;CACF;CACA,IAAI,CAAC,oBAAoB,OAAO,KAAA;CAChC,MAAM,WAAW,YAAY,KAAK,UAAU;CAC5C,MAAM,aAAa,YAAY,KAAK,YAAY;CAChD,IAAI,CAAC,YAAY,CAAC,YAAY,OAAO,KAAA;CACrC,IAAI,SAAS,yBAAyB;EACpC,MAAM,mBAAmB,YAAY,KAAK,kBAAkB;EAC5D,IAAI,CAAC,kBAAkB,OAAO,KAAA;EAC9B,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACnC;CACF;CACA,IAAI,SAAS,iBAAiB;EAC5B,MAAM,kBAAkB,YAAY,KAAK,iBAAiB;EAC1D,MAAM,qBAAqB,YAAY,KAAK,oBAAoB;EAChE,IAAI,CAAC,mBAAmB,CAAC,oBAAoB,OAAO,KAAA;EACpD,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,cAAc,IAAI;GAClB;GACA;GACA;GACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACnC;CACF;AAEF;;;;;;;;;;AAWA,SAAgB,qBACd,YACA,SACW;CACX,OAAO;EACL,GAAG;EACH,UAAU;GACR,GAAG,WAAW;IACb,8BAA8B;IAC7B,GAAG;IACH,GAAA;IACA,aAAa,WAAW;GAC1B;EACF;CACF;AACF;;;;;;;;AASA,SAAgB,qBACd,YAC8B;CAC9B,MAAM,WAAW,6BAA6B,UAAU;CACxD,IAAI,CAAC,UAAU,OAAO,KAAA;CACtB,MAAM,WAAW,YAAY,WAAW,QAAQ;CAChD,MAAM,MAAM,WACR,YAAY,SAAS,4BAA4B,IACjD;CACJ,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,MAAM,mBAAmB,YAAY,KAAK,kBAAkB;CAC5D,MAAM,aAAa,IAAI;CACvB,IACE,CAAC,oBACD,OAAO,eAAe,YACtB,CAAC,OAAO,UAAU,UAAU,KAC5B,aAAa,GAEb;CAEF,OAAO;EAAE,GAAG;EAAU;EAAkB;CAAW;AACrD;AAEA,SAAgB,wBAAwB,YAAkC;CACxE,MAAM,WAAW,YAAY,WAAW,QAAQ;CAChD,IAAI,CAAC,YAAY,EAAE,+BAA+B,WAAW,OAAO;CACpE,MAAM,iBAAiB,EAAE,GAAG,SAAS;CACrC,OAAO,eAAe;CACtB,OAAO;EAAE,GAAG;EAAY,UAAU;CAAe;AACnD"}
1
+ {"version":3,"file":"interrupt-resume.js","names":[],"sources":["../../src/interrupt-resume.ts"],"sourcesContent":["import {\n INTERRUPT_BINDING_VERSION,\n canonicalizeInterruptResolutions,\n} from './interrupts'\nimport {\n canonicalInterruptJson,\n digestInterruptJson,\n} from './interrupt-serialization'\nimport {\n hashSchemaInput,\n normalizeApprovalSchema,\n} from './activities/chat/tools/approval-schema'\nimport {\n isStandardSchema,\n validateWithStandardSchema,\n} from './activities/chat/tools/schema-converter'\nimport { tanstackMetadata } from './utilities/merge-metadata'\nimport type {\n InterruptBinding,\n InterruptSubmissionError,\n ItemInterruptErrorCode,\n ToolApprovalResolution,\n UnopenedInterruptBinding,\n} from './interrupts'\nimport type {\n ChatMiddlewareConfig,\n ChatResumeToolState,\n} from './activities/chat/middleware/types'\nimport type {\n GenericInterruptRequest,\n InterruptDefinition,\n} from './interrupt-definition'\nimport type { Interrupt, RunAgentResumeItem } from './types'\n\n/**\n * The `Interrupt.metadata` key under which this package's resume binding\n * travels.\n *\n * Exported so anything that produces an interrupt this package must later\n * resume — an application middleware raising a generic pause, a future\n * workflow-to-AG-UI projection — attaches the binding through\n * {@link withInterruptBinding} rather than copying the string. Everything\n * outside this key is the plain AG-UI envelope and is left untouched.\n */\nexport const INTERRUPT_BINDING_METADATA_KEY = 'tanstack:interruptBinding'\n\nconst interruptBindingMetadataKey = INTERRUPT_BINDING_METADATA_KEY\n\n/** The persistence-neutral shape required to validate an interrupt resume. */\nexport interface PendingInterruptResumeRecord {\n interruptId: string\n payload: unknown\n binding: InterruptBinding\n /** Present for a first-party generic interrupt. */\n genericRequest?: GenericInterruptRequest<\n InterruptDefinition<any, any, any, any>\n >\n}\n\nexport interface ValidateInterruptResumeBatchInput {\n threadId: string\n interruptedRunId: string\n generation: number\n pending: ReadonlyArray<PendingInterruptResumeRecord>\n resume?: ReadonlyArray<RunAgentResumeItem>\n tools: ChatMiddlewareConfig['tools']\n now?: number\n}\n\nexport interface ValidatedInterruptResumeBatch {\n errors: ReadonlyArray<InterruptSubmissionError>\n resolutions?: ReadonlyArray<RunAgentResumeItem>\n canonicalResolutions?: string\n fingerprint?: string\n resumeToolState?: ChatResumeToolState\n}\n\nexport class InterruptResumeValidationError extends Error {\n override readonly name = 'InterruptResumeValidationError'\n\n constructor(readonly errors: ReadonlyArray<InterruptSubmissionError>) {\n super(errors.map((error) => error.message).join(' '))\n }\n}\n\nfunction objectValue(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object'\n ? (value as Record<string, unknown>)\n : null\n}\n\nfunction stringField(\n value: Record<string, unknown>,\n key: string,\n): string | undefined {\n return typeof value[key] === 'string' ? value[key] : undefined\n}\n\ntype ClientToolResumeResult =\n | { state: 'output-available'; output: unknown }\n | { state: 'output-error'; errorText: string }\n\nfunction clientToolResult(\n entry: RunAgentResumeItem,\n): ClientToolResumeResult | null {\n if (tanstackMetadata(entry)?.state === 'output-error') {\n const result = objectValue(entry.payload)\n if (\n !result ||\n Object.keys(result).length !== 1 ||\n typeof result.error !== 'string'\n ) {\n return null\n }\n return {\n state: 'output-error',\n errorText: result.error,\n }\n }\n return {\n state: 'output-available',\n output: entry.payload,\n }\n}\n\nfunction normalizeIssuePath(\n path: ReadonlyArray<unknown> | undefined,\n): ReadonlyArray<string | number> | undefined {\n if (!path) return undefined\n return path.map((segment) => {\n if (typeof segment === 'string' || typeof segment === 'number') {\n return segment\n }\n const record = objectValue(segment)\n const key = record?.key\n return typeof key === 'number' ? key : String(key ?? segment)\n })\n}\n\nexport function interruptItemError(\n input: Pick<\n ValidateInterruptResumeBatchInput,\n 'threadId' | 'interruptedRunId' | 'generation'\n >,\n interruptId: string,\n code: ItemInterruptErrorCode,\n message: string,\n options?: {\n path?: ReadonlyArray<string | number>\n source?: 'client' | 'server'\n retryable?: boolean\n },\n): InterruptSubmissionError {\n return {\n scope: 'item',\n threadId: input.threadId,\n interruptedRunId: input.interruptedRunId,\n generation: input.generation,\n interruptId,\n code,\n message,\n source: options?.source ?? 'client',\n retryable: options?.retryable ?? false,\n ...(options?.path ? { path: options.path } : {}),\n }\n}\n\nasync function validateSchemaValue(input: {\n schema: unknown\n value: unknown\n onIssue: (message: string, path?: ReadonlyArray<string | number>) => void\n}): Promise<void> {\n if (isStandardSchema(input.schema)) {\n const result = await validateWithStandardSchema<unknown>(\n input.schema,\n input.value,\n )\n if (!result.success) {\n for (const issue of result.issues) {\n input.onIssue(issue.message, normalizeIssuePath(issue.path))\n }\n }\n return\n }\n\n // A non-Standard-Schema value (a raw JSON Schema, e.g. a generic interrupt's\n // wire responseSchema) is not validated by the library. The application\n // validates the resume value itself if it needs to; otherwise it flows\n // through as-is.\n}\n\ntype RuntimeTool = ChatMiddlewareConfig['tools'][number] & {\n approvalSchema?: Parameters<typeof normalizeApprovalSchema>[0]\n}\n\nfunction runtimeTool(\n tools: ChatMiddlewareConfig['tools'],\n name: string,\n): RuntimeTool | undefined {\n return tools.find((tool) => tool.name === name) as RuntimeTool | undefined\n}\n\nasync function parseSchemaValue(\n schema: unknown,\n value: unknown,\n): Promise<{ success: true; data: unknown } | { success: false }> {\n if (!isStandardSchema(schema)) return { success: true, data: value }\n const result = await validateWithStandardSchema<unknown>(schema, value)\n return result.success\n ? { success: true, data: result.data }\n : { success: false }\n}\n\nfunction descriptorResponseSchema(\n record: PendingInterruptResumeRecord,\n): unknown {\n return objectValue(record.payload)?.responseSchema\n}\n\nfunction schemaHash(schema: unknown): string {\n return digestInterruptJson(canonicalInterruptJson(schema))\n}\n\nasync function pushSchemaIssues(input: {\n request: ValidateInterruptResumeBatchInput\n errors: Array<InterruptSubmissionError>\n interruptId: string\n schema: unknown\n value: unknown\n code: ItemInterruptErrorCode\n label: string\n}): Promise<void> {\n try {\n await validateSchemaValue({\n schema: input.schema,\n value: input.value,\n onIssue: (message, path) => {\n input.errors.push(\n interruptItemError(\n input.request,\n input.interruptId,\n input.code,\n `${input.label}: ${message}`,\n { path },\n ),\n )\n },\n })\n } catch (error) {\n input.errors.push(\n interruptItemError(\n input.request,\n input.interruptId,\n 'invalid-response-schema',\n `${input.label} could not be validated: ${error instanceof Error ? error.message : String(error)}`,\n { source: 'server' },\n ),\n )\n }\n}\n\nfunction validateDescriptorSchema(\n input: ValidateInterruptResumeBatchInput,\n record: PendingInterruptResumeRecord,\n binding: InterruptBinding,\n errors: Array<InterruptSubmissionError>,\n): unknown {\n const schema = descriptorResponseSchema(record)\n const responseSchemaHash = binding.responseSchemaHash\n if (schema === undefined && responseSchemaHash === undefined) {\n return undefined\n }\n if (\n schema === undefined ||\n responseSchemaHash === undefined ||\n schemaHash(schema) !== responseSchemaHash\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-response-schema',\n `Interrupt ${record.interruptId} response schema no longer matches its binding.`,\n { source: 'server' },\n ),\n )\n }\n return schema\n}\n\n/**\n * Validate and translate a complete interrupt batch before any tool executes.\n * Used by ephemeral chat resume; a durable layer may share the same validator.\n */\nexport async function validateInterruptResumeBatch(\n input: ValidateInterruptResumeBatchInput,\n): Promise<ValidatedInterruptResumeBatch> {\n const grouped = new Map<string, Array<InterruptSubmissionError>>()\n const batchErrors: Array<InterruptSubmissionError> = []\n const group = (interruptId: string): Array<InterruptSubmissionError> => {\n const existing = grouped.get(interruptId)\n if (existing) return existing\n const created: Array<InterruptSubmissionError> = []\n grouped.set(interruptId, created)\n return created\n }\n const pendingById = new Map(\n input.pending.map((record) => [record.interruptId, record]),\n )\n const resumeById = new Map<string, RunAgentResumeItem>()\n const counts = new Map<string, number>()\n for (const entry of input.resume ?? []) {\n counts.set(entry.interruptId, (counts.get(entry.interruptId) ?? 0) + 1)\n if (!resumeById.has(entry.interruptId))\n resumeById.set(entry.interruptId, entry)\n }\n\n for (const [interruptId, count] of counts) {\n if (count > 1) {\n group(interruptId).push(\n interruptItemError(\n input,\n interruptId,\n 'conflict',\n `Interrupt ${interruptId} has duplicate resume entries.`,\n ),\n )\n }\n }\n\n const pendingGenerics = input.pending.filter(\n (record) => record.binding.kind === 'generic',\n )\n const genericBatchSatisfied =\n pendingGenerics.length > 0 &&\n pendingGenerics.every((record) => resumeById.has(record.interruptId))\n\n let incomplete = false\n for (const record of input.pending) {\n const errors = group(record.interruptId)\n const entry = resumeById.get(record.interruptId)\n const binding = record.binding\n if (!entry) {\n // Client tools that share a generic interrupt batch wait for\n // `toolResume`. `continue` re-emits them; `cancel` / `stop` skip them.\n if (genericBatchSatisfied && binding.kind === 'client-tool-execution') {\n continue\n }\n incomplete = true\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'unknown-interrupt',\n `Missing resume entry for interrupt ${record.interruptId}.`,\n ),\n )\n }\n if (\n binding.interruptedRunId !== input.interruptedRunId ||\n binding.generation !== input.generation ||\n binding.interruptId !== record.interruptId\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Interrupt ${record.interruptId} has stale correlation metadata.`,\n { source: 'server' },\n ),\n )\n }\n if (binding.expiresAt !== undefined) {\n const expiresAt = Date.parse(binding.expiresAt)\n if (!Number.isFinite(expiresAt)) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Interrupt ${record.interruptId} has an invalid expiresAt.`,\n { source: 'server' },\n ),\n )\n } else if (expiresAt <= (input.now ?? Date.now())) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'expired',\n `Interrupt ${record.interruptId} has expired.`,\n { source: 'server' },\n ),\n )\n }\n }\n\n const responseSchema = validateDescriptorSchema(\n input,\n record,\n binding,\n errors,\n )\n if (!entry) continue\n const entryStatus: unknown = entry.status\n if (entryStatus !== 'resolved' && entryStatus !== 'cancelled') {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Interrupt ${record.interruptId} has invalid status ${String(entryStatus)}.`,\n ),\n )\n continue\n }\n if (binding.kind === 'generic') {\n const genericRequest = record.genericRequest\n if (genericRequest !== undefined) {\n const batchIndex = binding.batchIndex\n if (\n binding.definitionId !== genericRequest.definition.id ||\n binding.key !== genericRequest.key ||\n binding.interruptId !== record.interruptId ||\n batchIndex === undefined ||\n !Number.isInteger(batchIndex) ||\n batchIndex < 0\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Generic interrupt ${record.interruptId} has stale definition metadata.`,\n { source: 'server' },\n ),\n )\n }\n }\n if (entry.status === 'cancelled') {\n if (entry.payload !== undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Cancelled interrupt ${record.interruptId} must not include a payload.`,\n ),\n )\n }\n } else if (genericRequest !== undefined) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: genericRequest.definition.responseSchema,\n value: entry.payload,\n code: 'invalid-payload',\n label: `Interrupt ${record.interruptId} payload is invalid`,\n })\n } else if (responseSchema !== undefined) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: responseSchema,\n value: entry.payload,\n code: 'invalid-payload',\n label: `Interrupt ${record.interruptId} payload is invalid`,\n })\n }\n continue\n }\n\n if (entry.status === 'cancelled') {\n if (entry.payload !== undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Cancelled interrupt ${record.interruptId} must not include a payload.`,\n ),\n )\n }\n continue\n }\n\n const tool = runtimeTool(input.tools, binding.toolName)\n if (!tool) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} is unavailable for interrupt ${record.interruptId}.`,\n { source: 'server' },\n ),\n )\n continue\n }\n\n let approval: ReturnType<typeof normalizeApprovalSchema> | undefined\n let schemaDrifted = false\n if (binding.kind === 'client-tool-execution') {\n if (hashSchemaInput(tool.outputSchema) !== binding.outputSchemaHash) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} output schema has changed.`,\n { source: 'server' },\n ),\n )\n schemaDrifted = true\n }\n } else {\n try {\n approval = normalizeApprovalSchema(\n tool.approvalSchema,\n tool.inputSchema,\n )\n } catch {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} approval schema is unavailable.`,\n { source: 'server' },\n ),\n )\n schemaDrifted = true\n }\n if (\n approval !== undefined &&\n (hashSchemaInput(tool.inputSchema) !== binding.inputSchemaHash ||\n approval.approvalSchemaHash !== binding.approvalSchemaHash ||\n approval.responseSchemaHash !== binding.responseSchemaHash)\n ) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'stale',\n `Tool ${binding.toolName} approval schema has changed.`,\n { source: 'server' },\n ),\n )\n schemaDrifted = true\n }\n }\n\n if (schemaDrifted) continue\n\n if (binding.kind === 'client-tool-execution') {\n const result = clientToolResult(entry)\n if (!result) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-tool-output',\n `Tool ${binding.toolName} result is invalid.`,\n ),\n )\n continue\n }\n if (result.state === 'output-available' && responseSchema !== undefined) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: responseSchema,\n value: entry.payload,\n code: 'invalid-tool-output',\n label: `Tool ${binding.toolName} output is invalid`,\n })\n }\n if (\n result.state === 'output-available' &&\n tool.outputSchema !== undefined\n ) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: tool.outputSchema,\n value: result.output,\n code: 'invalid-tool-output',\n label: `Tool ${binding.toolName} output is invalid`,\n })\n }\n continue\n }\n\n if (approval === undefined) continue\n const envelope = objectValue(entry.payload)\n const approved =\n typeof entry.payload === 'boolean'\n ? entry.payload\n : typeof envelope?.approved === 'boolean'\n ? envelope.approved\n : undefined\n if (approved === undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Approval ${record.interruptId} must be a boolean or decision envelope.`,\n ),\n )\n continue\n }\n if (envelope) {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: approval.responseSchema,\n value: entry.payload,\n code: 'invalid-payload',\n label: `Approval ${record.interruptId} envelope is invalid`,\n })\n }\n if (approved && envelope?.editedArgs !== undefined) {\n if (tool.inputSchema === undefined) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-edited-args',\n `Approval ${record.interruptId} cannot edit arguments without an input schema.`,\n ),\n )\n } else {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: tool.inputSchema,\n value: envelope.editedArgs,\n code: 'invalid-edited-args',\n label: `Approval ${record.interruptId} edited arguments are invalid`,\n })\n }\n }\n const branch = approved\n ? approval.branches.approve\n : approval.branches.reject\n if (branch) {\n if (!envelope) {\n errors.push(\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Approval ${record.interruptId} requires a payload for the ${approved ? 'approve' : 'reject'} decision.`,\n ),\n )\n } else {\n await pushSchemaIssues({\n request: input,\n errors,\n interruptId: record.interruptId,\n schema: branch.source,\n value: envelope.payload,\n code: 'invalid-payload',\n label: `Approval ${record.interruptId} payload is invalid`,\n })\n }\n }\n }\n\n for (const entry of input.resume ?? []) {\n if (!pendingById.has(entry.interruptId)) {\n incomplete = true\n group(entry.interruptId).push(\n interruptItemError(\n input,\n entry.interruptId,\n 'unknown-interrupt',\n `Resume entry references unknown interrupt ${entry.interruptId}.`,\n ),\n )\n }\n }\n\n if (incomplete) {\n batchErrors.push({\n scope: 'batch',\n threadId: input.threadId,\n interruptedRunId: input.interruptedRunId,\n generation: input.generation,\n code: 'incomplete-batch',\n message:\n 'Resume entries must resolve or cancel the complete interrupt batch.',\n source: 'client',\n retryable: false,\n interruptIds: input.pending.map((record) => record.interruptId),\n })\n }\n\n const itemErrors = [...grouped.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .flatMap(([, errors]) => errors)\n if (itemErrors.length > 0) {\n batchErrors.push({\n scope: 'batch',\n threadId: input.threadId,\n interruptedRunId: input.interruptedRunId,\n generation: input.generation,\n code: 'item-validation-failed',\n message: 'One or more interrupt resolutions are invalid.',\n source: 'client',\n retryable: false,\n interruptIds: input.pending.map((record) => record.interruptId),\n })\n return { errors: [...itemErrors, ...batchErrors] }\n }\n\n const canonical = canonicalizeInterruptResolutions(input.resume ?? [])\n const approvals = new Map<string, ToolApprovalResolution>()\n const clientToolResults = new Map<string, unknown>()\n const clientToolErrors = new Map<string, string>()\n const genericInterrupts = new Map<\n string,\n | { interruptId: string; status: 'resolved'; payload: unknown }\n | { interruptId: string; status: 'cancelled' }\n >()\n const deniedToolResults = new Map<string, unknown>()\n const cancelledToolCallIds = new Set<string>()\n\n for (const record of input.pending) {\n const entry = resumeById.get(record.interruptId)\n if (!entry) continue\n const binding = record.binding\n if (binding.kind === 'generic') {\n if (entry.status !== 'resolved') {\n genericInterrupts.set(record.interruptId, {\n interruptId: record.interruptId,\n status: 'cancelled',\n })\n continue\n }\n if (record.genericRequest === undefined) {\n genericInterrupts.set(record.interruptId, {\n interruptId: record.interruptId,\n status: 'resolved',\n payload: entry.payload,\n })\n continue\n }\n const parsed = await parseSchemaValue(\n record.genericRequest.definition.responseSchema,\n entry.payload,\n )\n if (!parsed.success) {\n return {\n errors: [\n interruptItemError(\n input,\n record.interruptId,\n 'invalid-payload',\n `Interrupt ${record.interruptId} payload is invalid.`,\n ),\n ],\n }\n }\n genericInterrupts.set(record.interruptId, {\n interruptId: record.interruptId,\n status: 'resolved',\n payload: parsed.data,\n })\n continue\n }\n if (entry.status === 'cancelled') {\n cancelledToolCallIds.add(binding.toolCallId)\n continue\n }\n if (binding.kind === 'client-tool-execution') {\n const result = clientToolResult(entry)\n if (!result) continue\n if (result.state === 'output-error') {\n clientToolErrors.set(binding.toolCallId, result.errorText)\n } else {\n clientToolResults.set(binding.toolCallId, result.output)\n }\n continue\n }\n const envelope = objectValue(entry.payload)\n const resolution: ToolApprovalResolution =\n typeof entry.payload === 'boolean'\n ? entry.payload\n : envelope?.approved === true\n ? {\n approved: true,\n ...(envelope.editedArgs !== undefined\n ? { editedArgs: envelope.editedArgs }\n : {}),\n ...(envelope.payload !== undefined\n ? { payload: envelope.payload }\n : {}),\n }\n : {\n approved: false,\n ...(envelope?.payload !== undefined\n ? { payload: envelope.payload }\n : {}),\n }\n approvals.set(binding.toolCallId, resolution)\n if (\n resolution === false ||\n (typeof resolution === 'object' && !resolution.approved)\n ) {\n deniedToolResults.set(\n binding.toolCallId,\n typeof resolution === 'object' ? resolution.payload : undefined,\n )\n }\n }\n\n return {\n errors: [],\n resolutions: canonical.resolutions,\n canonicalResolutions: canonical.canonicalResolutions,\n fingerprint: canonical.fingerprint,\n resumeToolState: {\n approvals,\n clientToolResults,\n clientToolErrors,\n genericInterrupts,\n deniedToolResults,\n cancelledToolCallIds,\n },\n }\n}\n\n/**\n * Is this a binding written by a version of the protocol we understand?\n *\n * A missing `v` is read as {@link INTERRUPT_BINDING_VERSION} so bindings\n * written before the field existed still resume. A `v` we don't recognise is\n * rejected outright — a newer or foreign producer's binding must not be\n * duck-typed into ours.\n */\nfunction isSupportedBindingVersion(raw: Record<string, unknown>): boolean {\n const version = raw['v']\n if (version === undefined) return true\n return version === INTERRUPT_BINDING_VERSION\n}\n\nexport function readUnopenedInterruptBinding(\n descriptor: Interrupt,\n): UnopenedInterruptBinding | undefined {\n const metadata = objectValue(descriptor.metadata)\n const raw = metadata\n ? objectValue(metadata[interruptBindingMetadataKey])\n : null\n if (!raw || stringField(raw, 'interruptId') !== descriptor.id)\n return undefined\n if (!isSupportedBindingVersion(raw)) return undefined\n const kind = stringField(raw, 'kind')\n const interruptId = stringField(raw, 'interruptId')\n const responseSchemaHash = stringField(raw, 'responseSchemaHash')\n const expiresAt = stringField(raw, 'expiresAt')\n if (!interruptId || responseSchemaHash === '') return undefined\n if (expiresAt !== undefined && !Number.isFinite(Date.parse(expiresAt))) {\n return undefined\n }\n const v = INTERRUPT_BINDING_VERSION\n if (kind === 'generic') {\n const definitionId = stringField(raw, 'definitionId')\n const key = stringField(raw, 'key')\n const batchIndex = raw['batchIndex']\n const payloadSchemaHash = stringField(raw, 'payloadSchemaHash')\n const hasFirstPartyFields =\n definitionId !== undefined ||\n key !== undefined ||\n batchIndex !== undefined ||\n payloadSchemaHash !== undefined\n if (\n hasFirstPartyFields &&\n (!definitionId ||\n !key ||\n typeof batchIndex !== 'number' ||\n !Number.isInteger(batchIndex) ||\n batchIndex < 0)\n ) {\n return undefined\n }\n return {\n v,\n kind,\n interruptId,\n ...(responseSchemaHash ? { responseSchemaHash } : {}),\n ...(expiresAt ? { expiresAt } : {}),\n ...(definitionId ? { definitionId } : {}),\n ...(key ? { key } : {}),\n ...(typeof batchIndex === 'number' ? { batchIndex } : {}),\n ...(payloadSchemaHash ? { payloadSchemaHash } : {}),\n }\n }\n if (!responseSchemaHash) return undefined\n const toolName = stringField(raw, 'toolName')\n const toolCallId = stringField(raw, 'toolCallId')\n if (!toolName || !toolCallId) return undefined\n if (kind === 'client-tool-execution') {\n const outputSchemaHash = stringField(raw, 'outputSchemaHash')\n if (!outputSchemaHash) return undefined\n return {\n v,\n kind,\n interruptId,\n toolName,\n toolCallId,\n outputSchemaHash,\n responseSchemaHash,\n ...(expiresAt ? { expiresAt } : {}),\n }\n }\n if (kind === 'tool-approval') {\n const inputSchemaHash = stringField(raw, 'inputSchemaHash')\n const approvalSchemaHash = stringField(raw, 'approvalSchemaHash')\n if (!inputSchemaHash || !approvalSchemaHash) return undefined\n return {\n v,\n kind,\n interruptId,\n toolName,\n toolCallId,\n originalArgs: raw.originalArgs,\n inputSchemaHash,\n approvalSchemaHash,\n responseSchemaHash,\n ...(expiresAt ? { expiresAt } : {}),\n }\n }\n return undefined\n}\n\n/**\n * Attach a resume binding to an interrupt descriptor, under\n * {@link INTERRUPT_BINDING_METADATA_KEY}.\n *\n * This is the supported way to make an interrupt resumable by this package.\n * The descriptor keeps its AG-UI shape; only `metadata` gains the namespaced\n * key. Pass the unopened form (no `interruptedRunId` / `generation`) when\n * emitting from inside a run — those fields are stamped as the run finishes.\n */\nexport function withInterruptBinding(\n descriptor: Interrupt,\n binding: UnopenedInterruptBinding | InterruptBinding,\n): Interrupt {\n return {\n ...descriptor,\n metadata: {\n ...descriptor.metadata,\n [interruptBindingMetadataKey]: {\n ...binding,\n v: INTERRUPT_BINDING_VERSION,\n interruptId: descriptor.id,\n },\n },\n }\n}\n\n/**\n * Read the opened resume binding off a descriptor, or `undefined` when the\n * descriptor carries no binding of a version we understand.\n *\n * `undefined` means \"this interrupt is not ours to resume\" — it is not a\n * failure to recover from by inventing a binding.\n */\nexport function readInterruptBinding(\n descriptor: Interrupt,\n): InterruptBinding | undefined {\n const unopened = readUnopenedInterruptBinding(descriptor)\n if (!unopened) return undefined\n const metadata = objectValue(descriptor.metadata)\n const raw = metadata\n ? objectValue(metadata[interruptBindingMetadataKey])\n : null\n if (!raw) return undefined\n const interruptedRunId = stringField(raw, 'interruptedRunId')\n const generation = raw['generation']\n if (\n !interruptedRunId ||\n typeof generation !== 'number' ||\n !Number.isInteger(generation) ||\n generation < 0\n ) {\n return undefined\n }\n return { ...unopened, interruptedRunId, generation }\n}\n\nexport function withoutInterruptBinding(descriptor: Interrupt): Interrupt {\n const metadata = objectValue(descriptor.metadata)\n if (!metadata || !(interruptBindingMetadataKey in metadata)) return descriptor\n const publicMetadata = { ...metadata }\n delete publicMetadata[interruptBindingMetadataKey]\n return { ...descriptor, metadata: publicMetadata }\n}\n"],"mappings":"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"}
@@ -53,7 +53,9 @@ export interface OtelMiddlewareOptions {
53
53
  meter?: Meter;
54
54
  /**
55
55
  * When `true`, prompt and completion content is attached to iteration spans
56
- * as `gen_ai.*.message` / `gen_ai.choice` events. Defaults to `false` so
56
+ * as `gen_ai.*.message` / `gen_ai.choice` events. Media spans get the
57
+ * prompt, input media and output (URLs or transcript text) as
58
+ * `gen_ai.input.messages` / `gen_ai.output.messages`. Defaults to `false` so
57
59
  * that PII never lands on a span by accident.
58
60
  */
59
61
  captureContent?: boolean;
@@ -66,7 +68,8 @@ export interface OtelMiddlewareOptions {
66
68
  redact?: (text: string) => string;
67
69
  /**
68
70
  * Maximum characters kept in the per-iteration assistant text buffer used
69
- * to emit `gen_ai.choice` events. Extra characters are truncated with a
71
+ * to emit `gen_ai.choice` events, and in each text part of a media span's
72
+ * input and output. Extra characters are truncated with a
70
73
  * trailing `"…"` marker. Defaults to 100 000. Set to `0` to disable the
71
74
  * cap. Exporters typically truncate long attribute values anyway.
72
75
  */
@@ -72,6 +72,94 @@ function serializeContent(content) {
72
72
  }
73
73
  return parts.join(" ");
74
74
  }
75
+ /**
76
+ * Structured form of `ContentPart[]` for `gen_ai.input.messages`, using the
77
+ * OTel GenAI semconv part shapes. URL and file-handle media keep their
78
+ * reference; inline bytes (and `data:` URLs) stay a `[type]` placeholder so
79
+ * they never blow attribute size limits. `redact` runs on text parts only.
80
+ */
81
+ function serializeParts(content, redact) {
82
+ const parts = [];
83
+ for (const part of content) {
84
+ if (!part || typeof part !== "object") continue;
85
+ const p = part;
86
+ if (p.type === "text") {
87
+ parts.push({
88
+ type: "text",
89
+ content: redact((p.text ?? p.content ?? "").toString())
90
+ });
91
+ continue;
92
+ }
93
+ const modality = p.type ?? "unknown";
94
+ const { type, value, mimeType } = p.source ?? {};
95
+ const mime = mimeType ? { mime_type: mimeType } : {};
96
+ if (type === "url" && value && !value.startsWith("data:")) parts.push({
97
+ type: "uri",
98
+ modality,
99
+ uri: value,
100
+ ...mime
101
+ });
102
+ else if (type === "file" && value) parts.push({
103
+ type: "file",
104
+ modality,
105
+ file_id: value,
106
+ ...mime
107
+ });
108
+ else parts.push({
109
+ type: "text",
110
+ content: `[${modality}]`
111
+ });
112
+ }
113
+ return parts;
114
+ }
115
+ /** A media reference as a content part, so `serializeParts` can map it. */
116
+ function mediaPart(modality, url) {
117
+ return {
118
+ type: modality,
119
+ source: typeof url === "string" ? {
120
+ type: "url",
121
+ value: url
122
+ } : void 0
123
+ };
124
+ }
125
+ /**
126
+ * Content parts for a media call's inputs, read from `artifactInputs`: the
127
+ * prompt (a string or `MediaPrompt` parts), TTS `text`, and transcription
128
+ * `audio`. Audio is a reference only when it is an http(s) URL; a base64
129
+ * string, `File` or `Blob` becomes a placeholder.
130
+ */
131
+ function mediaInputParts(inputs) {
132
+ if (!inputs || typeof inputs !== "object") return [];
133
+ const { prompt, text, audio } = inputs;
134
+ const parts = [];
135
+ for (const value of [prompt, text]) if (typeof value === "string") parts.push({
136
+ type: "text",
137
+ content: value
138
+ });
139
+ else if (Array.isArray(value)) parts.push(...value);
140
+ if (audio !== void 0) {
141
+ const url = typeof audio === "string" && /^https?:\/\//.test(audio) ? audio : null;
142
+ parts.push(mediaPart("audio", url));
143
+ }
144
+ return parts;
145
+ }
146
+ /**
147
+ * Content parts for a media call's result: generated image, audio or video
148
+ * URLs, and transcript text. Base64 output becomes a placeholder.
149
+ */
150
+ function mediaOutputParts(activity, result) {
151
+ if (!result || typeof result !== "object") return [];
152
+ const r = result;
153
+ const parts = [];
154
+ if (Array.isArray(r.images)) for (const image of r.images) parts.push(mediaPart("image", image?.url));
155
+ if (r.audio !== void 0) parts.push(mediaPart("audio", r.audio?.url));
156
+ if (activity === "video" && typeof r.url === "string") parts.push(mediaPart("video", r.url));
157
+ if (typeof r.text === "string") parts.push({
158
+ type: "text",
159
+ content: r.text
160
+ });
161
+ return parts;
162
+ }
75
163
  function messageEventName(role) {
76
164
  switch (role) {
77
165
  case "user": return "gen_ai.user.message";
@@ -146,6 +234,17 @@ function otelMiddleware(options) {
146
234
  ...errorType ? { "error.type": errorType } : {}
147
235
  });
148
236
  };
237
+ const redactMediaText = (text) => redactContent(maxContentLength > 0 && text.length > maxContentLength ? text.slice(0, maxContentLength) + "…" : text);
238
+ const setMediaMessages = (span, direction, parts) => {
239
+ const content = serializeParts(parts, redactMediaText);
240
+ if (content.length === 0) return;
241
+ const json = JSON.stringify([{
242
+ role: direction === "input" ? "user" : "assistant",
243
+ content
244
+ }]);
245
+ span.setAttribute(`gen_ai.${direction}.messages`, json);
246
+ span.setAttribute(`langfuse.observation.${direction}`, json);
247
+ };
149
248
  const startMediaSpan = (ctx) => {
150
249
  safeCall("otel.onStart", () => {
151
250
  const operationName = OPERATION_NAME[ctx.activity];
@@ -167,6 +266,12 @@ function otelMiddleware(options) {
167
266
  const enriched = safeCall("otel.attributeEnricher", () => attributeEnricher?.(info));
168
267
  if (enriched) span.setAttributes(enriched);
169
268
  mediaSpans.set(ctx, span);
269
+ if (captureContent) {
270
+ setMediaMessages(span, "input", mediaInputParts(ctx.artifactInputs));
271
+ ctx.resultTransforms.push((result) => {
272
+ safeCall("otel.captureOutput", () => setMediaMessages(span, "output", mediaOutputParts(ctx.activity, result)));
273
+ });
274
+ }
170
275
  });
171
276
  };
172
277
  const endMediaSpan = (ctx, finalize) => {
@@ -277,6 +382,15 @@ function otelMiddleware(options) {
277
382
  content: redactContent(sys)
278
383
  });
279
384
  for (const m of config.messages) {
385
+ if (Array.isArray(m.content)) {
386
+ const parts = serializeParts(m.content, redactContent);
387
+ if (parts.length === 0) continue;
388
+ inputMessages.push({
389
+ role: m.role,
390
+ content: parts
391
+ });
392
+ continue;
393
+ }
280
394
  const body = serializeContent(m.content);
281
395
  if (body.length === 0) continue;
282
396
  inputMessages.push({