@tanstack/ai 0.46.0 → 0.47.2
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.
- package/dist/esm/activities/chat/index.d.ts +36 -11
- package/dist/esm/activities/chat/index.js +440 -79
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.d.ts +1 -0
- package/dist/esm/activities/chat/messages.js +12 -7
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.d.ts +7 -2
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +10 -3
- package/dist/esm/activities/chat/middleware/compose.js +55 -0
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.d.ts +6 -3
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/generic-interrupts.d.ts +13 -0
- package/dist/esm/activities/chat/middleware/generic-interrupts.js +8 -0
- package/dist/esm/activities/chat/middleware/generic-interrupts.js.map +1 -0
- package/dist/esm/activities/chat/middleware/index.d.ts +4 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +54 -3
- package/dist/esm/activities/chat/middleware/types.js +16 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -0
- package/dist/esm/activities/chat/stream/processor.js +18 -5
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +6 -0
- package/dist/esm/adapter-internals.js +4 -1
- package/dist/esm/client.d.ts +4 -0
- package/dist/esm/client.js +3 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/generic-interrupt-continuation.d.ts +45 -0
- package/dist/esm/generic-interrupt-continuation.js +80 -0
- package/dist/esm/generic-interrupt-continuation.js.map +1 -0
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/interrupt-definition.d.ts +113 -0
- package/dist/esm/interrupt-definition.js +169 -0
- package/dist/esm/interrupt-definition.js.map +1 -0
- package/dist/esm/interrupt-resume.d.ts +3 -0
- package/dist/esm/interrupt-resume.js +77 -16
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/interrupts.d.ts +12 -3
- package/dist/esm/interrupts.js.map +1 -1
- package/dist/esm/types.d.ts +11 -3
- package/dist/esm/utilities/chat-params.js +10 -1
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/media-generation/SKILL.md +4 -1
- package/skills/ai-core/middleware/SKILL.md +53 -44
- package/skills/ai-core/structured-outputs/SKILL.md +59 -55
- package/skills/ai-core/tool-calling/SKILL.md +54 -1
- package/src/activities/chat/index.ts +1030 -211
- package/src/activities/chat/messages.ts +11 -3
- package/src/activities/chat/middleware/builder.ts +29 -4
- package/src/activities/chat/middleware/compose.ts +95 -5
- package/src/activities/chat/middleware/define.ts +13 -3
- package/src/activities/chat/middleware/generic-interrupts.ts +26 -0
- package/src/activities/chat/middleware/index.ts +15 -0
- package/src/activities/chat/middleware/types.ts +127 -2
- package/src/activities/chat/stream/processor.ts +21 -0
- package/src/adapter-internals.ts +20 -0
- package/src/client.ts +20 -0
- package/src/generic-interrupt-continuation.ts +162 -0
- package/src/index.ts +34 -0
- package/src/interrupt-definition.ts +581 -0
- package/src/interrupt-resume.ts +156 -25
- package/src/interrupts.ts +13 -3
- package/src/types.ts +11 -3
- package/src/utilities/chat-params.ts +16 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InterruptBinding, InterruptSubmissionError, ItemInterruptErrorCode, UnopenedInterruptBinding } from './interrupts.js';
|
|
2
2
|
import { ChatMiddlewareConfig, ChatResumeToolState } from './activities/chat/middleware/types.js';
|
|
3
|
+
import { GenericInterruptRequest, InterruptDefinition } from './interrupt-definition.js';
|
|
3
4
|
import { Interrupt, RunAgentResumeItem } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* The `Interrupt.metadata` key under which this package's resume binding
|
|
@@ -17,6 +18,8 @@ export interface PendingInterruptResumeRecord {
|
|
|
17
18
|
interruptId: string;
|
|
18
19
|
payload: unknown;
|
|
19
20
|
binding: InterruptBinding;
|
|
21
|
+
/** Present for a first-party generic interrupt. */
|
|
22
|
+
genericRequest?: GenericInterruptRequest<InterruptDefinition<any, any, any, any>>;
|
|
20
23
|
}
|
|
21
24
|
export interface ValidateInterruptResumeBatchInput {
|
|
22
25
|
threadId: string;
|
|
@@ -61,6 +61,17 @@ async function validateSchemaValue(input) {
|
|
|
61
61
|
function runtimeTool(tools, name) {
|
|
62
62
|
return tools.find((tool) => tool.name === name);
|
|
63
63
|
}
|
|
64
|
+
async function parseSchemaValue(schema, value) {
|
|
65
|
+
if (!isStandardSchema(schema)) return {
|
|
66
|
+
success: true,
|
|
67
|
+
data: value
|
|
68
|
+
};
|
|
69
|
+
const result = await validateWithStandardSchema(schema, value);
|
|
70
|
+
return result.success ? {
|
|
71
|
+
success: true,
|
|
72
|
+
data: result.data
|
|
73
|
+
} : { success: false };
|
|
74
|
+
}
|
|
64
75
|
function descriptorResponseSchema(record) {
|
|
65
76
|
return objectValue(record.payload)?.responseSchema;
|
|
66
77
|
}
|
|
@@ -82,7 +93,9 @@ async function pushSchemaIssues(input) {
|
|
|
82
93
|
}
|
|
83
94
|
function validateDescriptorSchema(input, record, binding, errors) {
|
|
84
95
|
const schema = descriptorResponseSchema(record);
|
|
85
|
-
|
|
96
|
+
const responseSchemaHash = binding.responseSchemaHash;
|
|
97
|
+
if (schema === void 0 && responseSchemaHash === void 0) return;
|
|
98
|
+
if (schema === void 0 || responseSchemaHash === void 0 || schemaHash(schema) !== responseSchemaHash) errors.push(interruptItemError(input, record.interruptId, "invalid-response-schema", `Interrupt ${record.interruptId} response schema no longer matches its binding.`, { source: "server" }));
|
|
86
99
|
return schema;
|
|
87
100
|
}
|
|
88
101
|
/**
|
|
@@ -107,17 +120,24 @@ async function validateInterruptResumeBatch(input) {
|
|
|
107
120
|
if (!resumeById.has(entry.interruptId)) resumeById.set(entry.interruptId, entry);
|
|
108
121
|
}
|
|
109
122
|
for (const [interruptId, count] of counts) if (count > 1) group(interruptId).push(interruptItemError(input, interruptId, "conflict", `Interrupt ${interruptId} has duplicate resume entries.`));
|
|
123
|
+
const pendingGenerics = input.pending.filter((record) => record.binding.kind === "generic");
|
|
124
|
+
const genericBatchSatisfied = pendingGenerics.length > 0 && pendingGenerics.every((record) => resumeById.has(record.interruptId));
|
|
110
125
|
let incomplete = false;
|
|
111
126
|
for (const record of input.pending) {
|
|
112
127
|
const errors = group(record.interruptId);
|
|
113
128
|
const entry = resumeById.get(record.interruptId);
|
|
114
129
|
const binding = record.binding;
|
|
115
130
|
if (!entry) {
|
|
131
|
+
if (genericBatchSatisfied && binding.kind === "client-tool-execution") continue;
|
|
116
132
|
incomplete = true;
|
|
117
133
|
errors.push(interruptItemError(input, record.interruptId, "unknown-interrupt", `Missing resume entry for interrupt ${record.interruptId}.`));
|
|
118
134
|
}
|
|
119
135
|
if (binding.interruptedRunId !== input.interruptedRunId || binding.generation !== input.generation || binding.interruptId !== record.interruptId) errors.push(interruptItemError(input, record.interruptId, "stale", `Interrupt ${record.interruptId} has stale correlation metadata.`, { source: "server" }));
|
|
120
|
-
if (binding.expiresAt !== void 0
|
|
136
|
+
if (binding.expiresAt !== void 0) {
|
|
137
|
+
const expiresAt = Date.parse(binding.expiresAt);
|
|
138
|
+
if (!Number.isFinite(expiresAt)) errors.push(interruptItemError(input, record.interruptId, "invalid-payload", `Interrupt ${record.interruptId} has an invalid expiresAt.`, { source: "server" }));
|
|
139
|
+
else if (expiresAt <= (input.now ?? Date.now())) errors.push(interruptItemError(input, record.interruptId, "expired", `Interrupt ${record.interruptId} has expired.`, { source: "server" }));
|
|
140
|
+
}
|
|
121
141
|
const responseSchema = validateDescriptorSchema(input, record, binding, errors);
|
|
122
142
|
if (!entry) continue;
|
|
123
143
|
const entryStatus = entry.status;
|
|
@@ -126,9 +146,23 @@ async function validateInterruptResumeBatch(input) {
|
|
|
126
146
|
continue;
|
|
127
147
|
}
|
|
128
148
|
if (binding.kind === "generic") {
|
|
149
|
+
const genericRequest = record.genericRequest;
|
|
150
|
+
if (genericRequest !== void 0) {
|
|
151
|
+
const batchIndex = binding.batchIndex;
|
|
152
|
+
if (binding.definitionId !== genericRequest.definition.id || binding.key !== genericRequest.key || binding.interruptId !== record.interruptId || batchIndex === void 0 || !Number.isInteger(batchIndex) || batchIndex < 0) errors.push(interruptItemError(input, record.interruptId, "stale", `Generic interrupt ${record.interruptId} has stale definition metadata.`, { source: "server" }));
|
|
153
|
+
}
|
|
129
154
|
if (entry.status === "cancelled") {
|
|
130
155
|
if (entry.payload !== void 0) errors.push(interruptItemError(input, record.interruptId, "invalid-payload", `Cancelled interrupt ${record.interruptId} must not include a payload.`));
|
|
131
|
-
} else if (
|
|
156
|
+
} else if (genericRequest !== void 0) await pushSchemaIssues({
|
|
157
|
+
request: input,
|
|
158
|
+
errors,
|
|
159
|
+
interruptId: record.interruptId,
|
|
160
|
+
schema: genericRequest.definition.responseSchema,
|
|
161
|
+
value: entry.payload,
|
|
162
|
+
code: "invalid-payload",
|
|
163
|
+
label: `Interrupt ${record.interruptId} payload is invalid`
|
|
164
|
+
});
|
|
165
|
+
else if (responseSchema !== void 0) await pushSchemaIssues({
|
|
132
166
|
request: input,
|
|
133
167
|
errors,
|
|
134
168
|
interruptId: record.interruptId,
|
|
@@ -272,13 +306,27 @@ async function validateInterruptResumeBatch(input) {
|
|
|
272
306
|
if (!entry) continue;
|
|
273
307
|
const binding = record.binding;
|
|
274
308
|
if (binding.kind === "generic") {
|
|
275
|
-
|
|
309
|
+
if (entry.status !== "resolved") {
|
|
310
|
+
genericInterrupts.set(record.interruptId, {
|
|
311
|
+
interruptId: record.interruptId,
|
|
312
|
+
status: "cancelled"
|
|
313
|
+
});
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (record.genericRequest === void 0) {
|
|
317
|
+
genericInterrupts.set(record.interruptId, {
|
|
318
|
+
interruptId: record.interruptId,
|
|
319
|
+
status: "resolved",
|
|
320
|
+
payload: entry.payload
|
|
321
|
+
});
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const parsed = await parseSchemaValue(record.genericRequest.definition.responseSchema, entry.payload);
|
|
325
|
+
if (!parsed.success) return { errors: [interruptItemError(input, record.interruptId, "invalid-payload", `Interrupt ${record.interruptId} payload is invalid.`)] };
|
|
326
|
+
genericInterrupts.set(record.interruptId, {
|
|
276
327
|
interruptId: record.interruptId,
|
|
277
328
|
status: "resolved",
|
|
278
|
-
payload:
|
|
279
|
-
} : {
|
|
280
|
-
interruptId: record.interruptId,
|
|
281
|
-
status: "cancelled"
|
|
329
|
+
payload: parsed.data
|
|
282
330
|
});
|
|
283
331
|
continue;
|
|
284
332
|
}
|
|
@@ -338,15 +386,28 @@ function readUnopenedInterruptBinding(descriptor) {
|
|
|
338
386
|
const interruptId = stringField(raw, "interruptId");
|
|
339
387
|
const responseSchemaHash = stringField(raw, "responseSchemaHash");
|
|
340
388
|
const expiresAt = stringField(raw, "expiresAt");
|
|
341
|
-
if (!interruptId ||
|
|
389
|
+
if (!interruptId || responseSchemaHash === "") return void 0;
|
|
390
|
+
if (expiresAt !== void 0 && !Number.isFinite(Date.parse(expiresAt))) return;
|
|
342
391
|
const v = 1;
|
|
343
|
-
if (kind === "generic")
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
392
|
+
if (kind === "generic") {
|
|
393
|
+
const definitionId = stringField(raw, "definitionId");
|
|
394
|
+
const key = stringField(raw, "key");
|
|
395
|
+
const batchIndex = raw["batchIndex"];
|
|
396
|
+
const payloadSchemaHash = stringField(raw, "payloadSchemaHash");
|
|
397
|
+
if ((definitionId !== void 0 || key !== void 0 || batchIndex !== void 0 || payloadSchemaHash !== void 0) && (!definitionId || !key || typeof batchIndex !== "number" || !Number.isInteger(batchIndex) || batchIndex < 0)) return;
|
|
398
|
+
return {
|
|
399
|
+
v,
|
|
400
|
+
kind,
|
|
401
|
+
interruptId,
|
|
402
|
+
...responseSchemaHash ? { responseSchemaHash } : {},
|
|
403
|
+
...expiresAt ? { expiresAt } : {},
|
|
404
|
+
...definitionId ? { definitionId } : {},
|
|
405
|
+
...key ? { key } : {},
|
|
406
|
+
...typeof batchIndex === "number" ? { batchIndex } : {},
|
|
407
|
+
...payloadSchemaHash ? { payloadSchemaHash } : {}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
if (!responseSchemaHash) return void 0;
|
|
350
411
|
const toolName = stringField(raw, "toolName");
|
|
351
412
|
const toolCallId = stringField(raw, "toolCallId");
|
|
352
413
|
if (!toolName || !toolCallId) return void 0;
|
|
@@ -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 { 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}\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\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 if (\n schema === undefined ||\n schemaHash(schema) !== binding.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 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 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 (\n binding.expiresAt !== undefined &&\n Date.parse(binding.expiresAt) <= (input.now ?? Date.now())\n ) {\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 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 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 (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 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 (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 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 genericInterrupts.set(\n record.interruptId,\n entry.status === 'resolved'\n ? {\n interruptId: record.interruptId,\n status: 'resolved',\n payload: entry.payload,\n }\n : { interruptId: record.interruptId, status: 'cancelled' },\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 const v = INTERRUPT_BINDING_VERSION\n if (kind === 'generic') {\n return {\n v,\n kind,\n interruptId,\n responseSchemaHash,\n ...(expiresAt ? { expiresAt } : {}),\n }\n }\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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"}
|
|
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 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 (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 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":"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"}
|
package/dist/esm/interrupts.d.ts
CHANGED
|
@@ -48,23 +48,32 @@ interface InterruptBindingBase {
|
|
|
48
48
|
interruptId: string;
|
|
49
49
|
interruptedRunId: string;
|
|
50
50
|
generation: number;
|
|
51
|
-
responseSchemaHash: string;
|
|
52
51
|
expiresAt?: string;
|
|
53
52
|
}
|
|
54
|
-
|
|
53
|
+
interface ResponseSchemaInterruptBindingBase extends InterruptBindingBase {
|
|
54
|
+
responseSchemaHash: string;
|
|
55
|
+
}
|
|
56
|
+
export type InterruptBinding = (ResponseSchemaInterruptBindingBase & {
|
|
55
57
|
kind: 'tool-approval';
|
|
56
58
|
toolName: string;
|
|
57
59
|
toolCallId: string;
|
|
58
60
|
originalArgs: unknown;
|
|
59
61
|
inputSchemaHash: string;
|
|
60
62
|
approvalSchemaHash: string;
|
|
61
|
-
}) | (
|
|
63
|
+
}) | (ResponseSchemaInterruptBindingBase & {
|
|
62
64
|
kind: 'client-tool-execution';
|
|
63
65
|
toolName: string;
|
|
64
66
|
toolCallId: string;
|
|
65
67
|
outputSchemaHash: string;
|
|
66
68
|
}) | (InterruptBindingBase & {
|
|
67
69
|
kind: 'generic';
|
|
70
|
+
/** Omitted when the generic interrupt accepts an unvalidated response. */
|
|
71
|
+
responseSchemaHash?: string;
|
|
72
|
+
/** Present only for a first-party generic interrupt. */
|
|
73
|
+
definitionId?: string;
|
|
74
|
+
key?: string;
|
|
75
|
+
batchIndex?: number;
|
|
76
|
+
payloadSchemaHash?: string;
|
|
68
77
|
});
|
|
69
78
|
export type UnopenedInterruptBinding = InterruptBinding extends infer TBinding ? TBinding extends InterruptBinding ? Omit<TBinding, 'interruptedRunId' | 'generation'> : never : never;
|
|
70
79
|
export type ToolApprovalResolution = boolean | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interrupts.js","names":[],"sources":["../../src/interrupts.ts"],"sourcesContent":["import {\n canonicalInterruptJson,\n cloneAndDeepFreezeJson,\n digestInterruptJson,\n} from './interrupt-serialization'\nimport type { RunAgentResumeItem } from './types'\n\nexport interface InterruptCorrelation {\n threadId: string\n interruptedRunId: string\n generation: number\n submissionId?: string\n continuationRunId?: string\n}\n\nexport type ItemInterruptErrorCode =\n | 'invalid-payload'\n | 'invalid-edited-args'\n | 'invalid-tool-output'\n | 'invalid-response-schema'\n | 'unknown-interrupt'\n | 'expired'\n | 'stale'\n | 'conflict'\n | 'legacy-unsupported'\n\nexport type BatchInterruptErrorCode =\n | 'incomplete-batch'\n | 'item-validation-failed'\n | 'unsupported-bulk-operation'\n | 'async-resolver'\n | 'inactive-transaction'\n | 'mixed-provenance'\n | 'transport'\n | 'server'\n | 'protocol'\n | 'invalid-response-schema'\n | 'expired'\n | 'stale'\n | 'conflict'\n | 'legacy-submit-failed'\n\nexport interface ItemInterruptError extends InterruptCorrelation {\n scope: 'item'\n interruptId: string\n code: ItemInterruptErrorCode\n message: string\n path?: ReadonlyArray<string | number>\n source: 'client' | 'server'\n retryable: boolean\n}\n\nexport interface BatchInterruptError extends InterruptCorrelation {\n scope: 'batch'\n code: BatchInterruptErrorCode\n message: string\n source: 'client' | 'server' | 'transport'\n retryable: boolean\n interruptIds: ReadonlyArray<string>\n}\n\nexport type InterruptSubmissionError = ItemInterruptError | BatchInterruptError\n\n/**\n * Wire version of {@link InterruptBinding}.\n *\n * The binding is the only part of an AG-UI `Interrupt` that this package\n * claims — it rides in `metadata` under\n * {@link INTERRUPT_BINDING_METADATA_KEY} and tells the resume path how to\n * correlate an answer back to a paused run. Producers stamp `v`; readers\n * reject any version they don't understand rather than duck-typing the fields.\n *\n * That matters because an AG-UI `Interrupt` is a shared envelope. Another\n * producer — a workflow engine projecting a durable approval, a third-party\n * agent — can legitimately put its own binding in the same envelope. Versioning\n * makes \"not mine\" a clean rejection instead of a partial match that resumes\n * against the wrong owner.\n */\nexport const INTERRUPT_BINDING_VERSION = 1 as const\n\ninterface InterruptBindingBase {\n /** @see INTERRUPT_BINDING_VERSION */\n v: typeof INTERRUPT_BINDING_VERSION\n interruptId: string\n interruptedRunId: string\n generation: number\n
|
|
1
|
+
{"version":3,"file":"interrupts.js","names":[],"sources":["../../src/interrupts.ts"],"sourcesContent":["import {\n canonicalInterruptJson,\n cloneAndDeepFreezeJson,\n digestInterruptJson,\n} from './interrupt-serialization'\nimport type { RunAgentResumeItem } from './types'\n\nexport interface InterruptCorrelation {\n threadId: string\n interruptedRunId: string\n generation: number\n submissionId?: string\n continuationRunId?: string\n}\n\nexport type ItemInterruptErrorCode =\n | 'invalid-payload'\n | 'invalid-edited-args'\n | 'invalid-tool-output'\n | 'invalid-response-schema'\n | 'unknown-interrupt'\n | 'expired'\n | 'stale'\n | 'conflict'\n | 'legacy-unsupported'\n\nexport type BatchInterruptErrorCode =\n | 'incomplete-batch'\n | 'item-validation-failed'\n | 'unsupported-bulk-operation'\n | 'async-resolver'\n | 'inactive-transaction'\n | 'mixed-provenance'\n | 'transport'\n | 'server'\n | 'protocol'\n | 'invalid-response-schema'\n | 'expired'\n | 'stale'\n | 'conflict'\n | 'legacy-submit-failed'\n\nexport interface ItemInterruptError extends InterruptCorrelation {\n scope: 'item'\n interruptId: string\n code: ItemInterruptErrorCode\n message: string\n path?: ReadonlyArray<string | number>\n source: 'client' | 'server'\n retryable: boolean\n}\n\nexport interface BatchInterruptError extends InterruptCorrelation {\n scope: 'batch'\n code: BatchInterruptErrorCode\n message: string\n source: 'client' | 'server' | 'transport'\n retryable: boolean\n interruptIds: ReadonlyArray<string>\n}\n\nexport type InterruptSubmissionError = ItemInterruptError | BatchInterruptError\n\n/**\n * Wire version of {@link InterruptBinding}.\n *\n * The binding is the only part of an AG-UI `Interrupt` that this package\n * claims — it rides in `metadata` under\n * {@link INTERRUPT_BINDING_METADATA_KEY} and tells the resume path how to\n * correlate an answer back to a paused run. Producers stamp `v`; readers\n * reject any version they don't understand rather than duck-typing the fields.\n *\n * That matters because an AG-UI `Interrupt` is a shared envelope. Another\n * producer — a workflow engine projecting a durable approval, a third-party\n * agent — can legitimately put its own binding in the same envelope. Versioning\n * makes \"not mine\" a clean rejection instead of a partial match that resumes\n * against the wrong owner.\n */\nexport const INTERRUPT_BINDING_VERSION = 1 as const\n\ninterface InterruptBindingBase {\n /** @see INTERRUPT_BINDING_VERSION */\n v: typeof INTERRUPT_BINDING_VERSION\n interruptId: string\n interruptedRunId: string\n generation: number\n expiresAt?: string\n}\n\ninterface ResponseSchemaInterruptBindingBase extends InterruptBindingBase {\n responseSchemaHash: string\n}\n\nexport type InterruptBinding =\n | (ResponseSchemaInterruptBindingBase & {\n kind: 'tool-approval'\n toolName: string\n toolCallId: string\n originalArgs: unknown\n inputSchemaHash: string\n approvalSchemaHash: string\n })\n | (ResponseSchemaInterruptBindingBase & {\n kind: 'client-tool-execution'\n toolName: string\n toolCallId: string\n outputSchemaHash: string\n })\n | (InterruptBindingBase & {\n kind: 'generic'\n /** Omitted when the generic interrupt accepts an unvalidated response. */\n responseSchemaHash?: string\n /** Present only for a first-party generic interrupt. */\n definitionId?: string\n key?: string\n batchIndex?: number\n payloadSchemaHash?: string\n })\n\nexport type UnopenedInterruptBinding = InterruptBinding extends infer TBinding\n ? TBinding extends InterruptBinding\n ? Omit<TBinding, 'interruptedRunId' | 'generation'>\n : never\n : never\n\nexport type ToolApprovalResolution =\n | boolean\n | {\n approved: true\n editedArgs?: unknown\n payload?: unknown\n }\n | {\n approved: false\n payload?: unknown\n editedArgs?: never\n }\n\nexport function canonicalizeInterruptResolutions(\n resolutions: ReadonlyArray<RunAgentResumeItem>,\n): {\n resolutions: ReadonlyArray<RunAgentResumeItem>\n canonicalResolutions: string\n fingerprint: string\n} {\n const sorted = [...resolutions].sort((left, right) =>\n left.interruptId.localeCompare(right.interruptId),\n )\n const frozen = cloneAndDeepFreezeJson(sorted)\n const canonicalResolutions = canonicalInterruptJson(frozen)\n return Object.freeze({\n resolutions: frozen,\n canonicalResolutions,\n fingerprint: digestInterruptJson(canonicalResolutions),\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8EA,IAAa,4BAA4B;AA4DzC,SAAgB,iCACd,aAKA;CACA,MAAM,SAAS,CAAC,GAAG,WAAW,CAAC,CAAC,MAAM,MAAM,UAC1C,KAAK,YAAY,cAAc,MAAM,WAAW,CAClD;CACA,MAAM,SAAS,uBAAuB,MAAM;CAC5C,MAAM,uBAAuB,uBAAuB,MAAM;CAC1D,OAAO,OAAO,OAAO;EACnB,aAAa;EACb;EACA,aAAa,oBAAoB,oBAAoB;CACvD,CAAC;AACH"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -250,6 +250,12 @@ export interface ModelMessage<TContent extends string | null | Array<ContentPart
|
|
|
250
250
|
content: string;
|
|
251
251
|
signature?: string;
|
|
252
252
|
}>;
|
|
253
|
+
/**
|
|
254
|
+
* Completed structured output represented by this assistant message.
|
|
255
|
+
* `content` remains the provider-facing JSON text; this field preserves the
|
|
256
|
+
* typed UI part across persistence and message conversion.
|
|
257
|
+
*/
|
|
258
|
+
structuredOutput?: StructuredOutputPart;
|
|
253
259
|
/**
|
|
254
260
|
* Optional stable message id. Providers ignore it; it exists so a persisted
|
|
255
261
|
* transcript can retain the streaming `messageId` and survive the
|
|
@@ -830,8 +836,7 @@ export interface TextOptions<TProviderOptionsSuperset extends Record<string, any
|
|
|
830
836
|
state?: unknown;
|
|
831
837
|
/**
|
|
832
838
|
* AG-UI interrupt resume responses supplied by the client on a follow-up run.
|
|
833
|
-
*
|
|
834
|
-
* upstream-native interrupts.
|
|
839
|
+
* A first-party generic item carries the original request in `metadata`.
|
|
835
840
|
*/
|
|
836
841
|
resume?: Array<RunAgentResumeItem>;
|
|
837
842
|
/**
|
|
@@ -900,7 +905,10 @@ export type { BilledUsage, BillingUnit, CompletionTokensDetails, PromptTokensDet
|
|
|
900
905
|
export type UsageTotals = TokenUsage;
|
|
901
906
|
export type Interrupt = AGUIInterrupt;
|
|
902
907
|
export type RunFinishedOutcome = AGUIRunFinishedOutcome;
|
|
903
|
-
export type RunAgentResumeItem = AGUIResumeEntry
|
|
908
|
+
export type RunAgentResumeItem = AGUIResumeEntry & {
|
|
909
|
+
/** AG-UI resume metadata. First-party generic requests ride here. */
|
|
910
|
+
metadata?: Record<string, unknown>;
|
|
911
|
+
};
|
|
904
912
|
/**
|
|
905
913
|
* Emitted when a run completes successfully.
|
|
906
914
|
*
|
|
@@ -8,7 +8,8 @@ var KNOWN_PART_TYPES = /* @__PURE__ */ new Set([
|
|
|
8
8
|
"document",
|
|
9
9
|
"tool-call",
|
|
10
10
|
"tool-result",
|
|
11
|
-
"thinking"
|
|
11
|
+
"thinking",
|
|
12
|
+
"structured-output"
|
|
12
13
|
]);
|
|
13
14
|
function isValidParts(value) {
|
|
14
15
|
if (!Array.isArray(value)) return false;
|
|
@@ -16,6 +17,10 @@ function isValidParts(value) {
|
|
|
16
17
|
if (!p || typeof p !== "object") return false;
|
|
17
18
|
const type = p.type;
|
|
18
19
|
if (typeof type !== "string" || !KNOWN_PART_TYPES.has(type)) return false;
|
|
20
|
+
if (type === "structured-output") {
|
|
21
|
+
const raw = p.raw;
|
|
22
|
+
if (raw !== void 0 && typeof raw !== "string") return false;
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
return true;
|
|
21
26
|
}
|
|
@@ -121,6 +126,10 @@ function validateResumeEntry(value, index) {
|
|
|
121
126
|
status
|
|
122
127
|
};
|
|
123
128
|
if (value.payload !== void 0) entry.payload = value.payload;
|
|
129
|
+
if (value.metadata !== void 0) {
|
|
130
|
+
if (!isRecord(value.metadata)) invalidBody(`${at}.metadata must be an object`);
|
|
131
|
+
entry.metadata = value.metadata;
|
|
132
|
+
}
|
|
124
133
|
return entry;
|
|
125
134
|
}
|
|
126
135
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-params.js","names":[],"sources":["../../../src/utilities/chat-params.ts"],"sourcesContent":["import { AGUIError } from '@ag-ui/core'\nimport type {\n Context as AGUIContext,\n Message as AGUIMessage,\n ResumeEntry as AGUIResumeEntry,\n Role as AGUIRole,\n} from '@ag-ui/core'\nimport type {\n AnyTool,\n JSONSchema,\n ModelMessage,\n RunAgentResumeItem,\n UIMessage,\n} from '../types'\n\nconst KNOWN_PART_TYPES = new Set([\n 'text',\n 'image',\n 'audio',\n 'video',\n 'document',\n 'tool-call',\n 'tool-result',\n 'thinking',\n])\n\nfunction isValidParts(value: unknown): value is Array<{ type: string }> {\n if (!Array.isArray(value)) return false\n for (const p of value) {\n if (!p || typeof p !== 'object') return false\n const type = (p as { type?: unknown }).type\n if (typeof type !== 'string' || !KNOWN_PART_TYPES.has(type)) return false\n }\n return true\n}\n\n/**\n * Keyed by `AGUIRole` so a role added upstream fails to compile here until it\n * is handled, rather than silently falling through as an unknown role.\n */\nconst AGUI_ROLES: Record<AGUIRole, true> = {\n developer: true,\n system: true,\n assistant: true,\n user: true,\n tool: true,\n activity: true,\n reasoning: true,\n}\n\nfunction isAGUIRole(value: unknown): value is AGUIRole {\n return typeof value === 'string' && value in AGUI_ROLES\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Reject the request body, pointing at the migration guide. Mirrors the\n * message the previous `RunAgentInputSchema.safeParse` failure produced.\n */\nfunction invalidBody(reason: string): never {\n throw new AGUIError(\n `Request body is not a valid AG-UI RunAgentInput. ` +\n `If you're upgrading from a previous @tanstack/ai-client release, ` +\n `see docs/migration/ag-ui-compliance.md. ` +\n `Validation errors: ${reason}`,\n )\n}\n\nfunction requireString(value: unknown, at: string): string {\n if (typeof value !== 'string') invalidBody(`${at} must be a string`)\n return value\n}\n\nfunction requireArray(value: unknown, at: string): Array<unknown> {\n if (!Array.isArray(value)) invalidBody(`${at} must be an array`)\n return value\n}\n\n/**\n * Assert one AG-UI `Message`, discriminating on `role` exactly as the upstream\n * `MessageSchema` discriminated union does. The record view is retained on the\n * asserted type so callers can still inspect non-AG-UI extras like `parts`.\n */\nfunction assertAGUIMessage(\n value: Record<string, unknown>,\n at: string,\n): asserts value is Record<string, unknown> & AGUIMessage {\n requireString(value.id, `${at}.id`)\n\n const role = value.role\n if (!isAGUIRole(role)) {\n invalidBody(\n `${at}.role must be one of ${Object.keys(AGUI_ROLES).join(' | ')}`,\n )\n }\n\n switch (role) {\n case 'assistant':\n // Both optional: a tool-calling turn carries no text content.\n if (value.content !== undefined) {\n requireString(value.content, `${at}.content`)\n }\n if (value.toolCalls !== undefined) {\n requireArray(value.toolCalls, `${at}.toolCalls`)\n }\n break\n case 'user':\n if (typeof value.content !== 'string' && !Array.isArray(value.content)) {\n invalidBody(\n `${at}.content must be a string or an array of content parts`,\n )\n }\n break\n case 'tool':\n requireString(value.content, `${at}.content`)\n requireString(value.toolCallId, `${at}.toolCallId`)\n break\n case 'activity':\n requireString(value.activityType, `${at}.activityType`)\n if (!isRecord(value.content)) {\n invalidBody(`${at}.content must be an object`)\n }\n break\n case 'developer':\n case 'system':\n case 'reasoning':\n requireString(value.content, `${at}.content`)\n break\n }\n}\n\nfunction validateMessage(value: unknown, index: number): AGUIMessage {\n const at = `messages[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n assertAGUIMessage(value, at)\n\n // `parts` is TanStack's canonical extra, carried through so the UIMessage\n // path inside `chat()` can use it. Keep it only when it holds recognized\n // part types — the previous schema-based path dropped `parts` during parse\n // and re-attached it from the raw body behind this same check.\n if ('parts' in value && !isValidParts(value.parts)) {\n const withoutParts = { ...value }\n Reflect.deleteProperty(withoutParts, 'parts')\n return withoutParts\n }\n return value\n}\n\nfunction validateTool(\n value: unknown,\n index: number,\n): { name: string; description: string; parameters: JSONSchema } {\n const at = `tools[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n return {\n name: requireString(value.name, `${at}.name`),\n description: requireString(value.description, `${at}.description`),\n // Upstream `ToolSchema` types this as optional `any`; it reaches the\n // provider as a raw JSON Schema either way.\n parameters: value.parameters as JSONSchema,\n }\n}\n\nfunction validateContext(value: unknown, index: number): AGUIContext {\n const at = `context[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n return {\n description: requireString(value.description, `${at}.description`),\n value: requireString(value.value, `${at}.value`),\n }\n}\n\nfunction validateResumeEntry(value: unknown, index: number): AGUIResumeEntry {\n const at = `resume[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n const status = value.status\n if (status !== 'resolved' && status !== 'cancelled') {\n invalidBody(`${at}.status must be \"resolved\" or \"cancelled\"`)\n }\n const entry: AGUIResumeEntry = {\n interruptId: requireString(value.interruptId, `${at}.interruptId`),\n status,\n }\n // Omit the key entirely when absent, matching the optional-field shape the\n // schema produced.\n if (value.payload !== undefined) entry.payload = value.payload\n return entry\n}\n\n/**\n * Parse and validate an HTTP request body as an AG-UI `RunAgentInput`.\n *\n * Returns a spread-friendly object whose `messages` field is suitable for\n * passing directly to `chat({ messages })`. The existing\n * `convertMessagesToModelMessages` handles AG-UI fan-out dedup and\n * reasoning/activity/developer-role normalization internally.\n *\n * Validated structurally against the AG-UI `RunAgentInput` contract without a\n * schema library, so this package pulls in no validation runtime of its own.\n *\n * @throws An error with a migration-pointing message when the body does\n * not conform to AG-UI `RunAgentInput`. Surface this as a\n * 400 Bad Request to the client.\n */\nexport async function chatParamsFromRequestBody(body: unknown): Promise<{\n messages: Array<UIMessage | ModelMessage>\n threadId: string\n runId: string\n parentRunId?: string\n tools: Array<{ name: string; description: string; parameters: JSONSchema }>\n forwardedProps: Record<string, unknown>\n state: unknown\n resume?: Array<RunAgentResumeItem>\n /**\n * @deprecated Use `aguiContext` instead. This alias will be removed in a\n * future release.\n */\n context: Array<AGUIContext>\n aguiContext: Array<AGUIContext>\n}> {\n if (!isRecord(body)) invalidBody('body must be a JSON object')\n\n const threadId = requireString(body.threadId, 'threadId')\n const runId = requireString(body.runId, 'runId')\n const parentRunId =\n body.parentRunId === undefined\n ? undefined\n : requireString(body.parentRunId, 'parentRunId')\n\n const messages = requireArray(body.messages, 'messages').map(validateMessage)\n const tools = requireArray(body.tools, 'tools').map(validateTool)\n const aguiContext = requireArray(body.context, 'context').map(validateContext)\n const resume =\n body.resume === undefined\n ? undefined\n : requireArray(body.resume, 'resume').map(validateResumeEntry)\n\n if (body.forwardedProps !== undefined && !isRecord(body.forwardedProps)) {\n invalidBody('forwardedProps must be an object')\n }\n\n return {\n // Unknown top-level fields (e.g. a legacy `cursor`) are dropped by\n // construction: only the fields below are copied onto the result.\n messages: messages as Array<UIMessage | ModelMessage>,\n threadId,\n runId,\n parentRunId,\n tools,\n forwardedProps: (body.forwardedProps ?? {}) as Record<string, unknown>,\n state: body.state,\n resume: resume as Array<RunAgentResumeItem> | undefined,\n context: aguiContext,\n aguiContext,\n }\n}\n\n/**\n * Read an HTTP `Request`, parse its JSON body, and validate it as an\n * AG-UI `RunAgentInput` — collapsing the standard `req.json()` +\n * `chatParamsFromRequestBody(...)` pair into a single call.\n *\n * On a malformed body or invalid AG-UI shape, this **throws a\n * `Response`** with status 400 and a migration-pointing message in the\n * body. Frameworks that natively handle thrown `Response` objects\n * (TanStack Start, SolidStart, Remix, React Router 7) will return the\n * 400 to the client automatically, so the handler reduces to:\n *\n * ```ts\n * export async function POST(req: Request) {\n * const params = await chatParamsFromRequest(req)\n * // ...use params\n * }\n * ```\n *\n * In frameworks that do not auto-handle thrown `Response` objects\n * (Next.js Route Handlers, SvelteKit, Hono, raw Node), wrap the call\n * with try/catch and return the caught Response yourself, or use\n * `chatParamsFromRequestBody` directly with your own JSON-parsing.\n *\n * @throws {Response} 400 on malformed JSON or invalid AG-UI shape.\n */\nexport async function chatParamsFromRequest(\n req: Request,\n): Promise<Awaited<ReturnType<typeof chatParamsFromRequestBody>>> {\n let body: unknown\n try {\n body = await req.json()\n } catch (cause) {\n // Preserve the underlying error on the thrown Response for\n // server-side observability without leaking it to the client.\n const res = new Response(\n 'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',\n { status: 400 },\n )\n ;(res as { cause?: unknown }).cause = cause\n throw res\n }\n try {\n return await chatParamsFromRequestBody(body)\n } catch (cause) {\n // Generic public message — avoid echoing Zod paths (which can contain\n // user payload fragments) or internal validator strings to the client.\n // The original AGUIError is attached as `cause` so server logs can\n // surface it without exposing it to remote callers.\n const res = new Response(\n 'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',\n { status: 400 },\n )\n ;(res as { cause?: unknown }).cause = cause\n throw res\n }\n}\n\n/**\n * Client-declared tool stub (no execute). `name` is `string`, so arrays that\n * include these stubs intentionally widen `TypedStreamChunk` tool-name\n * discrimination — pass server tools alone when you need a closed name union.\n */\nexport type ClientToolDeclaration = {\n name: string\n description: string\n inputSchema: JSONSchema\n}\n\nexport type MergedAgentTools<TServerTools extends ReadonlyArray<AnyTool>> =\n ReadonlyArray<TServerTools[number] | ClientToolDeclaration>\n\n/**\n * Merge a server-side tool array with the AG-UI client-declared tools\n * received in the request body.\n *\n * Rules:\n * - Server tools win on name collision. The client's declaration is\n * ignored if the server already has a tool with that name. The client's\n * UI-side handler still fires when the streamed tool-result event comes\n * through (see `chat-client.ts` `onToolCall`), giving the\n * \"after server execution the client also handles\" semantic for free.\n * - Client-only tools (name not in `serverTools`) become no-execute\n * entries: the runtime's existing `ClientToolRequest` path handles\n * them — server emits a tool-call request, client executes via its\n * registered handler, client posts back the result.\n *\n * Typing:\n * - Empty `clientTools` preserves the server tuple (closed name union).\n * - Non-empty `clientTools` returns a widened array that honestly includes\n * client stubs, so `TypedStreamChunk` does not claim a closed server-only\n * name union.\n *\n * @param serverTools - The server's tool array (e.g. from\n * `[myToolDef.server(...)]`). Pass directly to `chat({ tools })`.\n * @param clientTools - The `tools` array received from\n * `chatParamsFromRequest(...)` / `chatParamsFromRequestBody(...)`.\n * @returns A merged array suitable for `chat({ tools })`.\n */\nexport function mergeAgentTools<\n const TServerTools extends ReadonlyArray<AnyTool>,\n>(serverTools: TServerTools, clientTools: readonly []): TServerTools\nexport function mergeAgentTools<\n const TServerTools extends ReadonlyArray<AnyTool>,\n>(\n serverTools: TServerTools,\n clientTools: ReadonlyArray<{\n name: string\n description: string\n parameters: JSONSchema\n }>,\n): MergedAgentTools<TServerTools>\nexport function mergeAgentTools<\n const TServerTools extends ReadonlyArray<AnyTool>,\n>(\n serverTools: TServerTools,\n clientTools: ReadonlyArray<{\n name: string\n description: string\n parameters: JSONSchema\n }>,\n): TServerTools | MergedAgentTools<TServerTools> {\n if (clientTools.length === 0) {\n return serverTools\n }\n const seen = new Set(serverTools.map((t) => t.name))\n const merged: Array<TServerTools[number] | ClientToolDeclaration> = [\n ...serverTools,\n ]\n for (const ct of clientTools) {\n if (seen.has(ct.name)) {\n // Server wins on name collision.\n continue\n }\n seen.add(ct.name)\n merged.push({\n name: ct.name,\n description: ct.description,\n inputSchema: ct.parameters,\n // No `execute` — runtime treats this as a client-side tool and\n // emits ClientToolRequest events.\n })\n }\n return merged\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat-params.js","names":[],"sources":["../../../src/utilities/chat-params.ts"],"sourcesContent":["import { AGUIError } from '@ag-ui/core'\nimport type {\n Context as AGUIContext,\n Message as AGUIMessage,\n Role as AGUIRole,\n} from '@ag-ui/core'\nimport type {\n AnyTool,\n JSONSchema,\n ModelMessage,\n RunAgentResumeItem,\n UIMessage,\n} from '../types'\n\nconst KNOWN_PART_TYPES = new Set([\n 'text',\n 'image',\n 'audio',\n 'video',\n 'document',\n 'tool-call',\n 'tool-result',\n 'thinking',\n 'structured-output',\n])\n\nfunction isValidParts(value: unknown): value is Array<{ type: string }> {\n if (!Array.isArray(value)) return false\n for (const p of value) {\n if (!p || typeof p !== 'object') return false\n const type = (p as { type?: unknown }).type\n if (typeof type !== 'string' || !KNOWN_PART_TYPES.has(type)) return false\n if (type === 'structured-output') {\n const raw = (p as { raw?: unknown }).raw\n if (raw !== undefined && typeof raw !== 'string') return false\n }\n }\n return true\n}\n\n/**\n * Keyed by `AGUIRole` so a role added upstream fails to compile here until it\n * is handled, rather than silently falling through as an unknown role.\n */\nconst AGUI_ROLES: Record<AGUIRole, true> = {\n developer: true,\n system: true,\n assistant: true,\n user: true,\n tool: true,\n activity: true,\n reasoning: true,\n}\n\nfunction isAGUIRole(value: unknown): value is AGUIRole {\n return typeof value === 'string' && value in AGUI_ROLES\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Reject the request body, pointing at the migration guide. Mirrors the\n * message the previous `RunAgentInputSchema.safeParse` failure produced.\n */\nfunction invalidBody(reason: string): never {\n throw new AGUIError(\n `Request body is not a valid AG-UI RunAgentInput. ` +\n `If you're upgrading from a previous @tanstack/ai-client release, ` +\n `see docs/migration/ag-ui-compliance.md. ` +\n `Validation errors: ${reason}`,\n )\n}\n\nfunction requireString(value: unknown, at: string): string {\n if (typeof value !== 'string') invalidBody(`${at} must be a string`)\n return value\n}\n\nfunction requireArray(value: unknown, at: string): Array<unknown> {\n if (!Array.isArray(value)) invalidBody(`${at} must be an array`)\n return value\n}\n\n/**\n * Assert one AG-UI `Message`, discriminating on `role` exactly as the upstream\n * `MessageSchema` discriminated union does. The record view is retained on the\n * asserted type so callers can still inspect non-AG-UI extras like `parts`.\n */\nfunction assertAGUIMessage(\n value: Record<string, unknown>,\n at: string,\n): asserts value is Record<string, unknown> & AGUIMessage {\n requireString(value.id, `${at}.id`)\n\n const role = value.role\n if (!isAGUIRole(role)) {\n invalidBody(\n `${at}.role must be one of ${Object.keys(AGUI_ROLES).join(' | ')}`,\n )\n }\n\n switch (role) {\n case 'assistant':\n // Both optional: a tool-calling turn carries no text content.\n if (value.content !== undefined) {\n requireString(value.content, `${at}.content`)\n }\n if (value.toolCalls !== undefined) {\n requireArray(value.toolCalls, `${at}.toolCalls`)\n }\n break\n case 'user':\n if (typeof value.content !== 'string' && !Array.isArray(value.content)) {\n invalidBody(\n `${at}.content must be a string or an array of content parts`,\n )\n }\n break\n case 'tool':\n requireString(value.content, `${at}.content`)\n requireString(value.toolCallId, `${at}.toolCallId`)\n break\n case 'activity':\n requireString(value.activityType, `${at}.activityType`)\n if (!isRecord(value.content)) {\n invalidBody(`${at}.content must be an object`)\n }\n break\n case 'developer':\n case 'system':\n case 'reasoning':\n requireString(value.content, `${at}.content`)\n break\n }\n}\n\nfunction validateMessage(value: unknown, index: number): AGUIMessage {\n const at = `messages[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n assertAGUIMessage(value, at)\n\n // `parts` is TanStack's canonical extra, carried through so the UIMessage\n // path inside `chat()` can use it. Keep it only when it holds recognized\n // part types — the previous schema-based path dropped `parts` during parse\n // and re-attached it from the raw body behind this same check.\n if ('parts' in value && !isValidParts(value.parts)) {\n const withoutParts = { ...value }\n Reflect.deleteProperty(withoutParts, 'parts')\n return withoutParts\n }\n return value\n}\n\nfunction validateTool(\n value: unknown,\n index: number,\n): { name: string; description: string; parameters: JSONSchema } {\n const at = `tools[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n return {\n name: requireString(value.name, `${at}.name`),\n description: requireString(value.description, `${at}.description`),\n // Upstream `ToolSchema` types this as optional `any`; it reaches the\n // provider as a raw JSON Schema either way.\n parameters: value.parameters as JSONSchema,\n }\n}\n\nfunction validateContext(value: unknown, index: number): AGUIContext {\n const at = `context[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n return {\n description: requireString(value.description, `${at}.description`),\n value: requireString(value.value, `${at}.value`),\n }\n}\n\nfunction validateResumeEntry(\n value: unknown,\n index: number,\n): RunAgentResumeItem {\n const at = `resume[${index}]`\n if (!isRecord(value)) invalidBody(`${at} must be an object`)\n const status = value.status\n if (status !== 'resolved' && status !== 'cancelled') {\n invalidBody(`${at}.status must be \"resolved\" or \"cancelled\"`)\n }\n const entry: RunAgentResumeItem = {\n interruptId: requireString(value.interruptId, `${at}.interruptId`),\n status,\n }\n // Omit the key entirely when absent, matching the optional-field shape the\n // schema produced.\n if (value.payload !== undefined) entry.payload = value.payload\n if (value.metadata !== undefined) {\n if (!isRecord(value.metadata)) {\n invalidBody(`${at}.metadata must be an object`)\n }\n entry.metadata = value.metadata\n }\n return entry\n}\n\n/**\n * Parse and validate an HTTP request body as an AG-UI `RunAgentInput`.\n *\n * Returns a spread-friendly object whose `messages` field is suitable for\n * passing directly to `chat({ messages })`. The existing\n * `convertMessagesToModelMessages` handles AG-UI fan-out dedup and\n * reasoning/activity/developer-role normalization internally.\n *\n * Validated structurally against the AG-UI `RunAgentInput` contract without a\n * schema library, so this package pulls in no validation runtime of its own.\n *\n * @throws An error with a migration-pointing message when the body does\n * not conform to AG-UI `RunAgentInput`. Surface this as a\n * 400 Bad Request to the client.\n */\nexport async function chatParamsFromRequestBody(body: unknown): Promise<{\n messages: Array<UIMessage | ModelMessage>\n threadId: string\n runId: string\n parentRunId?: string\n tools: Array<{ name: string; description: string; parameters: JSONSchema }>\n forwardedProps: Record<string, unknown>\n state: unknown\n resume?: Array<RunAgentResumeItem>\n /**\n * @deprecated Use `aguiContext` instead. This alias will be removed in a\n * future release.\n */\n context: Array<AGUIContext>\n aguiContext: Array<AGUIContext>\n}> {\n if (!isRecord(body)) invalidBody('body must be a JSON object')\n\n const threadId = requireString(body.threadId, 'threadId')\n const runId = requireString(body.runId, 'runId')\n const parentRunId =\n body.parentRunId === undefined\n ? undefined\n : requireString(body.parentRunId, 'parentRunId')\n\n const messages = requireArray(body.messages, 'messages').map(validateMessage)\n const tools = requireArray(body.tools, 'tools').map(validateTool)\n const aguiContext = requireArray(body.context, 'context').map(validateContext)\n const resume =\n body.resume === undefined\n ? undefined\n : requireArray(body.resume, 'resume').map(validateResumeEntry)\n\n if (body.forwardedProps !== undefined && !isRecord(body.forwardedProps)) {\n invalidBody('forwardedProps must be an object')\n }\n\n return {\n // Unknown top-level fields (e.g. a legacy `cursor`) are dropped by\n // construction: only the fields below are copied onto the result.\n messages: messages as Array<UIMessage | ModelMessage>,\n threadId,\n runId,\n parentRunId,\n tools,\n forwardedProps: (body.forwardedProps ?? {}) as Record<string, unknown>,\n state: body.state,\n resume: resume as Array<RunAgentResumeItem> | undefined,\n context: aguiContext,\n aguiContext,\n }\n}\n\n/**\n * Read an HTTP `Request`, parse its JSON body, and validate it as an\n * AG-UI `RunAgentInput` — collapsing the standard `req.json()` +\n * `chatParamsFromRequestBody(...)` pair into a single call.\n *\n * On a malformed body or invalid AG-UI shape, this **throws a\n * `Response`** with status 400 and a migration-pointing message in the\n * body. Frameworks that natively handle thrown `Response` objects\n * (TanStack Start, SolidStart, Remix, React Router 7) will return the\n * 400 to the client automatically, so the handler reduces to:\n *\n * ```ts\n * export async function POST(req: Request) {\n * const params = await chatParamsFromRequest(req)\n * // ...use params\n * }\n * ```\n *\n * In frameworks that do not auto-handle thrown `Response` objects\n * (Next.js Route Handlers, SvelteKit, Hono, raw Node), wrap the call\n * with try/catch and return the caught Response yourself, or use\n * `chatParamsFromRequestBody` directly with your own JSON-parsing.\n *\n * @throws {Response} 400 on malformed JSON or invalid AG-UI shape.\n */\nexport async function chatParamsFromRequest(\n req: Request,\n): Promise<Awaited<ReturnType<typeof chatParamsFromRequestBody>>> {\n let body: unknown\n try {\n body = await req.json()\n } catch (cause) {\n // Preserve the underlying error on the thrown Response for\n // server-side observability without leaking it to the client.\n const res = new Response(\n 'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',\n { status: 400 },\n )\n ;(res as { cause?: unknown }).cause = cause\n throw res\n }\n try {\n return await chatParamsFromRequestBody(body)\n } catch (cause) {\n // Generic public message — avoid echoing Zod paths (which can contain\n // user payload fragments) or internal validator strings to the client.\n // The original AGUIError is attached as `cause` so server logs can\n // surface it without exposing it to remote callers.\n const res = new Response(\n 'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',\n { status: 400 },\n )\n ;(res as { cause?: unknown }).cause = cause\n throw res\n }\n}\n\n/**\n * Client-declared tool stub (no execute). `name` is `string`, so arrays that\n * include these stubs intentionally widen `TypedStreamChunk` tool-name\n * discrimination — pass server tools alone when you need a closed name union.\n */\nexport type ClientToolDeclaration = {\n name: string\n description: string\n inputSchema: JSONSchema\n}\n\nexport type MergedAgentTools<TServerTools extends ReadonlyArray<AnyTool>> =\n ReadonlyArray<TServerTools[number] | ClientToolDeclaration>\n\n/**\n * Merge a server-side tool array with the AG-UI client-declared tools\n * received in the request body.\n *\n * Rules:\n * - Server tools win on name collision. The client's declaration is\n * ignored if the server already has a tool with that name. The client's\n * UI-side handler still fires when the streamed tool-result event comes\n * through (see `chat-client.ts` `onToolCall`), giving the\n * \"after server execution the client also handles\" semantic for free.\n * - Client-only tools (name not in `serverTools`) become no-execute\n * entries: the runtime's existing `ClientToolRequest` path handles\n * them — server emits a tool-call request, client executes via its\n * registered handler, client posts back the result.\n *\n * Typing:\n * - Empty `clientTools` preserves the server tuple (closed name union).\n * - Non-empty `clientTools` returns a widened array that honestly includes\n * client stubs, so `TypedStreamChunk` does not claim a closed server-only\n * name union.\n *\n * @param serverTools - The server's tool array (e.g. from\n * `[myToolDef.server(...)]`). Pass directly to `chat({ tools })`.\n * @param clientTools - The `tools` array received from\n * `chatParamsFromRequest(...)` / `chatParamsFromRequestBody(...)`.\n * @returns A merged array suitable for `chat({ tools })`.\n */\nexport function mergeAgentTools<\n const TServerTools extends ReadonlyArray<AnyTool>,\n>(serverTools: TServerTools, clientTools: readonly []): TServerTools\nexport function mergeAgentTools<\n const TServerTools extends ReadonlyArray<AnyTool>,\n>(\n serverTools: TServerTools,\n clientTools: ReadonlyArray<{\n name: string\n description: string\n parameters: JSONSchema\n }>,\n): MergedAgentTools<TServerTools>\nexport function mergeAgentTools<\n const TServerTools extends ReadonlyArray<AnyTool>,\n>(\n serverTools: TServerTools,\n clientTools: ReadonlyArray<{\n name: string\n description: string\n parameters: JSONSchema\n }>,\n): TServerTools | MergedAgentTools<TServerTools> {\n if (clientTools.length === 0) {\n return serverTools\n }\n const seen = new Set(serverTools.map((t) => t.name))\n const merged: Array<TServerTools[number] | ClientToolDeclaration> = [\n ...serverTools,\n ]\n for (const ct of clientTools) {\n if (seen.has(ct.name)) {\n // Server wins on name collision.\n continue\n }\n seen.add(ct.name)\n merged.push({\n name: ct.name,\n description: ct.description,\n inputSchema: ct.parameters,\n // No `execute` — runtime treats this as a client-side tool and\n // emits ClientToolRequest events.\n })\n }\n return merged\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.2",
|
|
4
4
|
"description": "Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"@ag-ui/core": "0.1.1-canary.beta.0",
|
|
81
81
|
"@standard-schema/spec": "^1.1.0",
|
|
82
82
|
"partial-json": "^0.1.7",
|
|
83
|
-
"@tanstack/ai-
|
|
84
|
-
"@tanstack/ai-
|
|
83
|
+
"@tanstack/ai-utils": "^0.4.0",
|
|
84
|
+
"@tanstack/ai-event-client": "^0.9.0"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"@opentelemetry/api": ">=1.9.0"
|
|
@@ -565,7 +565,10 @@ integer durations 1-15s, reports `usage.billed` seconds ({ quantity, unit: 'seco
|
|
|
565
565
|
aspect-ratio size template like `'16:9_720p'`, durations 4-15s on the 2.0 family,
|
|
566
566
|
4-12s on 1.5-pro, 2-12s on the 1.0-pro models; reads `ARK_API_KEY`),
|
|
567
567
|
`openRouterVideo(...)` (OpenRouter's dedicated `POST /api/v1/videos` gateway),
|
|
568
|
-
and `falVideo(...)` (hosted models
|
|
568
|
+
and `falVideo(...)` (hosted models; `duration` typed from `@fal-ai/client`'s
|
|
569
|
+
`EndpointTypeMap` — `'5' | '10'` on Kling 2.6, `'3'`…`'15'` on Kling 3,
|
|
570
|
+
`'4s' | '6s' | '8s'` on Veo 3.1, `'5s' | '9s'` on Luma; `availableDurations()` /
|
|
571
|
+
`snapDuration()` on the curated set; see cost tracking below).
|
|
569
572
|
|
|
570
573
|
> **Seedance option applicability is per model and enforced server-side** —
|
|
571
574
|
> Ark returns a 400 for an inapplicable field rather than ignoring it.
|