@tanstack/ai 0.61.0 → 0.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/esm/activities/chat/agents/define-agent.d.ts +17 -5
- package/dist/esm/activities/chat/agents/define-agent.js.map +1 -1
- package/dist/esm/activities/chat/agents/spawn.d.ts +2 -0
- package/dist/esm/activities/chat/agents/spawn.js +10 -33
- package/dist/esm/activities/chat/agents/spawn.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +2 -0
- package/dist/esm/activities/chat/index.js +166 -60
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +43 -22
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +1 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
- package/dist/esm/activities/chat/stream/message-updaters.js +6 -2
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +11 -9
- package/dist/esm/activities/chat/stream/processor.js +38 -18
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +8 -0
- package/dist/esm/activities/chat/tools/schema-converter.js +6 -5
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +20 -3
- package/dist/esm/activities/chat/tools/tool-calls.js +126 -36
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/evaluate/adapter.d.ts +4 -0
- package/dist/esm/activities/evaluate/adapter.js.map +1 -1
- package/dist/esm/activities/evaluate/index.d.ts +4 -0
- package/dist/esm/activities/evaluate/index.js +3 -1
- package/dist/esm/activities/evaluate/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +1 -1
- package/dist/esm/activities/generateSpeech/index.js +1 -1
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +14 -6
- package/dist/esm/activities/generateVideo/adapter.js +6 -3
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +5 -4
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.d.ts +12 -3
- package/dist/esm/activities/generateVideo/snap.js +47 -8
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/generateVoice/index.d.ts +1 -1
- package/dist/esm/activities/generateVoice/index.js +1 -1
- package/dist/esm/activities/generateVoice/index.js.map +1 -1
- package/dist/esm/activities/index.d.ts +2 -2
- package/dist/esm/activities/index.js +2 -2
- package/dist/esm/adapter-internals.d.ts +1 -0
- package/dist/esm/adapter-internals.js +2 -1
- package/dist/esm/client.d.ts +1 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/interrupt-resume.js +29 -4
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/middlewares/otel.d.ts +5 -2
- package/dist/esm/middlewares/otel.js +114 -0
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/types.d.ts +70 -4
- package/dist/esm/utilities/ag-ui-wire.js +8 -4
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/merge-streams.d.ts +6 -0
- package/dist/esm/utilities/merge-streams.js +37 -0
- package/dist/esm/utilities/merge-streams.js.map +1 -0
- package/dist/esm/utilities/reasoning-encrypted-value.d.ts +8 -0
- package/dist/esm/utilities/reasoning-encrypted-value.js +11 -1
- package/dist/esm/utilities/reasoning-encrypted-value.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +2 -1
- package/dist/esm/utilities/tool-result.js +4 -1
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/chat-experience/SKILL.md +120 -0
- package/skills/ai-core/media-generation/SKILL.md +3 -3
- package/skills/ai-core/tool-calling/SKILL.md +103 -0
- package/src/activities/chat/agents/define-agent.ts +20 -3
- package/src/activities/chat/agents/spawn.ts +22 -43
- package/src/activities/chat/index.ts +248 -65
- package/src/activities/chat/messages.ts +54 -7
- package/src/activities/chat/middleware/types.ts +1 -0
- package/src/activities/chat/stream/message-updaters.ts +8 -0
- package/src/activities/chat/stream/processor.ts +57 -27
- package/src/activities/chat/tools/schema-converter.ts +17 -5
- package/src/activities/chat/tools/tool-calls.ts +215 -68
- package/src/activities/chat/tools/tool-definition.ts +8 -0
- package/src/activities/evaluate/adapter.ts +4 -0
- package/src/activities/evaluate/index.ts +6 -0
- package/src/activities/generateSpeech/index.ts +1 -1
- package/src/activities/generateVideo/adapter.ts +21 -7
- package/src/activities/generateVideo/index.ts +5 -4
- package/src/activities/generateVideo/snap.ts +64 -6
- package/src/activities/generateVoice/index.ts +1 -1
- package/src/activities/index.ts +2 -1
- package/src/adapter-internals.ts +1 -0
- package/src/client.ts +1 -0
- package/src/index.ts +1 -0
- package/src/interrupt-resume.ts +55 -4
- package/src/middlewares/otel.ts +161 -3
- package/src/types.ts +66 -5
- package/src/utilities/ag-ui-wire.ts +17 -2
- package/src/utilities/merge-streams.ts +34 -0
- package/src/utilities/reasoning-encrypted-value.ts +12 -0
- package/src/utilities/tool-result.ts +7 -1
|
@@ -25,6 +25,14 @@ export interface ConvertSchemaOptions {
|
|
|
25
25
|
* @default false
|
|
26
26
|
*/
|
|
27
27
|
forStructuredOutput?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Which view of a Standard JSON Schema to convert. A schema with a
|
|
30
|
+
* transform or a pipe has a different `output` view. Use `'output'` to
|
|
31
|
+
* describe the value that parsing returns.
|
|
32
|
+
*
|
|
33
|
+
* @default 'input'
|
|
34
|
+
*/
|
|
35
|
+
io?: 'input' | 'output';
|
|
28
36
|
}
|
|
29
37
|
/**
|
|
30
38
|
* Converts a Standard JSON Schema compliant schema or plain JSONSchema to JSON Schema format
|
|
@@ -151,16 +151,17 @@ function makeStructuredOutputCompatible(schema, originalRequired = []) {
|
|
|
151
151
|
*
|
|
152
152
|
* - Standard JSON Schemas are rebuilt structurally (dropping `$schema`, which
|
|
153
153
|
* LLM providers ignore) and given the explicit `type`/`properties`/`required`
|
|
154
|
-
* defaults object shapes need downstream.
|
|
154
|
+
* defaults object shapes need downstream. `io` picks the `input` (default)
|
|
155
|
+
* or `output` view.
|
|
155
156
|
* - Plain `JSONSchema` inputs are rebuilt into the typed view; non-object inputs
|
|
156
157
|
* are surfaced untouched (they can't be widened).
|
|
157
158
|
* - Standard Schema validators lacking a `~standard.jsonSchema` converter throw
|
|
158
159
|
* with actionable guidance, rather than shipping `{ '~standard': … }` to the
|
|
159
160
|
* provider and producing an opaque downstream error.
|
|
160
161
|
*/
|
|
161
|
-
function toTypedJsonSchema(schema) {
|
|
162
|
+
function toTypedJsonSchema(schema, io = "input") {
|
|
162
163
|
if (isStandardJSONSchema(schema)) {
|
|
163
|
-
const result = toJsonSchema(schema["~standard"].jsonSchema
|
|
164
|
+
const result = toJsonSchema(schema["~standard"].jsonSchema[io]({ target: "draft-07" }));
|
|
164
165
|
if ("properties" in result && !result.type) result.type = "object";
|
|
165
166
|
if (result.type === "object" && !("properties" in result)) result.properties = {};
|
|
166
167
|
if (result.type === "object" && !("required" in result)) result.required = [];
|
|
@@ -232,9 +233,9 @@ function toTypedJsonSchema(schema) {
|
|
|
232
233
|
*/
|
|
233
234
|
function convertSchemaToJsonSchema(schema, options = {}) {
|
|
234
235
|
if (!schema) return void 0;
|
|
235
|
-
const { forStructuredOutput = false } = options;
|
|
236
|
+
const { forStructuredOutput = false, io } = options;
|
|
236
237
|
if (!forStructuredOutput && !isStandardJSONSchema(schema) && !isStandardSchema(schema)) return schema;
|
|
237
|
-
const base = toTypedJsonSchema(schema);
|
|
238
|
+
const base = toTypedJsonSchema(schema, io);
|
|
238
239
|
if (!base || typeof base !== "object") return base;
|
|
239
240
|
if (!forStructuredOutput) return base;
|
|
240
241
|
return makeStructuredOutputCompatible(base, base.required || []).schema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-converter.js","names":[],"sources":["../../../../../src/activities/chat/tools/schema-converter.ts"],"sourcesContent":["import type {\n StandardJSONSchemaV1,\n StandardSchemaV1,\n} from '@standard-schema/spec'\nimport type { NullWideningMap } from '@tanstack/ai-utils'\nimport type { JSONSchema, SchemaInput } from '../../../types'\n\n/**\n * Build a JSONSchema object from any plain key/value source. The `JSONSchema`\n * interface's `[key: string]: any` index signature makes every property\n * assignable through bracket access without a type cast — copying keys here\n * lets us narrow either `Record<string, unknown>` (returned by\n * `~standard.jsonSchema.input()`) or a `JSONSchema` (from the SchemaInput\n * pass-through arm) into the typed view used by the rest of this module.\n *\n * Accepts `object` so callers don't need a cast when narrowing from union\n * types like `SchemaInput`.\n */\nfunction toJsonSchema(obj: object): JSONSchema {\n const result: JSONSchema = {}\n for (const [key, value] of Object.entries(obj)) {\n if (key === '$schema') continue // not needed by LLM providers\n result[key] = value\n }\n return result\n}\n\n/**\n * Whether a value can carry a `~standard` property. Most schema libraries\n * (Zod, Valibot) return plain objects, but ArkType's `type()` returns a\n * *callable function* with `~standard` attached — so `typeof` must accept\n * both `'object'` and `'function'` or ArkType schemas are missed entirely\n * (issue #276).\n */\nfunction isPropertyCarrier(schema: unknown): schema is Record<string, unknown> {\n return (\n (typeof schema === 'object' || typeof schema === 'function') &&\n schema !== null\n )\n}\n\n/**\n * Check if a value is a Standard JSON Schema compliant schema.\n * Standard JSON Schema compliant libraries (Zod v4+, ArkType, Valibot with toStandardJsonSchema, etc.)\n * implement the '~standard' property with jsonSchema converter methods.\n */\nexport function isStandardJSONSchema(\n schema: unknown,\n): schema is StandardJSONSchemaV1 {\n if (!isPropertyCarrier(schema) || !('~standard' in schema)) return false\n\n const standard = schema['~standard']\n if (\n typeof standard !== 'object' ||\n standard === null ||\n !('version' in standard) ||\n standard.version !== 1 ||\n !('jsonSchema' in standard) ||\n typeof standard.jsonSchema !== 'object' ||\n standard.jsonSchema === null ||\n !('input' in standard.jsonSchema)\n ) {\n return false\n }\n\n return typeof standard.jsonSchema.input === 'function'\n}\n\n/**\n * Check if a value is a Standard Schema compliant schema (for validation).\n * Standard Schema compliant libraries implement the '~standard' property with a validate function.\n */\nexport function isStandardSchema(schema: unknown): schema is StandardSchemaV1 {\n return (\n isPropertyCarrier(schema) &&\n '~standard' in schema &&\n typeof schema['~standard'] === 'object' &&\n schema['~standard'] !== null &&\n 'version' in schema['~standard'] &&\n schema['~standard'].version === 1 &&\n 'validate' in schema['~standard'] &&\n typeof schema['~standard'].validate === 'function'\n )\n}\n\n/**\n * Result of {@link makeStructuredOutputCompatible}: the strict-ready schema plus\n * a {@link NullWideningMap} recording every position where a `null` was\n * synthesized, so the response can be un-widened before validation without\n * re-deriving (or guessing) which nulls were synthetic.\n */\ninterface StructuredOutputConversion {\n schema: JSONSchema\n nullWidening: NullWideningMap | undefined\n}\n\n/** Drop an empty map to `undefined` so leaf/no-op subtrees don't litter it. */\nfunction pruneMap(map: NullWideningMap): NullWideningMap | undefined {\n return Object.keys(map).length > 0 ? map : undefined\n}\n\nfunction coerceArrayItems(items: JSONSchema | Array<JSONSchema>): {\n schema: JSONSchema | Array<JSONSchema>\n itemMap: NullWideningMap | Array<NullWideningMap> | undefined\n} {\n if (Array.isArray(items)) {\n const nested = items.map((item) =>\n makeStructuredOutputCompatible(item, item.required || []),\n )\n const itemMaps = nested.map((entry) => entry.nullWidening ?? {})\n return {\n schema: nested.map((entry) => entry.schema),\n itemMap: itemMaps.some((entry) => Object.keys(entry).length > 0)\n ? itemMaps\n : undefined,\n }\n }\n const nested = makeStructuredOutputCompatible(items, items.required || [])\n return { schema: nested.schema, itemMap: nested.nullWidening }\n}\n\n/**\n * Transform a JSON schema to be compatible with OpenAI's structured output requirements.\n * OpenAI requires:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for objects\n *\n * Alongside the transformed schema it returns a {@link NullWideningMap} marking\n * exactly the positions where `null` was added, so `undoNullWidening` can strip\n * those synthesized nulls (and only those) from the provider's response.\n *\n * @param schema - JSON schema to transform\n * @param originalRequired - Original required array (to know which fields were optional)\n * @returns Transformed schema + the null-widening map for the round trip\n */\nfunction makeStructuredOutputCompatible(\n schema: JSONSchema,\n originalRequired: Array<string> = [],\n): StructuredOutputConversion {\n const result: JSONSchema = { ...schema }\n const map: NullWideningMap = {}\n\n // Handle object types\n if (result.type === 'object' && result.properties) {\n const properties: Record<string, JSONSchema> = { ...result.properties }\n const allPropertyNames = Object.keys(properties)\n const propertyMaps: Record<string, NullWideningMap> = {}\n\n // Transform each property\n for (const propName of allPropertyNames) {\n const prop = properties[propName]\n if (!prop) continue\n const wasOptional = !originalRequired.includes(propName)\n // `null` synthesized AT this property (the field itself can come back null).\n let widenedHere = false\n // Map describing widened positions INSIDE this property.\n let childMap: NullWideningMap | undefined\n\n // Recursively transform nested objects/arrays\n if (prop.type === 'object' && prop.properties) {\n const nested = makeStructuredOutputCompatible(prop, prop.required || [])\n properties[propName] = wasOptional\n ? { ...nested.schema, type: ['object', 'null'] }\n : nested.schema\n widenedHere = wasOptional\n childMap = nested.nullWidening\n } else if (prop.type === 'array' && prop.items) {\n const nestedItems = coerceArrayItems(prop.items)\n properties[propName] = {\n ...prop,\n items: nestedItems.schema,\n ...(wasOptional ? { type: ['array', 'null'] } : {}),\n }\n widenedHere = wasOptional\n childMap = nestedItems.itemMap\n ? { items: nestedItems.itemMap }\n : undefined\n } else if (wasOptional) {\n // Make optional fields nullable by adding null to the type. Mark\n // `widenedHere` only where we actually add `null`; a field already\n // typed nullable (`.nullish()`) is left as-is and keeps its null.\n if (prop.type && !Array.isArray(prop.type)) {\n properties[propName] = { ...prop, type: [prop.type, 'null'] }\n widenedHere = true\n } else if (Array.isArray(prop.type) && !prop.type.includes('null')) {\n properties[propName] = { ...prop, type: [...prop.type, 'null'] }\n widenedHere = true\n }\n }\n\n if (widenedHere || childMap) {\n propertyMaps[propName] = {\n ...(childMap ?? {}),\n ...(widenedHere ? { widened: true } : {}),\n }\n }\n }\n\n result.properties = properties\n // ALL properties must be required for OpenAI structured output\n result.required = allPropertyNames\n // additionalProperties must be false\n result.additionalProperties = false\n if (Object.keys(propertyMaps).length > 0) map.properties = propertyMaps\n }\n\n // Handle array item schemas. A tuple (`items: [a, b, …]`) keeps every\n // position. A homogeneous schema stays a single items map so\n // `undoNullWidening` applies it to every element.\n if (result.type === 'array' && result.items) {\n const nestedItems = coerceArrayItems(result.items)\n result.items = nestedItems.schema\n if (nestedItems.itemMap) map.items = nestedItems.itemMap\n }\n\n return { schema: result, nullWidening: pruneMap(map) }\n}\n\n/**\n * Options for schema conversion\n */\nexport interface ConvertSchemaOptions {\n /**\n * When true, transforms the schema to be compatible with OpenAI's structured output requirements:\n * - All properties are added to the `required` array\n * - Optional fields get null added to their type union\n * - additionalProperties is set to false for all objects\n *\n * @default false\n */\n forStructuredOutput?: boolean\n}\n\n/**\n * Normalize any supported schema input to a typed, UN-widened `JSONSchema` —\n * the shared first half of conversion, before any structured-output widening.\n *\n * - Standard JSON Schemas are rebuilt structurally (dropping `$schema`, which\n * LLM providers ignore) and given the explicit `type`/`properties`/`required`\n * defaults object shapes need downstream.\n * - Plain `JSONSchema` inputs are rebuilt into the typed view; non-object inputs\n * are surfaced untouched (they can't be widened).\n * - Standard Schema validators lacking a `~standard.jsonSchema` converter throw\n * with actionable guidance, rather than shipping `{ '~standard': … }` to the\n * provider and producing an opaque downstream error.\n */\nfunction toTypedJsonSchema(schema: SchemaInput): JSONSchema | undefined {\n if (isStandardJSONSchema(schema)) {\n const jsonSchema = schema['~standard'].jsonSchema.input({\n target: 'draft-07',\n })\n const result: JSONSchema = toJsonSchema(jsonSchema)\n if ('properties' in result && !result.type) result.type = 'object'\n if (result.type === 'object' && !('properties' in result)) {\n result.properties = {}\n }\n if (result.type === 'object' && !('required' in result)) {\n result.required = []\n }\n return result\n }\n\n if (isStandardSchema(schema)) {\n throw new Error(\n 'Schema is a Standard Schema validator but does not expose a JSON Schema ' +\n 'converter on `~standard.jsonSchema`. Use Zod v4.2+, ArkType v2.1.28+, ' +\n 'or wrap a Valibot schema with `toStandardJsonSchema()` from ' +\n '`@valibot/to-json-schema` before passing it as `outputSchema`.',\n )\n }\n\n if (typeof schema !== 'object') return schema\n return toJsonSchema(schema)\n}\n\n/**\n * Converts a Standard JSON Schema compliant schema or plain JSONSchema to JSON Schema format\n * compatible with LLM providers.\n *\n * Supports any schema library that implements the Standard JSON Schema spec (v1):\n * - Zod v4+ (natively supports StandardJSONSchemaV1)\n * - ArkType (natively supports StandardJSONSchemaV1)\n * - Valibot (via `toStandardJsonSchema()` from `@valibot/to-json-schema`)\n *\n * If the input is already a plain JSONSchema object, it is returned as-is.\n *\n * @param schema - Standard JSON Schema compliant schema or plain JSONSchema object to convert\n * @param options - Conversion options\n * @returns JSON Schema object that can be sent to LLM providers\n *\n * @example\n * ```typescript\n * // Using Zod v4+ (natively supports Standard JSON Schema)\n * import * as z from 'zod';\n *\n * const zodSchema = z.object({\n * location: z.string().describe('City name'),\n * unit: z.enum(['celsius', 'fahrenheit']).optional()\n * });\n *\n * const jsonSchema = convertSchemaToJsonSchema(zodSchema);\n *\n * @example\n * // Using ArkType (natively supports Standard JSON Schema)\n * import { type } from 'arktype';\n *\n * const arkSchema = type({\n * location: 'string',\n * unit: \"'celsius' | 'fahrenheit'\"\n * });\n *\n * const jsonSchema = convertSchemaToJsonSchema(arkSchema);\n *\n * @example\n * // Using Valibot (via toStandardJsonSchema)\n * import * as v from 'valibot';\n * import { toStandardJsonSchema } from '@valibot/to-json-schema';\n *\n * const valibotSchema = toStandardJsonSchema(v.object({\n * location: v.string(),\n * unit: v.optional(v.picklist(['celsius', 'fahrenheit']))\n * }));\n *\n * const jsonSchema = convertSchemaToJsonSchema(valibotSchema);\n *\n * @example\n * // Using JSONSchema directly (passes through unchanged)\n * const rawSchema = {\n * type: 'object',\n * properties: { location: { type: 'string' } },\n * required: ['location']\n * };\n * const result = convertSchemaToJsonSchema(rawSchema);\n * ```\n */\nexport function convertSchemaToJsonSchema(\n schema: SchemaInput | undefined,\n options: ConvertSchemaOptions = {},\n): JSONSchema | undefined {\n if (!schema) return undefined\n\n const { forStructuredOutput = false } = options\n\n // Plain-JSONSchema passthrough: with no widening requested, return the schema\n // by reference so callers comparing via `===` keep identity. Only the widening\n // path needs the rebuilt, normalized view from `toTypedJsonSchema`.\n if (\n !forStructuredOutput &&\n !isStandardJSONSchema(schema) &&\n !isStandardSchema(schema)\n ) {\n return schema\n }\n\n const base = toTypedJsonSchema(schema)\n // Non-object inputs can't be widened; surface them untouched.\n if (!base || typeof base !== 'object') return base\n if (!forStructuredOutput) return base\n return makeStructuredOutputCompatible(base, base.required || []).schema\n}\n\n/**\n * Convert a schema for structured output AND capture the {@link NullWideningMap}\n * recording every `null` the strict-mode widening synthesized. The map lets the\n * caller undo that widening on the provider's response (via `undoNullWidening`)\n * before validating against the original schema — optional fields read back as\n * absent while genuine `.nullable()` nulls survive. The map is `undefined` when\n * the schema isn't a widenable object or when no field needed widening.\n */\nexport function convertSchemaForStructuredOutput(\n schema: SchemaInput | undefined,\n): {\n jsonSchema: JSONSchema | undefined\n nullWideningMap: NullWideningMap | undefined\n} {\n if (!schema) return { jsonSchema: undefined, nullWideningMap: undefined }\n const base = toTypedJsonSchema(schema)\n if (!base || typeof base !== 'object') {\n return { jsonSchema: base, nullWideningMap: undefined }\n }\n const { schema: jsonSchema, nullWidening } = makeStructuredOutputCompatible(\n base,\n base.required || [],\n )\n return { jsonSchema, nullWideningMap: nullWidening }\n}\n\n/**\n * Validates data against a Standard Schema compliant schema.\n *\n * @param schema - Standard Schema compliant schema\n * @param data - Data to validate\n * @returns Validation result with success status, data or issues\n */\nexport async function validateWithStandardSchema<T>(\n schema: unknown,\n data: unknown,\n): Promise<\n | { success: true; data: T }\n | {\n success: false\n issues: Array<{ message: string; path?: Array<string> | undefined }>\n }\n> {\n if (!isStandardSchema(schema)) {\n // If it's not a Standard Schema, just return the data as-is\n return { success: true, data: data as T }\n }\n\n const result = await schema['~standard'].validate(data)\n\n if (!result.issues) {\n return { success: true, data: result.value as T }\n }\n\n return {\n success: false,\n issues: result.issues.map((issue) => ({\n message: issue.message || 'Validation failed',\n path: issue.path?.map(String),\n })),\n }\n}\n\n/**\n * Error thrown when Standard Schema validation fails. Carries the original\n * `issues` array so consumers (middleware `onError`, callers catching from\n * `chat({ outputSchema })`) can programmatically inspect each failure.\n */\nexport class StandardSchemaValidationError extends Error {\n override readonly name = 'StandardSchemaValidationError'\n readonly issues: ReadonlyArray<StandardSchemaV1.Issue>\n\n constructor(issues: ReadonlyArray<StandardSchemaV1.Issue>) {\n super(\n `Validation failed: ${issues\n .map((i) => i.message || 'Validation failed')\n .join(', ')}`,\n )\n this.issues = issues\n }\n}\n\n/**\n * Synchronously validates data against a Standard Schema compliant schema.\n * Note: Some Standard Schema implementations may only support async validation.\n * In those cases, this function will throw.\n *\n * @param schema - Standard Schema compliant schema\n * @param data - Data to validate\n * @returns Parsed/validated data\n * @throws StandardSchemaValidationError if validation fails; Error if the\n * schema only supports async validation.\n */\nexport function parseWithStandardSchema<T>(schema: unknown, data: unknown): T {\n if (!isStandardSchema(schema)) {\n // If it's not a Standard Schema, just return the data as-is\n return data as T\n }\n\n const result = schema['~standard'].validate(data)\n\n // Handle async result (Promise)\n if (result instanceof Promise) {\n throw new Error(\n 'Schema validation returned a Promise. Use validateWithStandardSchema for async validation.',\n )\n }\n // Standard Schema validation returns { value } for success or { issues } for failure\n if (!result.issues) {\n return result.value as T\n }\n\n throw new StandardSchemaValidationError(result.issues)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schema-converter.js","names":[],"sources":["../../../../../src/activities/chat/tools/schema-converter.ts"],"sourcesContent":["import type {\n StandardJSONSchemaV1,\n StandardSchemaV1,\n} from '@standard-schema/spec'\nimport type { NullWideningMap } from '@tanstack/ai-utils'\nimport type { JSONSchema, SchemaInput } from '../../../types'\n\n/**\n * Build a JSONSchema object from any plain key/value source. The `JSONSchema`\n * interface's `[key: string]: any` index signature makes every property\n * assignable through bracket access without a type cast — copying keys here\n * lets us narrow either `Record<string, unknown>` (returned by\n * `~standard.jsonSchema.input()`) or a `JSONSchema` (from the SchemaInput\n * pass-through arm) into the typed view used by the rest of this module.\n *\n * Accepts `object` so callers don't need a cast when narrowing from union\n * types like `SchemaInput`.\n */\nfunction toJsonSchema(obj: object): JSONSchema {\n const result: JSONSchema = {}\n for (const [key, value] of Object.entries(obj)) {\n if (key === '$schema') continue // not needed by LLM providers\n result[key] = value\n }\n return result\n}\n\n/**\n * Whether a value can carry a `~standard` property. Most schema libraries\n * (Zod, Valibot) return plain objects, but ArkType's `type()` returns a\n * *callable function* with `~standard` attached — so `typeof` must accept\n * both `'object'` and `'function'` or ArkType schemas are missed entirely\n * (issue #276).\n */\nfunction isPropertyCarrier(schema: unknown): schema is Record<string, unknown> {\n return (\n (typeof schema === 'object' || typeof schema === 'function') &&\n schema !== null\n )\n}\n\n/**\n * Check if a value is a Standard JSON Schema compliant schema.\n * Standard JSON Schema compliant libraries (Zod v4+, ArkType, Valibot with toStandardJsonSchema, etc.)\n * implement the '~standard' property with jsonSchema converter methods.\n */\nexport function isStandardJSONSchema(\n schema: unknown,\n): schema is StandardJSONSchemaV1 {\n if (!isPropertyCarrier(schema) || !('~standard' in schema)) return false\n\n const standard = schema['~standard']\n if (\n typeof standard !== 'object' ||\n standard === null ||\n !('version' in standard) ||\n standard.version !== 1 ||\n !('jsonSchema' in standard) ||\n typeof standard.jsonSchema !== 'object' ||\n standard.jsonSchema === null ||\n !('input' in standard.jsonSchema)\n ) {\n return false\n }\n\n return typeof standard.jsonSchema.input === 'function'\n}\n\n/**\n * Check if a value is a Standard Schema compliant schema (for validation).\n * Standard Schema compliant libraries implement the '~standard' property with a validate function.\n */\nexport function isStandardSchema(schema: unknown): schema is StandardSchemaV1 {\n return (\n isPropertyCarrier(schema) &&\n '~standard' in schema &&\n typeof schema['~standard'] === 'object' &&\n schema['~standard'] !== null &&\n 'version' in schema['~standard'] &&\n schema['~standard'].version === 1 &&\n 'validate' in schema['~standard'] &&\n typeof schema['~standard'].validate === 'function'\n )\n}\n\n/**\n * Result of {@link makeStructuredOutputCompatible}: the strict-ready schema plus\n * a {@link NullWideningMap} recording every position where a `null` was\n * synthesized, so the response can be un-widened before validation without\n * re-deriving (or guessing) which nulls were synthetic.\n */\ninterface StructuredOutputConversion {\n schema: JSONSchema\n nullWidening: NullWideningMap | undefined\n}\n\n/** Drop an empty map to `undefined` so leaf/no-op subtrees don't litter it. */\nfunction pruneMap(map: NullWideningMap): NullWideningMap | undefined {\n return Object.keys(map).length > 0 ? map : undefined\n}\n\nfunction coerceArrayItems(items: JSONSchema | Array<JSONSchema>): {\n schema: JSONSchema | Array<JSONSchema>\n itemMap: NullWideningMap | Array<NullWideningMap> | undefined\n} {\n if (Array.isArray(items)) {\n const nested = items.map((item) =>\n makeStructuredOutputCompatible(item, item.required || []),\n )\n const itemMaps = nested.map((entry) => entry.nullWidening ?? {})\n return {\n schema: nested.map((entry) => entry.schema),\n itemMap: itemMaps.some((entry) => Object.keys(entry).length > 0)\n ? itemMaps\n : undefined,\n }\n }\n const nested = makeStructuredOutputCompatible(items, items.required || [])\n return { schema: nested.schema, itemMap: nested.nullWidening }\n}\n\n/**\n * Transform a JSON schema to be compatible with OpenAI's structured output requirements.\n * OpenAI requires:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for objects\n *\n * Alongside the transformed schema it returns a {@link NullWideningMap} marking\n * exactly the positions where `null` was added, so `undoNullWidening` can strip\n * those synthesized nulls (and only those) from the provider's response.\n *\n * @param schema - JSON schema to transform\n * @param originalRequired - Original required array (to know which fields were optional)\n * @returns Transformed schema + the null-widening map for the round trip\n */\nfunction makeStructuredOutputCompatible(\n schema: JSONSchema,\n originalRequired: Array<string> = [],\n): StructuredOutputConversion {\n const result: JSONSchema = { ...schema }\n const map: NullWideningMap = {}\n\n // Handle object types\n if (result.type === 'object' && result.properties) {\n const properties: Record<string, JSONSchema> = { ...result.properties }\n const allPropertyNames = Object.keys(properties)\n const propertyMaps: Record<string, NullWideningMap> = {}\n\n // Transform each property\n for (const propName of allPropertyNames) {\n const prop = properties[propName]\n if (!prop) continue\n const wasOptional = !originalRequired.includes(propName)\n // `null` synthesized AT this property (the field itself can come back null).\n let widenedHere = false\n // Map describing widened positions INSIDE this property.\n let childMap: NullWideningMap | undefined\n\n // Recursively transform nested objects/arrays\n if (prop.type === 'object' && prop.properties) {\n const nested = makeStructuredOutputCompatible(prop, prop.required || [])\n properties[propName] = wasOptional\n ? { ...nested.schema, type: ['object', 'null'] }\n : nested.schema\n widenedHere = wasOptional\n childMap = nested.nullWidening\n } else if (prop.type === 'array' && prop.items) {\n const nestedItems = coerceArrayItems(prop.items)\n properties[propName] = {\n ...prop,\n items: nestedItems.schema,\n ...(wasOptional ? { type: ['array', 'null'] } : {}),\n }\n widenedHere = wasOptional\n childMap = nestedItems.itemMap\n ? { items: nestedItems.itemMap }\n : undefined\n } else if (wasOptional) {\n // Make optional fields nullable by adding null to the type. Mark\n // `widenedHere` only where we actually add `null`; a field already\n // typed nullable (`.nullish()`) is left as-is and keeps its null.\n if (prop.type && !Array.isArray(prop.type)) {\n properties[propName] = { ...prop, type: [prop.type, 'null'] }\n widenedHere = true\n } else if (Array.isArray(prop.type) && !prop.type.includes('null')) {\n properties[propName] = { ...prop, type: [...prop.type, 'null'] }\n widenedHere = true\n }\n }\n\n if (widenedHere || childMap) {\n propertyMaps[propName] = {\n ...(childMap ?? {}),\n ...(widenedHere ? { widened: true } : {}),\n }\n }\n }\n\n result.properties = properties\n // ALL properties must be required for OpenAI structured output\n result.required = allPropertyNames\n // additionalProperties must be false\n result.additionalProperties = false\n if (Object.keys(propertyMaps).length > 0) map.properties = propertyMaps\n }\n\n // Handle array item schemas. A tuple (`items: [a, b, …]`) keeps every\n // position. A homogeneous schema stays a single items map so\n // `undoNullWidening` applies it to every element.\n if (result.type === 'array' && result.items) {\n const nestedItems = coerceArrayItems(result.items)\n result.items = nestedItems.schema\n if (nestedItems.itemMap) map.items = nestedItems.itemMap\n }\n\n return { schema: result, nullWidening: pruneMap(map) }\n}\n\n/**\n * Options for schema conversion\n */\nexport interface ConvertSchemaOptions {\n /**\n * When true, transforms the schema to be compatible with OpenAI's structured output requirements:\n * - All properties are added to the `required` array\n * - Optional fields get null added to their type union\n * - additionalProperties is set to false for all objects\n *\n * @default false\n */\n forStructuredOutput?: boolean\n /**\n * Which view of a Standard JSON Schema to convert. A schema with a\n * transform or a pipe has a different `output` view. Use `'output'` to\n * describe the value that parsing returns.\n *\n * @default 'input'\n */\n io?: 'input' | 'output'\n}\n\n/**\n * Normalize any supported schema input to a typed, UN-widened `JSONSchema` —\n * the shared first half of conversion, before any structured-output widening.\n *\n * - Standard JSON Schemas are rebuilt structurally (dropping `$schema`, which\n * LLM providers ignore) and given the explicit `type`/`properties`/`required`\n * defaults object shapes need downstream. `io` picks the `input` (default)\n * or `output` view.\n * - Plain `JSONSchema` inputs are rebuilt into the typed view; non-object inputs\n * are surfaced untouched (they can't be widened).\n * - Standard Schema validators lacking a `~standard.jsonSchema` converter throw\n * with actionable guidance, rather than shipping `{ '~standard': … }` to the\n * provider and producing an opaque downstream error.\n */\nfunction toTypedJsonSchema(\n schema: SchemaInput,\n io: 'input' | 'output' = 'input',\n): JSONSchema | undefined {\n if (isStandardJSONSchema(schema)) {\n const jsonSchema = schema['~standard'].jsonSchema[io]({\n target: 'draft-07',\n })\n const result: JSONSchema = toJsonSchema(jsonSchema)\n if ('properties' in result && !result.type) result.type = 'object'\n if (result.type === 'object' && !('properties' in result)) {\n result.properties = {}\n }\n if (result.type === 'object' && !('required' in result)) {\n result.required = []\n }\n return result\n }\n\n if (isStandardSchema(schema)) {\n throw new Error(\n 'Schema is a Standard Schema validator but does not expose a JSON Schema ' +\n 'converter on `~standard.jsonSchema`. Use Zod v4.2+, ArkType v2.1.28+, ' +\n 'or wrap a Valibot schema with `toStandardJsonSchema()` from ' +\n '`@valibot/to-json-schema` before passing it as `outputSchema`.',\n )\n }\n\n if (typeof schema !== 'object') return schema\n return toJsonSchema(schema)\n}\n\n/**\n * Converts a Standard JSON Schema compliant schema or plain JSONSchema to JSON Schema format\n * compatible with LLM providers.\n *\n * Supports any schema library that implements the Standard JSON Schema spec (v1):\n * - Zod v4+ (natively supports StandardJSONSchemaV1)\n * - ArkType (natively supports StandardJSONSchemaV1)\n * - Valibot (via `toStandardJsonSchema()` from `@valibot/to-json-schema`)\n *\n * If the input is already a plain JSONSchema object, it is returned as-is.\n *\n * @param schema - Standard JSON Schema compliant schema or plain JSONSchema object to convert\n * @param options - Conversion options\n * @returns JSON Schema object that can be sent to LLM providers\n *\n * @example\n * ```typescript\n * // Using Zod v4+ (natively supports Standard JSON Schema)\n * import * as z from 'zod';\n *\n * const zodSchema = z.object({\n * location: z.string().describe('City name'),\n * unit: z.enum(['celsius', 'fahrenheit']).optional()\n * });\n *\n * const jsonSchema = convertSchemaToJsonSchema(zodSchema);\n *\n * @example\n * // Using ArkType (natively supports Standard JSON Schema)\n * import { type } from 'arktype';\n *\n * const arkSchema = type({\n * location: 'string',\n * unit: \"'celsius' | 'fahrenheit'\"\n * });\n *\n * const jsonSchema = convertSchemaToJsonSchema(arkSchema);\n *\n * @example\n * // Using Valibot (via toStandardJsonSchema)\n * import * as v from 'valibot';\n * import { toStandardJsonSchema } from '@valibot/to-json-schema';\n *\n * const valibotSchema = toStandardJsonSchema(v.object({\n * location: v.string(),\n * unit: v.optional(v.picklist(['celsius', 'fahrenheit']))\n * }));\n *\n * const jsonSchema = convertSchemaToJsonSchema(valibotSchema);\n *\n * @example\n * // Using JSONSchema directly (passes through unchanged)\n * const rawSchema = {\n * type: 'object',\n * properties: { location: { type: 'string' } },\n * required: ['location']\n * };\n * const result = convertSchemaToJsonSchema(rawSchema);\n * ```\n */\nexport function convertSchemaToJsonSchema(\n schema: SchemaInput | undefined,\n options: ConvertSchemaOptions = {},\n): JSONSchema | undefined {\n if (!schema) return undefined\n\n const { forStructuredOutput = false, io } = options\n\n // Plain-JSONSchema passthrough: with no widening requested, return the schema\n // by reference so callers comparing via `===` keep identity. Only the widening\n // path needs the rebuilt, normalized view from `toTypedJsonSchema`.\n if (\n !forStructuredOutput &&\n !isStandardJSONSchema(schema) &&\n !isStandardSchema(schema)\n ) {\n return schema\n }\n\n const base = toTypedJsonSchema(schema, io)\n // Non-object inputs can't be widened; surface them untouched.\n if (!base || typeof base !== 'object') return base\n if (!forStructuredOutput) return base\n return makeStructuredOutputCompatible(base, base.required || []).schema\n}\n\n/**\n * Convert a schema for structured output AND capture the {@link NullWideningMap}\n * recording every `null` the strict-mode widening synthesized. The map lets the\n * caller undo that widening on the provider's response (via `undoNullWidening`)\n * before validating against the original schema — optional fields read back as\n * absent while genuine `.nullable()` nulls survive. The map is `undefined` when\n * the schema isn't a widenable object or when no field needed widening.\n */\nexport function convertSchemaForStructuredOutput(\n schema: SchemaInput | undefined,\n): {\n jsonSchema: JSONSchema | undefined\n nullWideningMap: NullWideningMap | undefined\n} {\n if (!schema) return { jsonSchema: undefined, nullWideningMap: undefined }\n const base = toTypedJsonSchema(schema)\n if (!base || typeof base !== 'object') {\n return { jsonSchema: base, nullWideningMap: undefined }\n }\n const { schema: jsonSchema, nullWidening } = makeStructuredOutputCompatible(\n base,\n base.required || [],\n )\n return { jsonSchema, nullWideningMap: nullWidening }\n}\n\n/**\n * Validates data against a Standard Schema compliant schema.\n *\n * @param schema - Standard Schema compliant schema\n * @param data - Data to validate\n * @returns Validation result with success status, data or issues\n */\nexport async function validateWithStandardSchema<T>(\n schema: unknown,\n data: unknown,\n): Promise<\n | { success: true; data: T }\n | {\n success: false\n issues: Array<{ message: string; path?: Array<string> | undefined }>\n }\n> {\n if (!isStandardSchema(schema)) {\n // If it's not a Standard Schema, just return the data as-is\n return { success: true, data: data as T }\n }\n\n const result = await schema['~standard'].validate(data)\n\n if (!result.issues) {\n return { success: true, data: result.value as T }\n }\n\n return {\n success: false,\n issues: result.issues.map((issue) => ({\n message: issue.message || 'Validation failed',\n path: issue.path?.map(String),\n })),\n }\n}\n\n/**\n * Error thrown when Standard Schema validation fails. Carries the original\n * `issues` array so consumers (middleware `onError`, callers catching from\n * `chat({ outputSchema })`) can programmatically inspect each failure.\n */\nexport class StandardSchemaValidationError extends Error {\n override readonly name = 'StandardSchemaValidationError'\n readonly issues: ReadonlyArray<StandardSchemaV1.Issue>\n\n constructor(issues: ReadonlyArray<StandardSchemaV1.Issue>) {\n super(\n `Validation failed: ${issues\n .map((i) => i.message || 'Validation failed')\n .join(', ')}`,\n )\n this.issues = issues\n }\n}\n\n/**\n * Synchronously validates data against a Standard Schema compliant schema.\n * Note: Some Standard Schema implementations may only support async validation.\n * In those cases, this function will throw.\n *\n * @param schema - Standard Schema compliant schema\n * @param data - Data to validate\n * @returns Parsed/validated data\n * @throws StandardSchemaValidationError if validation fails; Error if the\n * schema only supports async validation.\n */\nexport function parseWithStandardSchema<T>(schema: unknown, data: unknown): T {\n if (!isStandardSchema(schema)) {\n // If it's not a Standard Schema, just return the data as-is\n return data as T\n }\n\n const result = schema['~standard'].validate(data)\n\n // Handle async result (Promise)\n if (result instanceof Promise) {\n throw new Error(\n 'Schema validation returned a Promise. Use validateWithStandardSchema for async validation.',\n )\n }\n // Standard Schema validation returns { value } for success or { issues } for failure\n if (!result.issues) {\n return result.value as T\n }\n\n throw new StandardSchemaValidationError(result.issues)\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AdapterYieldChunk } from '../../../utilities/adapter-yield-chunk.js';
|
|
2
2
|
import { ToolApprovalResolution } from '../../../interrupts.js';
|
|
3
|
-
import { AnyTool, CustomEvent, EmitCustomEventOptions, Interrupt, ModelMessage, RunFinishedEvent, StreamChunk, Tool, ToolCall, ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent, ToolExecutionContext } from '../../../types.js';
|
|
3
|
+
import { AnyTool, CustomEvent, EmitCustomEventOptions, Interrupt, ModelMessage, RunFinishedEvent, StreamChunk, TextOptions, Tool, ToolCall, ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent, ToolExecutionContext, ToolInputResponse, ToolResultOutcome } from '../../../types.js';
|
|
4
4
|
import { AfterToolCallInfo, BeforeToolCallDecision } from '../middleware/types.js';
|
|
5
5
|
import { ContextFromTool, DefinedContext, MergeContext, UnionToIntersection } from '../runtime-context-types.js';
|
|
6
6
|
/** Marks the synthetic tool that runs a subagent. */
|
|
@@ -113,6 +113,8 @@ export interface ToolResult {
|
|
|
113
113
|
toolName: string;
|
|
114
114
|
result: any;
|
|
115
115
|
state?: 'output-available' | 'output-error';
|
|
116
|
+
/** Set when the user or middleware cancelled or denied the tool call; state is output-error. */
|
|
117
|
+
outcome?: ToolResultOutcome;
|
|
116
118
|
/** Duration of tool execution in milliseconds (only for server-executed tools) */
|
|
117
119
|
duration?: number;
|
|
118
120
|
/**
|
|
@@ -138,9 +140,19 @@ export interface ClientToolRequest {
|
|
|
138
140
|
toolName: string;
|
|
139
141
|
input: any;
|
|
140
142
|
}
|
|
143
|
+
/** Form or sampling input that paused a server tool. */
|
|
144
|
+
export interface McpInputRequest {
|
|
145
|
+
toolCallId: string;
|
|
146
|
+
toolName: string;
|
|
147
|
+
kind: 'form' | 'sampling';
|
|
148
|
+
request: unknown;
|
|
149
|
+
}
|
|
141
150
|
export interface ToolResumeExecutionState {
|
|
151
|
+
clientToolErrors?: ReadonlyMap<string, string>;
|
|
142
152
|
deniedToolResults?: ReadonlyMap<string, unknown>;
|
|
143
153
|
cancelledToolCallIds?: ReadonlySet<string>;
|
|
154
|
+
/** Answers to `mcp_input` interrupts, by tool call id. */
|
|
155
|
+
inputResponses?: ReadonlyMap<string, ToolInputResponse>;
|
|
144
156
|
}
|
|
145
157
|
interface ExecuteToolCallsResult {
|
|
146
158
|
/** Tool results ready to send to LLM */
|
|
@@ -149,6 +161,8 @@ interface ExecuteToolCallsResult {
|
|
|
149
161
|
needsApproval: Array<ApprovalRequest>;
|
|
150
162
|
/** Tools that need client-side execution */
|
|
151
163
|
needsClientExecution: Array<ClientToolRequest>;
|
|
164
|
+
/** Server tools that paused for MCP form or sampling input */
|
|
165
|
+
inputRequired: Array<McpInputRequest>;
|
|
152
166
|
/** Interrupts raised by subagents that run as tools */
|
|
153
167
|
subagentInterrupts: Array<Interrupt>;
|
|
154
168
|
}
|
|
@@ -156,7 +170,7 @@ interface ExecuteToolCallsResult {
|
|
|
156
170
|
* Execute a server-side tool with event polling, output validation, and middleware hooks.
|
|
157
171
|
* Yields CustomEvent chunks during execution and pushes the result to the results array.
|
|
158
172
|
*/
|
|
159
|
-
export declare function executeServerTool<TContext = unknown>(toolCall: ToolCall, tool: AnyTool, toolName: string, input: unknown, context: ToolExecutionContext<TContext>, pendingEvents: Array<CustomEvent | StreamChunk>, results: Array<ToolResult>, middlewareHooks?: ToolExecutionMiddlewareHooks, subagentInterrupts?: Array<Interrupt>): AsyncGenerator<CustomEvent | StreamChunk, void, void>;
|
|
173
|
+
export declare function executeServerTool<TContext = unknown>(toolCall: ToolCall, tool: AnyTool, toolName: string, input: unknown, context: ToolExecutionContext<TContext>, pendingEvents: Array<CustomEvent | StreamChunk>, results: Array<ToolResult>, middlewareHooks?: ToolExecutionMiddlewareHooks, inputRequired?: Array<McpInputRequest>, subagentInterrupts?: Array<Interrupt>): AsyncGenerator<CustomEvent | StreamChunk, void, void>;
|
|
160
174
|
/**
|
|
161
175
|
* Execute tool calls based on their configuration.
|
|
162
176
|
* Yields CustomEvent chunks during tool execution for real-time progress updates.
|
|
@@ -171,6 +185,9 @@ export declare function executeServerTool<TContext = unknown>(toolCall: ToolCall
|
|
|
171
185
|
* @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution
|
|
172
186
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
173
187
|
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
188
|
+
* @param toolExecution - `'parallel'` (default) prepares every call in call
|
|
189
|
+
* order, then starts the server tools together. `'sequential'` runs one
|
|
190
|
+
* call at a time. Results come back in call order either way.
|
|
174
191
|
*/
|
|
175
|
-
export declare function executeToolCalls<TContext = unknown>(toolCalls: Array<ToolCall>, tools: ReadonlyArray<AnyTool>, approvals?: Map<string, ToolApprovalResolution>, clientResults?: Map<string, any>, createCustomEventChunk?: (eventName: string, value: Record<string, any>, options?: EmitCustomEventOptions) => CustomEvent, middlewareHooks?: ToolExecutionMiddlewareHooks, userContext?: TContext, abortSignal?: AbortSignal, resumeState?: ToolResumeExecutionState): AsyncGenerator<CustomEvent | StreamChunk, ExecuteToolCallsResult, void>;
|
|
192
|
+
export declare function executeToolCalls<TContext = unknown>(toolCalls: Array<ToolCall>, tools: ReadonlyArray<AnyTool>, approvals?: Map<string, ToolApprovalResolution>, clientResults?: Map<string, any>, createCustomEventChunk?: (eventName: string, value: Record<string, any>, options?: EmitCustomEventOptions) => CustomEvent, middlewareHooks?: ToolExecutionMiddlewareHooks, userContext?: TContext, abortSignal?: AbortSignal, resumeState?: ToolResumeExecutionState, toolExecution?: NonNullable<TextOptions['toolExecution']>): AsyncGenerator<CustomEvent | StreamChunk, ExecuteToolCallsResult, void>;
|
|
176
193
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { tanstackMetadata } from "../../../utilities/merge-metadata.js";
|
|
2
|
-
import { isStandardSchema, parseWithStandardSchema } from "./schema-converter.js";
|
|
2
|
+
import { StandardSchemaValidationError, isStandardSchema, parseWithStandardSchema, validateWithStandardSchema } from "./schema-converter.js";
|
|
3
3
|
import { isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
|
|
4
4
|
import { normalizeToolResult } from "../../../utilities/tool-result.js";
|
|
5
|
+
import { mergeStreams } from "../../../utilities/merge-streams.js";
|
|
5
6
|
//#region src/activities/chat/tools/tool-calls.ts
|
|
6
7
|
function safeJsonParse(value) {
|
|
7
8
|
try {
|
|
@@ -236,6 +237,13 @@ var ToolCallManager = class {
|
|
|
236
237
|
this.toolCallsMap.clear();
|
|
237
238
|
}
|
|
238
239
|
};
|
|
240
|
+
function isMcpInputRequired(value) {
|
|
241
|
+
if (typeof value !== "object" || value === null) return false;
|
|
242
|
+
if (!("name" in value) || value.name !== "MCPInputRequiredError") return false;
|
|
243
|
+
if (!("kind" in value)) return false;
|
|
244
|
+
if (!(value.kind === "form" || value.kind === "sampling")) return false;
|
|
245
|
+
return "request" in value;
|
|
246
|
+
}
|
|
239
247
|
function approvalResolution(approvals, toolCallId) {
|
|
240
248
|
return approvals.get(toolCallId) ?? approvals.get(`approval_${toolCallId}`);
|
|
241
249
|
}
|
|
@@ -317,7 +325,7 @@ async function applyBeforeToolCallDecision(toolCall, tool, input, toolName, midd
|
|
|
317
325
|
* Execute a server-side tool with event polling, output validation, and middleware hooks.
|
|
318
326
|
* Yields CustomEvent chunks during execution and pushes the result to the results array.
|
|
319
327
|
*/
|
|
320
|
-
async function* executeServerTool(toolCall, tool, toolName, input, context, pendingEvents, results, middlewareHooks, subagentInterrupts) {
|
|
328
|
+
async function* executeServerTool(toolCall, tool, toolName, input, context, pendingEvents, results, middlewareHooks, inputRequired, subagentInterrupts) {
|
|
321
329
|
const startTime = Date.now();
|
|
322
330
|
try {
|
|
323
331
|
if (!tool.execute) throw new Error(`Tool ${toolName} has no execute() implementation`);
|
|
@@ -390,6 +398,16 @@ async function* executeServerTool(toolCall, tool, toolName, input, context, pend
|
|
|
390
398
|
let pendingEvent;
|
|
391
399
|
while ((pendingEvent = pendingEvents.shift()) !== void 0) yield pendingEvent;
|
|
392
400
|
if (error instanceof MiddlewareAbortError) throw error;
|
|
401
|
+
if (isMcpInputRequired(error)) {
|
|
402
|
+
if (!inputRequired) throw error;
|
|
403
|
+
inputRequired.push({
|
|
404
|
+
toolCallId: toolCall.id,
|
|
405
|
+
toolName,
|
|
406
|
+
kind: error.kind,
|
|
407
|
+
request: error.request
|
|
408
|
+
});
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
393
411
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
394
412
|
results.push({
|
|
395
413
|
toolCallId: toolCall.id,
|
|
@@ -410,10 +428,21 @@ async function* executeServerTool(toolCall, tool, toolName, input, context, pend
|
|
|
410
428
|
});
|
|
411
429
|
}
|
|
412
430
|
}
|
|
413
|
-
function buildClientToolResult(toolCallId, toolName, tool, rawResult, input) {
|
|
431
|
+
async function buildClientToolResult(toolCallId, toolName, tool, rawResult, input, errorText) {
|
|
432
|
+
if (errorText !== void 0) return {
|
|
433
|
+
toolCallId,
|
|
434
|
+
toolName,
|
|
435
|
+
result: { error: errorText },
|
|
436
|
+
input,
|
|
437
|
+
state: "output-error"
|
|
438
|
+
};
|
|
414
439
|
try {
|
|
415
440
|
let result = rawResult;
|
|
416
|
-
if (tool.outputSchema && isStandardSchema(tool.outputSchema))
|
|
441
|
+
if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {
|
|
442
|
+
const validation = await validateWithStandardSchema(tool.outputSchema, result);
|
|
443
|
+
if (!validation.success) throw new StandardSchemaValidationError(validation.issues);
|
|
444
|
+
result = validation.data;
|
|
445
|
+
}
|
|
417
446
|
const parsed = typeof result === "string" ? safeJsonParse(result) : result ?? null;
|
|
418
447
|
return {
|
|
419
448
|
toolCallId,
|
|
@@ -446,14 +475,50 @@ function buildClientToolResult(toolCallId, toolName, tool, rawResult, input) {
|
|
|
446
475
|
* @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution
|
|
447
476
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
448
477
|
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
478
|
+
* @param toolExecution - `'parallel'` (default) prepares every call in call
|
|
479
|
+
* order, then starts the server tools together. `'sequential'` runs one
|
|
480
|
+
* call at a time. Results come back in call order either way.
|
|
449
481
|
*/
|
|
450
|
-
async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ new Map(), clientResults = /* @__PURE__ */ new Map(), createCustomEventChunk, middlewareHooks, userContext, abortSignal, resumeState) {
|
|
482
|
+
async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ new Map(), clientResults = /* @__PURE__ */ new Map(), createCustomEventChunk, middlewareHooks, userContext, abortSignal, resumeState, toolExecution = "parallel") {
|
|
451
483
|
const results = [];
|
|
452
484
|
const needsApproval = [];
|
|
453
485
|
const needsClientExecution = [];
|
|
454
|
-
const
|
|
486
|
+
const inputRequired = [];
|
|
455
487
|
const toolMap = /* @__PURE__ */ new Map();
|
|
456
488
|
for (const tool of tools) toolMap.set(tool.name, tool);
|
|
489
|
+
const runsInOrder = toolExecution === "sequential";
|
|
490
|
+
const runs = [];
|
|
491
|
+
const failures = [];
|
|
492
|
+
const interruptsByCall = /* @__PURE__ */ new Map();
|
|
493
|
+
async function* runServerTool(toolCall, tool, toolName, input, context, pendingEvents) {
|
|
494
|
+
try {
|
|
495
|
+
if (abortSignal?.aborted) {
|
|
496
|
+
results.push({
|
|
497
|
+
toolCallId: toolCall.id,
|
|
498
|
+
toolName,
|
|
499
|
+
result: { error: "Operation aborted" },
|
|
500
|
+
input,
|
|
501
|
+
state: "output-error",
|
|
502
|
+
duration: 0
|
|
503
|
+
});
|
|
504
|
+
await middlewareHooks?.onAfterToolCall?.({
|
|
505
|
+
toolCall,
|
|
506
|
+
tool,
|
|
507
|
+
toolName,
|
|
508
|
+
toolCallId: toolCall.id,
|
|
509
|
+
ok: false,
|
|
510
|
+
duration: 0,
|
|
511
|
+
error: /* @__PURE__ */ new Error("Operation aborted")
|
|
512
|
+
});
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const interrupts = [];
|
|
516
|
+
interruptsByCall.set(toolCall.id, interrupts);
|
|
517
|
+
yield* executeServerTool(toolCall, tool, toolName, input, context, pendingEvents, results, middlewareHooks, inputRequired, interrupts);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
failures.push(error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
457
522
|
const hasPendingApprovals = toolCalls.some((tc) => {
|
|
458
523
|
return toolMap.get(tc.function.name)?.needsApproval && approvalResolution(approvals, tc.id) === void 0 && !resumeState?.cancelledToolCallIds?.has(tc.id);
|
|
459
524
|
});
|
|
@@ -480,7 +545,8 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
480
545
|
toolCallId: toolCall.id,
|
|
481
546
|
toolName,
|
|
482
547
|
result: { error: "Tool execution cancelled" },
|
|
483
|
-
state: "output-error"
|
|
548
|
+
state: "output-error",
|
|
549
|
+
outcome: "cancelled"
|
|
484
550
|
});
|
|
485
551
|
continue;
|
|
486
552
|
}
|
|
@@ -513,10 +579,12 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
513
579
|
continue;
|
|
514
580
|
}
|
|
515
581
|
const pendingEvents = [];
|
|
582
|
+
const inputResponse = resumeState?.inputResponses?.get(toolCall.id);
|
|
516
583
|
const context = {
|
|
517
584
|
toolCallId: toolCall.id,
|
|
518
585
|
context: userContext,
|
|
519
586
|
abortSignal,
|
|
587
|
+
...inputResponse !== void 0 ? { inputResponse } : {},
|
|
520
588
|
emitCustomEvent: (eventName, value, options) => {
|
|
521
589
|
if (createCustomEventChunk) pendingEvents.push(createCustomEventChunk(eventName, {
|
|
522
590
|
...value,
|
|
@@ -531,7 +599,8 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
531
599
|
if (resolution !== void 0) {
|
|
532
600
|
if (isApproved(resolution)) {
|
|
533
601
|
input = editedApprovalArgs(resolution) ?? input;
|
|
534
|
-
|
|
602
|
+
const clientError = resumeState?.clientToolErrors?.get(toolCall.id);
|
|
603
|
+
if (clientResults.has(toolCall.id) || clientError !== void 0) results.push(await buildClientToolResult(toolCall.id, toolName, tool, clientResults.get(toolCall.id), input, clientError));
|
|
535
604
|
else needsClientExecution.push({
|
|
536
605
|
toolCallId: toolCall.id,
|
|
537
606
|
toolName,
|
|
@@ -542,7 +611,8 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
542
611
|
toolName,
|
|
543
612
|
result: resumeState?.deniedToolResults?.get(toolCall.id) ?? deniedApprovalResult(resolution),
|
|
544
613
|
input,
|
|
545
|
-
state: "output-error"
|
|
614
|
+
state: "output-error",
|
|
615
|
+
outcome: "denied"
|
|
546
616
|
});
|
|
547
617
|
} else needsApproval.push({
|
|
548
618
|
toolCallId: toolCall.id,
|
|
@@ -550,53 +620,73 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
550
620
|
input,
|
|
551
621
|
approvalId
|
|
552
622
|
});
|
|
553
|
-
} else
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
623
|
+
} else {
|
|
624
|
+
const clientError = resumeState?.clientToolErrors?.get(toolCall.id);
|
|
625
|
+
if (clientResults.has(toolCall.id) || clientError !== void 0) results.push(await buildClientToolResult(toolCall.id, toolName, tool, clientResults.get(toolCall.id), input, clientError));
|
|
626
|
+
else needsClientExecution.push({
|
|
627
|
+
toolCallId: toolCall.id,
|
|
628
|
+
toolName,
|
|
629
|
+
input
|
|
630
|
+
});
|
|
631
|
+
}
|
|
559
632
|
continue;
|
|
560
633
|
}
|
|
561
634
|
if (tool.needsApproval) {
|
|
562
635
|
const approvalId = `approval_${toolCall.id}`;
|
|
563
636
|
const resolution = approvalResolution(approvals, toolCall.id);
|
|
564
|
-
if (resolution
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
637
|
+
if (resolution === void 0) {
|
|
638
|
+
needsApproval.push({
|
|
639
|
+
toolCallId: toolCall.id,
|
|
640
|
+
toolName,
|
|
641
|
+
input,
|
|
642
|
+
approvalId
|
|
643
|
+
});
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (!isApproved(resolution)) {
|
|
647
|
+
results.push({
|
|
574
648
|
toolCallId: toolCall.id,
|
|
575
649
|
toolName,
|
|
576
650
|
result: resumeState?.deniedToolResults?.get(toolCall.id) ?? deniedApprovalResult(resolution),
|
|
577
651
|
input,
|
|
578
|
-
state: "output-error"
|
|
652
|
+
state: "output-error",
|
|
653
|
+
outcome: "denied"
|
|
579
654
|
});
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
input,
|
|
584
|
-
approvalId
|
|
585
|
-
});
|
|
586
|
-
continue;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
input = editedApprovalArgs(resolution) ?? input;
|
|
587
658
|
}
|
|
588
659
|
if (middlewareHooks) {
|
|
589
|
-
|
|
660
|
+
let decision;
|
|
661
|
+
try {
|
|
662
|
+
decision = await applyBeforeToolCallDecision(toolCall, tool, input, toolName, middlewareHooks, results);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
failures.push(error);
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
590
667
|
if (!decision.proceed) continue;
|
|
591
668
|
input = decision.input;
|
|
592
669
|
}
|
|
593
|
-
|
|
670
|
+
const run = runServerTool(toolCall, tool, toolName, input, context, pendingEvents);
|
|
671
|
+
if (!runsInOrder) {
|
|
672
|
+
runs.push(run);
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
yield* run;
|
|
676
|
+
if (failures.length > 0) break;
|
|
594
677
|
}
|
|
678
|
+
yield* mergeStreams(runs);
|
|
679
|
+
if (failures.length > 0) throw failures[0];
|
|
680
|
+
const callOrder = new Map(toolCalls.map((tc, index) => [tc.id, index]));
|
|
681
|
+
const byCallOrder = (a, b) => (callOrder.get(a.toolCallId) ?? 0) - (callOrder.get(b.toolCallId) ?? 0);
|
|
682
|
+
results.sort(byCallOrder);
|
|
683
|
+
inputRequired.sort(byCallOrder);
|
|
595
684
|
return {
|
|
596
685
|
results,
|
|
597
686
|
needsApproval,
|
|
598
687
|
needsClientExecution,
|
|
599
|
-
|
|
688
|
+
inputRequired,
|
|
689
|
+
subagentInterrupts: toolCalls.flatMap((tc) => interruptsByCall.get(tc.id) ?? [])
|
|
600
690
|
};
|
|
601
691
|
}
|
|
602
692
|
//#endregion
|