@tanstack/ai 0.63.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/dist/esm/activities/chat/agents/spawn.js +2 -28
- 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 +10 -3
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +13 -4
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +4 -1
- package/dist/esm/activities/chat/stream/message-updaters.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 +5 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +73 -22
- package/dist/esm/activities/chat/tools/tool-calls.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/types.d.ts +28 -3
- package/dist/esm/utilities/ag-ui-wire.js +3 -1
- 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/package.json +1 -1
- package/skills/ai-core/media-generation/SKILL.md +3 -3
- package/src/activities/chat/agents/spawn.ts +2 -31
- package/src/activities/chat/index.ts +13 -3
- package/src/activities/chat/messages.ts +14 -2
- package/src/activities/chat/stream/message-updaters.ts +5 -0
- package/src/activities/chat/tools/schema-converter.ts +17 -5
- package/src/activities/chat/tools/tool-calls.ts +121 -63
- 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/types.ts +28 -4
- package/src/utilities/ag-ui-wire.ts +5 -1
- package/src/utilities/merge-streams.ts +34 -0
- package/src/utilities/reasoning-encrypted-value.ts +12 -0
|
@@ -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":";;;;;;;;;;;;AAkBA,SAAS,aAAa,KAAyB;CAC7C,MAAM,SAAqB,CAAC;CAC5B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAAG;EAC9C,IAAI,QAAQ,WAAW;EACvB,OAAO,OAAO;CAChB;CACA,OAAO;AACT;;;;;;;;AASA,SAAS,kBAAkB,QAAoD;CAC7E,QACG,OAAO,WAAW,YAAY,OAAO,WAAW,eACjD,WAAW;AAEf;;;;;;AAOA,SAAgB,qBACd,QACgC;CAChC,IAAI,CAAC,kBAAkB,MAAM,KAAK,EAAE,eAAe,SAAS,OAAO;CAEnE,MAAM,WAAW,OAAO;CACxB,IACE,OAAO,aAAa,YACpB,aAAa,QACb,EAAE,aAAa,aACf,SAAS,YAAY,KACrB,EAAE,gBAAgB,aAClB,OAAO,SAAS,eAAe,YAC/B,SAAS,eAAe,QACxB,EAAE,WAAW,SAAS,aAEtB,OAAO;CAGT,OAAO,OAAO,SAAS,WAAW,UAAU;AAC9C;;;;;AAMA,SAAgB,iBAAiB,QAA6C;CAC5E,OACE,kBAAkB,MAAM,KACxB,eAAe,UACf,OAAO,OAAO,iBAAiB,YAC/B,OAAO,iBAAiB,QACxB,aAAa,OAAO,gBACpB,OAAO,YAAY,CAAC,YAAY,KAChC,cAAc,OAAO,gBACrB,OAAO,OAAO,YAAY,CAAC,aAAa;AAE5C;;AAcA,SAAS,SAAS,KAAmD;CACnE,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,MAAM,KAAA;AAC7C;AAEA,SAAS,iBAAiB,OAGxB;CACA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,MAAM,SAAS,MAAM,KAAK,SACxB,+BAA+B,MAAM,KAAK,YAAY,CAAC,CAAC,CAC1D;EACA,MAAM,WAAW,OAAO,KAAK,UAAU,MAAM,gBAAgB,CAAC,CAAC;EAC/D,OAAO;GACL,QAAQ,OAAO,KAAK,UAAU,MAAM,MAAM;GAC1C,SAAS,SAAS,MAAM,UAAU,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,CAAC,IAC3D,WACA,KAAA;EACN;CACF;CACA,MAAM,SAAS,+BAA+B,OAAO,MAAM,YAAY,CAAC,CAAC;CACzE,OAAO;EAAE,QAAQ,OAAO;EAAQ,SAAS,OAAO;CAAa;AAC/D;;;;;;;;;;;;;;;;AAiBA,SAAS,+BACP,QACA,mBAAkC,CAAC,GACP;CAC5B,MAAM,SAAqB,EAAE,GAAG,OAAO;CACvC,MAAM,MAAuB,CAAC;CAG9B,IAAI,OAAO,SAAS,YAAY,OAAO,YAAY;EACjD,MAAM,aAAyC,EAAE,GAAG,OAAO,WAAW;EACtE,MAAM,mBAAmB,OAAO,KAAK,UAAU;EAC/C,MAAM,eAAgD,CAAC;EAGvD,KAAK,MAAM,YAAY,kBAAkB;GACvC,MAAM,OAAO,WAAW;GACxB,IAAI,CAAC,MAAM;GACX,MAAM,cAAc,CAAC,iBAAiB,SAAS,QAAQ;GAEvD,IAAI,cAAc;GAElB,IAAI;GAGJ,IAAI,KAAK,SAAS,YAAY,KAAK,YAAY;IAC7C,MAAM,SAAS,+BAA+B,MAAM,KAAK,YAAY,CAAC,CAAC;IACvE,WAAW,YAAY,cACnB;KAAE,GAAG,OAAO;KAAQ,MAAM,CAAC,UAAU,MAAM;IAAE,IAC7C,OAAO;IACX,cAAc;IACd,WAAW,OAAO;GACpB,OAAO,IAAI,KAAK,SAAS,WAAW,KAAK,OAAO;IAC9C,MAAM,cAAc,iBAAiB,KAAK,KAAK;IAC/C,WAAW,YAAY;KACrB,GAAG;KACH,OAAO,YAAY;KACnB,GAAI,cAAc,EAAE,MAAM,CAAC,SAAS,MAAM,EAAE,IAAI,CAAC;IACnD;IACA,cAAc;IACd,WAAW,YAAY,UACnB,EAAE,OAAO,YAAY,QAAQ,IAC7B,KAAA;GACN,OAAO,IAAI,aAAa;IAItB,IAAI,KAAK,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,GAAG;KAC1C,WAAW,YAAY;MAAE,GAAG;MAAM,MAAM,CAAC,KAAK,MAAM,MAAM;KAAE;KAC5D,cAAc;IAChB,OAAO,IAAI,MAAM,QAAQ,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,MAAM,GAAG;KAClE,WAAW,YAAY;MAAE,GAAG;MAAM,MAAM,CAAC,GAAG,KAAK,MAAM,MAAM;KAAE;KAC/D,cAAc;IAChB;GACF;GAEA,IAAI,eAAe,UACjB,aAAa,YAAY;IACvB,GAAI,YAAY,CAAC;IACjB,GAAI,cAAc,EAAE,SAAS,KAAK,IAAI,CAAC;GACzC;EAEJ;EAEA,OAAO,aAAa;EAEpB,OAAO,WAAW;EAElB,OAAO,uBAAuB;EAC9B,IAAI,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,GAAG,IAAI,aAAa;CAC7D;CAKA,IAAI,OAAO,SAAS,WAAW,OAAO,OAAO;EAC3C,MAAM,cAAc,iBAAiB,OAAO,KAAK;EACjD,OAAO,QAAQ,YAAY;EAC3B,IAAI,YAAY,SAAS,IAAI,QAAQ,YAAY;CACnD;CAEA,OAAO;EAAE,QAAQ;EAAQ,cAAc,SAAS,GAAG;CAAE;AACvD;;;;;;;;;;;;;;AA8BA,SAAS,kBAAkB,QAA6C;CACtE,IAAI,qBAAqB,MAAM,GAAG;EAIhC,MAAM,SAAqB,aAHR,OAAO,YAAY,CAAC,WAAW,MAAM,EACtD,QAAQ,WACV,CACwC,CAAU;EAClD,IAAI,gBAAgB,UAAU,CAAC,OAAO,MAAM,OAAO,OAAO;EAC1D,IAAI,OAAO,SAAS,YAAY,EAAE,gBAAgB,SAChD,OAAO,aAAa,CAAC;EAEvB,IAAI,OAAO,SAAS,YAAY,EAAE,cAAc,SAC9C,OAAO,WAAW,CAAC;EAErB,OAAO;CACT;CAEA,IAAI,iBAAiB,MAAM,GACzB,MAAM,IAAI,MACR,0QAIF;CAGF,IAAI,OAAO,WAAW,UAAU,OAAO;CACvC,OAAO,aAAa,MAAM;AAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,SAAgB,0BACd,QACA,UAAgC,CAAC,GACT;CACxB,IAAI,CAAC,QAAQ,OAAO,KAAA;CAEpB,MAAM,EAAE,sBAAsB,UAAU;CAKxC,IACE,CAAC,uBACD,CAAC,qBAAqB,MAAM,KAC5B,CAAC,iBAAiB,MAAM,GAExB,OAAO;CAGT,MAAM,OAAO,kBAAkB,MAAM;CAErC,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,IAAI,CAAC,qBAAqB,OAAO;CACjC,OAAO,+BAA+B,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC;AACnE;;;;;;;;;AAUA,SAAgB,iCACd,QAIA;CACA,IAAI,CAAC,QAAQ,OAAO;EAAE,YAAY,KAAA;EAAW,iBAAiB,KAAA;CAAU;CACxE,MAAM,OAAO,kBAAkB,MAAM;CACrC,IAAI,CAAC,QAAQ,OAAO,SAAS,UAC3B,OAAO;EAAE,YAAY;EAAM,iBAAiB,KAAA;CAAU;CAExD,MAAM,EAAE,QAAQ,YAAY,iBAAiB,+BAC3C,MACA,KAAK,YAAY,CAAC,CACpB;CACA,OAAO;EAAE;EAAY,iBAAiB;CAAa;AACrD;;;;;;;;AASA,eAAsB,2BACpB,QACA,MAOA;CACA,IAAI,CAAC,iBAAiB,MAAM,GAE1B,OAAO;EAAE,SAAS;EAAY;CAAU;CAG1C,MAAM,SAAS,MAAM,OAAO,YAAY,CAAC,SAAS,IAAI;CAEtD,IAAI,CAAC,OAAO,QACV,OAAO;EAAE,SAAS;EAAM,MAAM,OAAO;CAAW;CAGlD,OAAO;EACL,SAAS;EACT,QAAQ,OAAO,OAAO,KAAK,WAAW;GACpC,SAAS,MAAM,WAAW;GAC1B,MAAM,MAAM,MAAM,IAAI,MAAM;EAC9B,EAAE;CACJ;AACF;;;;;;AAOA,IAAa,gCAAb,cAAmD,MAAM;CACvD,OAAyB;CACzB;CAEA,YAAY,QAA+C;EACzD,MACE,sBAAsB,OACnB,KAAK,MAAM,EAAE,WAAW,mBAAmB,CAAC,CAC5C,KAAK,IAAI,GACd;EACA,KAAK,SAAS;CAChB;AACF;;;;;;;;;;;;AAaA,SAAgB,wBAA2B,QAAiB,MAAkB;CAC5E,IAAI,CAAC,iBAAiB,MAAM,GAE1B,OAAO;CAGT,MAAM,SAAS,OAAO,YAAY,CAAC,SAAS,IAAI;CAGhD,IAAI,kBAAkB,SACpB,MAAM,IAAI,MACR,4FACF;CAGF,IAAI,CAAC,OAAO,QACV,OAAO,OAAO;CAGhB,MAAM,IAAI,8BAA8B,OAAO,MAAM;AACvD"}
|
|
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, ToolInputResponse, ToolResultOutcome } 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. */
|
|
@@ -185,6 +185,9 @@ export declare function executeServerTool<TContext = unknown>(toolCall: ToolCall
|
|
|
185
185
|
* @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution
|
|
186
186
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
187
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.
|
|
188
191
|
*/
|
|
189
|
-
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>;
|
|
190
193
|
export {};
|
|
@@ -2,6 +2,7 @@ import { tanstackMetadata } from "../../../utilities/merge-metadata.js";
|
|
|
2
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 {
|
|
@@ -474,15 +475,50 @@ async function buildClientToolResult(toolCallId, toolName, tool, rawResult, inpu
|
|
|
474
475
|
* @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution
|
|
475
476
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
476
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.
|
|
477
481
|
*/
|
|
478
|
-
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") {
|
|
479
483
|
const results = [];
|
|
480
484
|
const needsApproval = [];
|
|
481
485
|
const needsClientExecution = [];
|
|
482
486
|
const inputRequired = [];
|
|
483
|
-
const subagentInterrupts = [];
|
|
484
487
|
const toolMap = /* @__PURE__ */ new Map();
|
|
485
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
|
+
}
|
|
486
522
|
const hasPendingApprovals = toolCalls.some((tc) => {
|
|
487
523
|
return toolMap.get(tc.function.name)?.needsApproval && approvalResolution(approvals, tc.id) === void 0 && !resumeState?.cancelledToolCallIds?.has(tc.id);
|
|
488
524
|
});
|
|
@@ -598,16 +634,17 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
598
634
|
if (tool.needsApproval) {
|
|
599
635
|
const approvalId = `approval_${toolCall.id}`;
|
|
600
636
|
const resolution = approvalResolution(approvals, toolCall.id);
|
|
601
|
-
if (resolution
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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({
|
|
611
648
|
toolCallId: toolCall.id,
|
|
612
649
|
toolName,
|
|
613
650
|
result: resumeState?.deniedToolResults?.get(toolCall.id) ?? deniedApprovalResult(resolution),
|
|
@@ -615,27 +652,41 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
615
652
|
state: "output-error",
|
|
616
653
|
outcome: "denied"
|
|
617
654
|
});
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
input,
|
|
622
|
-
approvalId
|
|
623
|
-
});
|
|
624
|
-
continue;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
input = editedApprovalArgs(resolution) ?? input;
|
|
625
658
|
}
|
|
626
659
|
if (middlewareHooks) {
|
|
627
|
-
|
|
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
|
+
}
|
|
628
667
|
if (!decision.proceed) continue;
|
|
629
668
|
input = decision.input;
|
|
630
669
|
}
|
|
631
|
-
|
|
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;
|
|
632
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);
|
|
633
684
|
return {
|
|
634
685
|
results,
|
|
635
686
|
needsApproval,
|
|
636
687
|
needsClientExecution,
|
|
637
688
|
inputRequired,
|
|
638
|
-
subagentInterrupts
|
|
689
|
+
subagentInterrupts: toolCalls.flatMap((tc) => interruptsByCall.get(tc.id) ?? [])
|
|
639
690
|
};
|
|
640
691
|
}
|
|
641
692
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-calls.js","names":[],"sources":["../../../../../src/activities/chat/tools/tool-calls.ts"],"sourcesContent":["import { normalizeToolResult } from '../../../utilities/tool-result'\nimport { tanstackMetadata } from '../../../utilities/merge-metadata'\nimport { isProviderExecutedToolCall } from '../../../utilities/provider-executed'\nimport type { AdapterYieldChunk } from '../../../utilities/adapter-yield-chunk'\nimport {\n StandardSchemaValidationError,\n isStandardSchema,\n parseWithStandardSchema,\n validateWithStandardSchema,\n} from './schema-converter'\nimport type { ToolApprovalResolution } from '../../../interrupts'\nimport type {\n AnyTool,\n ContentPart,\n CustomEvent,\n EmitCustomEventOptions,\n Interrupt,\n ModelMessage,\n RunFinishedEvent,\n StreamChunk,\n Tool,\n ToolCall,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolExecutionContext,\n ToolInputResponse,\n ToolResultOutcome,\n ToolOutputState,\n} from '../../../types'\nimport type {\n AfterToolCallInfo,\n BeforeToolCallDecision,\n} from '../middleware/types'\nimport type { McpResourceReadResult } from '../mcp/types'\nimport type {\n ContextFromTool,\n DefinedContext,\n MergeContext,\n UnionToIntersection,\n} from '../runtime-context-types'\n\nfunction safeJsonParse(value: string): unknown {\n try {\n return JSON.parse(value)\n } catch {\n return value\n }\n}\n\n/** Marks the synthetic tool that runs a subagent. */\nexport const SUBAGENT_TOOL = Symbol.for('tanstack.ai.subagentTool')\n\n/** Set on the tool context of a subagent tool. Streams a child chunk live. */\nexport const EMIT_STREAM_CHUNK = Symbol.for('tanstack.ai.emitStreamChunk')\n\n/** What a subagent tool's `execute` returns. */\nexport interface SubagentToolOutcome {\n subagentRunId: string\n text: string\n error?: string\n /** Set when the child stopped for outside input. The tool call stays open. */\n interrupts?: Array<Interrupt>\n}\n\nfunction isSubagentTool(tool: AnyTool): boolean {\n return (tool as { [SUBAGENT_TOOL]?: true })[SUBAGENT_TOOL] === true\n}\n\n/**\n * MCP Apps metadata attached to a server tool at discovery (see\n * `@tanstack/ai-mcp` discovery + `MCPManager.discover()`).\n *\n * - `uiResourceUri` / `serverId` are stamped by ai-mcp at tool discovery.\n * - `readResource` is bound by `MCPManager.discover()` (the one site that has\n * both the tool and its originating source) so the resource can be eagerly\n * read at the emit site. Under `chat()`-managed MCP lifecycle\n * (`connection:'close'`), the MCP source is not disposed until the run\n * drains, so `readResource` is still live at this emit point. Note: a caller\n * who closes the MCP source early (outside `chat()`'s managed lifecycle)\n * degrades fail-soft — `readResource` may reject, the widget is absent, but\n * the tool result still flows to the model.\n * `@tanstack/ai` never imports `@tanstack/ai-mcp`; this travels structurally\n * on the tool.\n */\ninterface McpToolAppMeta {\n uiResourceUri?: string\n serverId?: string\n /** Server-native (unprefixed) MCP tool name — used as the renderer's toolName. */\n serverToolName?: string\n readResource?: (uri: string) => Promise<McpResourceReadResult>\n}\n\nfunction readMcpAppMeta(tool: AnyTool): McpToolAppMeta | undefined {\n const meta = (tool.metadata as { mcp?: McpToolAppMeta } | undefined)?.mcp\n return meta\n}\n\n/**\n * Eagerly read a tool's linked `ui://` resource (MCP Apps) and emit a\n * `ui-resource` CUSTOM event so the client can render the widget. The model\n * still receives the normal text tool-result; the widget rides alongside and\n * never enters model input.\n *\n * Fail-soft: any read error logs a warning and emits nothing — it never throws,\n * so the normal tool-result still flows and a broken widget cannot break the run.\n */\nasync function emitUiResourceIfLinked<TContext>(\n tool: AnyTool,\n context: ToolExecutionContext<TContext>,\n): Promise<void> {\n const mcp = readMcpAppMeta(tool)\n const uiUri = mcp?.uiResourceUri\n if (!uiUri || !mcp.readResource) return\n\n // The try covers ONLY the fallible read — keep `emitCustomEvent` out of it so\n // an exception from the emit path can't be mislabeled as a read failure.\n let matched: McpResourceReadResult['contents'][number] | undefined\n try {\n const res = await mcp.readResource(uiUri)\n // Emit ONLY the content whose uri matches the requested `uiUri`. A source\n // can return unrelated contents; falling back to `contents[0]` would risk\n // rendering a widget that doesn't correspond to the linked resource. This\n // is a display widget — a mismatched resource is worse than none, so if no\n // content matches we fail-soft (warn + return) rather than emit.\n matched = res.contents.find((c) => c.uri === uiUri)\n } catch (err) {\n // fail-soft — the text tool-result already flows; a broken widget must\n // not break the run.\n console.warn(`[mcp-apps] failed to read ui resource ${uiUri}:`, err)\n return\n }\n if (!matched) {\n console.warn(\n `[mcp-apps] ui resource ${uiUri} returned no content matching that uri; not emitting`,\n )\n return\n }\n // NOTE: `toolCallId` is intentionally NOT set here — it is stamped onto\n // every emitted event by the `executeToolCalls` context wrapper, so the\n // UIResourceEvent.value.toolCallId / UIResourcePart.toolCallId contract is\n // still satisfied downstream.\n context.emitCustomEvent('ui-resource', {\n resource: {\n uri: matched.uri,\n mimeType: matched.mimeType ?? 'text/html',\n text: matched.text,\n blob: matched.blob,\n },\n serverId: mcp.serverId,\n toolName: mcp.serverToolName ?? tool.name,\n meta: undefined,\n })\n}\n\n/**\n * Optional middleware hooks for tool execution.\n * When provided, these callbacks are invoked before/after each tool execution.\n */\nexport interface ToolExecutionMiddlewareHooks {\n onBeforeToolCall?: (\n toolCall: ToolCall,\n tool: Tool | undefined,\n args: unknown,\n ) => Promise<BeforeToolCallDecision>\n onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>\n}\n\n/**\n * Error thrown when middleware decides to abort the chat run during tool execution.\n */\nexport class MiddlewareAbortError extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'MiddlewareAbortError'\n }\n}\n\n// The leaf context-inference primitives (ContextFromTool, MergeContext,\n// UnionToIntersection, DefinedContext) are shared with the chat activity\n// options layer — see ../runtime-context-types.\ntype RequiredContextFromToolUnion<T> = T extends unknown\n ? undefined extends ContextFromTool<T>\n ? never\n : ContextFromTool<T>\n : never\n\ntype ContextFromToolUnion<T> = [\n UnionToIntersection<DefinedContext<ContextFromTool<T>>>,\n] extends [never]\n ? unknown\n : [RequiredContextFromToolUnion<T>] extends [never]\n ? UnionToIntersection<DefinedContext<ContextFromTool<T>>> | undefined\n : UnionToIntersection<DefinedContext<ContextFromTool<T>>>\n\ntype ContextFromTools<TTools> = TTools extends readonly [\n infer THead,\n ...infer TTail,\n]\n ? MergeContext<ContextFromTool<THead>, ContextFromTools<TTail>>\n : TTools extends ReadonlyArray<infer TTool>\n ? ContextFromToolUnion<TTool>\n : unknown\n\ntype ExecuteToolsContextArgs<TContext> = undefined extends TContext\n ? [userContext?: TContext]\n : [userContext: TContext]\n\n/**\n * Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.\n *\n * Responsibilities:\n * - Accumulates streaming tool call events (ID, name, arguments)\n * - Validates tool calls (filters out incomplete ones)\n * - Executes tool `execute` functions with parsed arguments\n * - Emits `TOOL_CALL_END` events for client visibility\n * - Returns tool result messages for conversation history\n *\n * This class is used internally by the AI.chat() method to handle the automatic\n * tool execution loop. It can also be used independently for custom tool execution logic.\n *\n * @example\n * ```typescript\n * const manager = new ToolCallManager(tools);\n *\n * // During streaming, accumulate tool calls\n * for await (const chunk of stream) {\n * if (chunk.type === 'TOOL_CALL_START') {\n * manager.addToolCallStartEvent(chunk);\n * } else if (chunk.type === 'TOOL_CALL_ARGS') {\n * manager.addToolCallArgsEvent(chunk);\n * }\n * }\n *\n * // After stream completes, execute tools\n * if (manager.hasToolCalls()) {\n * const toolResults = yield* manager.executeTools(finishEvent);\n * messages = [...messages, ...toolResults];\n * manager.clear();\n * }\n * ```\n */\nexport class ToolCallManager<\n TToolsOrContext = ReadonlyArray<AnyTool>,\n TContext = TToolsOrContext extends ReadonlyArray<AnyTool>\n ? ContextFromTools<TToolsOrContext>\n : TToolsOrContext,\n> {\n private readonly toolCallsMap = new Map<number, ToolCall>()\n private readonly tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>\n\n constructor(\n tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>,\n ) {\n this.tools = tools\n }\n\n /**\n * Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)\n */\n addToolCallStartEvent(event: ToolCallStartEvent): void {\n // AG-UI's TOOL_CALL_START carries no index, and a non-first-party or\n // malformed producer can send a second START for a toolCallId that is\n // already tracked. Without this guard, a repeat with the same index\n // overwrites the slot (wiping any TOOL_CALL_ARGS already accumulated),\n // and a repeat with a missing/different index inserts a duplicate row\n // that getToolCalls() returns twice, running the tool twice.\n for (const toolCall of this.toolCallsMap.values()) {\n if (toolCall.id === event.toolCallId) return\n }\n const index = (event as AdapterYieldChunk).index ?? this.toolCallsMap.size\n const name = event.toolCallName ?? event.toolName\n this.toolCallsMap.set(index, {\n id: event.toolCallId,\n type: 'function',\n function: {\n name,\n arguments: '',\n },\n ...(event.metadata !== undefined && { metadata: event.metadata }),\n })\n }\n\n /**\n * Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)\n */\n addToolCallArgsEvent(event: ToolCallArgsEvent): void {\n const extra = event as AdapterYieldChunk\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n if (typeof extra.args === 'string' && extra.args !== '') {\n toolCall.function.arguments = extra.args\n } else {\n toolCall.function.arguments += event.delta\n }\n break\n }\n }\n }\n\n /**\n * Complete a tool call with its final input\n * Called when TOOL_CALL_END is received\n */\n completeToolCall(event: ToolCallEndEvent): void {\n for (const toolCall of this.toolCallsMap.values()) {\n if (toolCall.id !== event.toolCallId) continue\n if (event.input === undefined) return\n const normalized =\n event.input && typeof event.input === 'object' ? event.input : {}\n toolCall.function.arguments = JSON.stringify(normalized)\n return\n }\n }\n\n /**\n * Check if there are any complete tool calls to execute\n */\n hasToolCalls(): boolean {\n return this.getToolCalls().length > 0\n }\n\n /**\n * Get all complete tool calls (filtered for valid ID and name)\n */\n getToolCalls(): Array<ToolCall> {\n return Array.from(this.toolCallsMap.values()).filter(\n (tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0,\n )\n }\n\n /**\n * Execute all tool calls and return tool result messages\n * Yields TOOL_CALL_END events for streaming\n * @param finishEvent - RUN_FINISHED event from the stream\n */\n async *executeTools(\n finishEvent: RunFinishedEvent,\n ...contextArgs: ExecuteToolsContextArgs<TContext>\n ): AsyncGenerator<AdapterYieldChunk, Array<ModelMessage>, void> {\n const toolCallsArray = this.getToolCalls()\n const toolResults: Array<ModelMessage> = []\n const hasRuntimeContext = contextArgs.length > 0\n const userContext = contextArgs[0]\n\n for (const toolCall of toolCallsArray) {\n const tool = this.tools.find((t) => t.name === toolCall.function.name)\n\n let toolResultContent: string | Array<ContentPart>\n let toolResultState: ToolOutputState | undefined\n // Holds the parsed/validated execution output before serialization.\n // Stays `undefined` when the tool has no `execute` (client-only\n // tools) or when execution throws.\n let toolOutput: unknown\n if (tool?.execute) {\n try {\n // Parse arguments (normalize null/non-object to {} for empty tool_use blocks)\n let args: unknown\n try {\n const argsString = toolCall.function.arguments.trim() || '{}'\n const parsed = JSON.parse(argsString)\n args = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n throw new Error(\n `Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`,\n )\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n args = parseWithStandardSchema(tool.inputSchema, args)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Input validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n // Execute the tool\n const executionContext = {\n toolCallId: toolCall.id,\n context: userContext,\n emitCustomEvent: () => {},\n } as ToolExecutionContext<TContext>\n let result = hasRuntimeContext\n ? await tool.execute(args, executionContext)\n : await tool.execute(args)\n\n // Validate output against outputSchema if provided (for Standard\n // Schema compliant schemas). Unlike the previous implementation we\n // intentionally validate `undefined`/`null` results too, so a tool\n // whose schema forbids them surfaces a validation error instead of\n // silently passing — the schema itself decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n try {\n result = parseWithStandardSchema(tool.outputSchema, result)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Output validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n toolOutput = result\n toolResultContent = normalizeToolResult(result)\n } catch (error: unknown) {\n // If tool execution fails, add error message\n const message =\n error instanceof Error ? error.message : 'Unknown error'\n toolResultContent = `Error executing tool: ${message}`\n toolResultState = 'output-error'\n }\n } else {\n // Tool doesn't have execute function, add placeholder\n toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`\n }\n\n // Emit TOOL_CALL_END event\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.function.name,\n toolName: toolCall.function.name,\n model: (() => {\n const model = tanstackMetadata(finishEvent)?.model\n return typeof model === 'string' ? model : undefined\n })(),\n timestamp: Date.now(),\n // Typed parsed output (undefined for failed exec / client-only tools).\n ...(toolOutput !== undefined ? { output: toolOutput } : {}),\n result: toolResultContent,\n ...(toolResultState !== undefined && { state: toolResultState }),\n }\n\n // Add tool result message\n toolResults.push({\n role: 'tool',\n content: toolResultContent,\n toolCallId: toolCall.id,\n })\n }\n\n return toolResults\n }\n\n /**\n * Clear the tool calls map for the next iteration\n */\n clear(): void {\n this.toolCallsMap.clear()\n }\n}\n\nexport interface ToolResult {\n toolCallId: string\n toolName: string\n result: any\n state?: 'output-available' | 'output-error'\n /** Set when the user or middleware cancelled or denied the tool call; state is output-error. */\n outcome?: ToolResultOutcome\n /** Duration of tool execution in milliseconds (only for server-executed tools) */\n duration?: number\n /**\n * Parsed tool input (after JSON parse + optional Standard Schema validation).\n * Parsed tool input after JSON parse + optional Standard Schema validation.\n */\n input?: unknown\n /**\n * Parsed tool output before wire serialization. Surfaced on engine-emitted\n * `TOOL_CALL_END` events so consumers can read typed `output` without\n * re-parsing `result`. Undefined on error paths and when execution is skipped.\n */\n output?: unknown\n}\n\nexport interface ApprovalRequest {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n}\n\nexport interface ClientToolRequest {\n toolCallId: string\n toolName: string\n input: any\n}\n\n/** Form or sampling input that paused a server tool. */\nexport interface McpInputRequest {\n toolCallId: string\n toolName: string\n kind: 'form' | 'sampling'\n request: unknown\n}\n\ninterface McpInputRequiredThrow {\n name: 'MCPInputRequiredError'\n kind: 'form' | 'sampling'\n request: unknown\n}\n\nfunction isMcpInputRequired(value: unknown): value is McpInputRequiredThrow {\n if (typeof value !== 'object' || value === null) return false\n if (!('name' in value) || value.name !== 'MCPInputRequiredError') {\n return false\n }\n if (!('kind' in value)) return false\n const kindIsFormOrSampling =\n value.kind === 'form' || value.kind === 'sampling'\n if (!kindIsFormOrSampling) return false\n return 'request' in value\n}\n\nexport interface ToolResumeExecutionState {\n clientToolErrors?: ReadonlyMap<string, string>\n deniedToolResults?: ReadonlyMap<string, unknown>\n cancelledToolCallIds?: ReadonlySet<string>\n /** Answers to `mcp_input` interrupts, by tool call id. */\n inputResponses?: ReadonlyMap<string, ToolInputResponse>\n}\n\nfunction approvalResolution(\n approvals: ReadonlyMap<string, ToolApprovalResolution>,\n toolCallId: string,\n): ToolApprovalResolution | undefined {\n return approvals.get(toolCallId) ?? approvals.get(`approval_${toolCallId}`)\n}\n\nfunction isApproved(resolution: ToolApprovalResolution): boolean {\n return typeof resolution === 'boolean' ? resolution : resolution.approved\n}\n\nfunction editedApprovalArgs(\n resolution: ToolApprovalResolution,\n): unknown | undefined {\n return typeof resolution === 'object' && resolution.approved\n ? resolution.editedArgs\n : undefined\n}\n\nfunction deniedApprovalResult(resolution: ToolApprovalResolution): unknown {\n return typeof resolution === 'object' && !resolution.approved\n ? (resolution.payload ?? { error: 'User declined tool execution' })\n : { error: 'User declined tool execution' }\n}\n\ninterface ExecuteToolCallsResult {\n /** Tool results ready to send to LLM */\n results: Array<ToolResult>\n /** Tools that need user approval before execution */\n needsApproval: Array<ApprovalRequest>\n /** Tools that need client-side execution */\n needsClientExecution: Array<ClientToolRequest>\n /** Server tools that paused for MCP form or sampling input */\n inputRequired: Array<McpInputRequest>\n /** Interrupts raised by subagents that run as tools */\n subagentInterrupts: Array<Interrupt>\n}\n\n/**\n * Helper that runs a tool execution promise while polling for pending custom events.\n * Yields any custom events that are emitted during execution, then returns the\n * execution result.\n */\nasync function* executeWithEventPolling<T>(\n executionPromise: Promise<T>,\n pendingEvents: Array<CustomEvent | StreamChunk>,\n): AsyncGenerator<CustomEvent | StreamChunk, T, void> {\n // Use an object to track mutable state across the async boundary\n const state = { done: false, result: undefined as T }\n const executionWithFlag = executionPromise.then((r) => {\n state.done = true\n state.result = r\n return r\n })\n\n while (!state.done) {\n // Wait for either the execution to complete or a short timeout\n await Promise.race([\n executionWithFlag,\n new Promise((resolve) => setTimeout(resolve, 10)),\n ])\n\n // Flush any pending events\n let event: CustomEvent | StreamChunk | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n }\n\n // Final flush in case events were emitted right at completion\n let event: CustomEvent | StreamChunk | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n\n return state.result\n}\n\n/**\n * Apply a middleware onBeforeToolCall decision.\n * Returns the (possibly transformed) input if execution should proceed,\n * or undefined if the tool call was skipped (result already pushed).\n * Throws MiddlewareAbortError if the decision is 'abort'.\n */\nasync function applyBeforeToolCallDecision(\n toolCall: ToolCall,\n tool: Tool,\n input: unknown,\n toolName: string,\n middlewareHooks: ToolExecutionMiddlewareHooks,\n results: Array<ToolResult>,\n): Promise<{ proceed: true; input: unknown } | { proceed: false }> {\n if (!middlewareHooks.onBeforeToolCall) {\n return { proceed: true, input }\n }\n\n const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input)\n if (!decision) {\n return { proceed: true, input }\n }\n\n if (decision.type === 'abort') {\n throw new MiddlewareAbortError(decision.reason || 'Aborted by middleware')\n }\n\n if (decision.type === 'skip') {\n const skipResult = decision.result\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n typeof skipResult === 'string'\n ? safeJsonParse(skipResult)\n : (skipResult ?? null),\n duration: 0,\n })\n if (middlewareHooks.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration: 0,\n result: skipResult,\n })\n }\n return { proceed: false }\n }\n\n return { proceed: true, input: decision.args }\n}\n\n/**\n * Execute a server-side tool with event polling, output validation, and middleware hooks.\n * Yields CustomEvent chunks during execution and pushes the result to the results array.\n */\nexport async function* executeServerTool<TContext = unknown>(\n toolCall: ToolCall,\n tool: AnyTool,\n toolName: string,\n input: unknown,\n context: ToolExecutionContext<TContext>,\n pendingEvents: Array<CustomEvent | StreamChunk>,\n results: Array<ToolResult>,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n inputRequired?: Array<McpInputRequest>,\n subagentInterrupts?: Array<Interrupt>,\n): AsyncGenerator<CustomEvent | StreamChunk, void, void> {\n const startTime = Date.now()\n try {\n if (!tool.execute) {\n throw new Error(`Tool ${toolName} has no execute() implementation`)\n }\n const subagent = isSubagentTool(tool)\n if (subagent) {\n Object.assign(context, {\n [EMIT_STREAM_CHUNK]: (chunk: StreamChunk) => pendingEvents.push(chunk),\n })\n }\n const executionPromise = Promise.resolve(tool.execute(input, context))\n let result = yield* executeWithEventPolling(executionPromise, pendingEvents)\n const duration = Date.now() - startTime\n\n if (subagent) {\n const outcome = result as SubagentToolOutcome\n if (outcome.interrupts?.length) {\n // The child waits for outside input. Leave the call open so the\n // resume run executes it again and continues the same child.\n subagentInterrupts?.push(...outcome.interrupts)\n return\n }\n const modelResult = outcome.error\n ? { subagentRunId: outcome.subagentRunId, error: outcome.error }\n : { subagentRunId: outcome.subagentRunId, result: outcome.text }\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: modelResult,\n input,\n output: modelResult,\n duration,\n ...(outcome.error ? { state: 'output-error' as const } : {}),\n })\n await middlewareHooks?.onAfterToolCall?.({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n duration,\n ...(outcome.error\n ? { ok: false as const, error: new Error(outcome.error) }\n : { ok: true as const, result: modelResult }),\n })\n return\n }\n\n // MCP Apps: if this tool links a ui:// resource, eagerly read it and queue\n // a `ui-resource` CUSTOM event. The MCP source stays live until the run\n // drains (MCPManager's `connection:'close'` policy disposes on completion),\n // so `readResource` is callable here. Fail-soft: a read error warns and\n // emits nothing — the text result still flows.\n await emitUiResourceIfLinked(tool, context)\n\n // Flush remaining events (including any queued ui-resource event)\n let pendingEvent: CustomEvent | StreamChunk | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n // Validate output against outputSchema if provided. Validates\n // `undefined`/`null` too — the schema decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n const finalResult =\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null)\n\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: finalResult,\n input,\n output: finalResult,\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration,\n result: finalResult,\n })\n }\n } catch (error: unknown) {\n const duration = Date.now() - startTime\n\n // Flush remaining events\n let pendingEvent: CustomEvent | StreamChunk | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n if (error instanceof MiddlewareAbortError) {\n throw error\n }\n\n // Same shape as MCPInputRequiredError. Pause instead of a tool error.\n if (isMcpInputRequired(error)) {\n if (!inputRequired) throw error\n inputRequired.push({\n toolCallId: toolCall.id,\n toolName,\n kind: error.kind,\n request: error.request,\n })\n return\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: message },\n input,\n state: 'output-error',\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: false,\n duration,\n error,\n })\n }\n }\n}\n\nasync function buildClientToolResult(\n toolCallId: string,\n toolName: string,\n tool: AnyTool,\n rawResult: unknown,\n input?: unknown,\n errorText?: string,\n): Promise<ToolResult> {\n if (errorText !== undefined) {\n return {\n toolCallId,\n toolName,\n result: { error: errorText },\n input,\n state: 'output-error',\n }\n }\n\n try {\n let result = rawResult\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n const validation = await validateWithStandardSchema<unknown>(\n tool.outputSchema,\n result,\n )\n if (!validation.success) {\n throw new StandardSchemaValidationError(validation.issues)\n }\n result = validation.data\n }\n\n const parsed =\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null)\n return {\n toolCallId,\n toolName,\n result: parsed,\n input,\n output: parsed,\n }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Validation failed'\n return {\n toolCallId,\n toolName,\n result: { error: message },\n input,\n state: 'output-error',\n }\n }\n}\n\n/**\n * Execute tool calls based on their configuration.\n * Yields CustomEvent chunks during tool execution for real-time progress updates.\n *\n * Handles three cases:\n * 1. Client tools (no execute) - request client to execute\n * 2. Server tools with approval - check approval before executing\n * 3. Normal server tools - execute immediately\n *\n * @param toolCalls - Tool calls from the LLM\n * @param tools - Available tools with their configurations\n * @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution\n * @param clientResults - Map of client-side execution results (toolCallId -> result)\n * @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)\n */\nexport async function* executeToolCalls<TContext = unknown>(\n toolCalls: Array<ToolCall>,\n tools: ReadonlyArray<AnyTool>,\n approvals: Map<string, ToolApprovalResolution> = new Map(),\n clientResults: Map<string, any> = new Map(),\n createCustomEventChunk?: (\n eventName: string,\n value: Record<string, any>,\n options?: EmitCustomEventOptions,\n ) => CustomEvent,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n userContext?: TContext,\n abortSignal?: AbortSignal,\n resumeState?: ToolResumeExecutionState,\n): AsyncGenerator<CustomEvent | StreamChunk, ExecuteToolCallsResult, void> {\n const results: Array<ToolResult> = []\n const needsApproval: Array<ApprovalRequest> = []\n const needsClientExecution: Array<ClientToolRequest> = []\n const inputRequired: Array<McpInputRequest> = []\n const subagentInterrupts: Array<Interrupt> = []\n\n // Create tool lookup map\n const toolMap = new Map<string, AnyTool>()\n for (const tool of tools) {\n toolMap.set(tool.name, tool)\n }\n\n // Batch gating: when any tool in the batch still needs an approval decision,\n // defer all execution so side effects don't happen before the user decides.\n const hasPendingApprovals = toolCalls.some((tc) => {\n const t = toolMap.get(tc.function.name)\n return (\n t?.needsApproval &&\n approvalResolution(approvals, tc.id) === undefined &&\n !resumeState?.cancelledToolCallIds?.has(tc.id)\n )\n })\n\n for (const toolCall of toolCalls) {\n // Provider-executed tools (Anthropic web_search / web_fetch) already ran\n // inside the provider response and carry their result on the call's\n // metadata. They have no execute() and must not become client requests.\n if (isProviderExecutedToolCall(toolCall)) continue\n\n const tool = toolMap.get(toolCall.function.name)\n const toolName = toolCall.function.name\n\n if (!tool) {\n // Unknown tool - return error\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: `Unknown tool: ${toolName}` },\n state: 'output-error',\n })\n continue\n }\n\n // Skip non-pending tools while approvals are outstanding\n if (hasPendingApprovals) {\n const isPendingApproval =\n tool.needsApproval &&\n approvalResolution(approvals, toolCall.id) === undefined\n const isPlainClientRequest = !tool.needsApproval && !tool.execute\n if (!isPendingApproval && !isPlainClientRequest) {\n continue\n }\n }\n\n if (resumeState?.cancelledToolCallIds?.has(toolCall.id)) {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'Tool execution cancelled' },\n state: 'output-error',\n outcome: 'cancelled',\n })\n continue\n }\n\n // Parse arguments\n let input: unknown = {}\n const argsStr = toolCall.function.arguments.trim() || '{}'\n if (argsStr) {\n try {\n const parsed = JSON.parse(argsStr)\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n input = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Failed to parse tool arguments as JSON: ${argsStr}`,\n },\n input,\n state: 'output-error',\n })\n continue\n }\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n input = parseWithStandardSchema(tool.inputSchema, input)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Input validation failed for tool ${tool.name}: ${message}`,\n },\n // raw parse may have failed validation — still attach best-effort input\n input,\n state: 'output-error',\n })\n continue\n }\n }\n\n // Create a ToolExecutionContext for this tool call with event emission\n const pendingEvents: Array<CustomEvent | StreamChunk> = []\n const inputResponse = resumeState?.inputResponses?.get(toolCall.id)\n const context = {\n toolCallId: toolCall.id,\n context: userContext,\n abortSignal,\n ...(inputResponse !== undefined ? { inputResponse } : {}),\n emitCustomEvent: (\n eventName: string,\n value: Record<string, any>,\n options?: EmitCustomEventOptions,\n ) => {\n if (createCustomEventChunk) {\n pendingEvents.push(\n createCustomEventChunk(\n eventName,\n {\n ...value,\n toolCallId: toolCall.id,\n },\n options,\n ),\n )\n }\n },\n } as ToolExecutionContext<TContext>\n\n // CASE 1: Client-side tool (no execute function)\n if (!tool.execute) {\n // Check if tool needs approval\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n const resolution = approvalResolution(approvals, toolCall.id)\n\n // Check if approval decision exists\n if (resolution !== undefined) {\n const approved = isApproved(resolution)\n\n if (approved) {\n input = editedApprovalArgs(resolution) ?? input\n // Approved - check if client has executed\n const clientError = resumeState?.clientToolErrors?.get(toolCall.id)\n if (clientResults.has(toolCall.id) || clientError !== undefined) {\n results.push(\n await buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n input,\n clientError,\n ),\n )\n } else {\n // Approved but not executed yet - request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n resumeState?.deniedToolResults?.get(toolCall.id) ??\n deniedApprovalResult(resolution),\n input,\n state: 'output-error',\n outcome: 'denied',\n })\n }\n } else {\n // Need approval first\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n input,\n approvalId,\n })\n }\n } else {\n // No approval needed - check if client has executed\n const clientError = resumeState?.clientToolErrors?.get(toolCall.id)\n if (clientResults.has(toolCall.id) || clientError !== undefined) {\n results.push(\n await buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n input,\n clientError,\n ),\n )\n } else {\n // Request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n }\n continue\n }\n\n // CASE 2: Server tool with approval required\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n const resolution = approvalResolution(approvals, toolCall.id)\n\n // Check if approval decision exists\n if (resolution !== undefined) {\n const approved = isApproved(resolution)\n\n if (approved) {\n input = editedApprovalArgs(resolution) ?? input\n // Apply middleware before-hook for approved tools\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n inputRequired,\n subagentInterrupts,\n )\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n resumeState?.deniedToolResults?.get(toolCall.id) ??\n deniedApprovalResult(resolution),\n input,\n state: 'output-error',\n outcome: 'denied',\n })\n }\n } else {\n // Need approval\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n approvalId,\n })\n }\n continue\n }\n\n // CASE 3: Normal server tool - execute immediately\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n inputRequired,\n subagentInterrupts,\n )\n }\n\n return {\n results,\n needsApproval,\n needsClientExecution,\n inputRequired,\n subagentInterrupts,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-calls.js","names":[],"sources":["../../../../../src/activities/chat/tools/tool-calls.ts"],"sourcesContent":["import { normalizeToolResult } from '../../../utilities/tool-result'\nimport { tanstackMetadata } from '../../../utilities/merge-metadata'\nimport { isProviderExecutedToolCall } from '../../../utilities/provider-executed'\nimport { mergeStreams } from '../../../utilities/merge-streams'\nimport type { AdapterYieldChunk } from '../../../utilities/adapter-yield-chunk'\nimport {\n StandardSchemaValidationError,\n isStandardSchema,\n parseWithStandardSchema,\n validateWithStandardSchema,\n} from './schema-converter'\nimport type { ToolApprovalResolution } from '../../../interrupts'\nimport type {\n AnyTool,\n ContentPart,\n CustomEvent,\n EmitCustomEventOptions,\n Interrupt,\n ModelMessage,\n RunFinishedEvent,\n StreamChunk,\n TextOptions,\n Tool,\n ToolCall,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolExecutionContext,\n ToolInputResponse,\n ToolResultOutcome,\n ToolOutputState,\n} from '../../../types'\nimport type {\n AfterToolCallInfo,\n BeforeToolCallDecision,\n} from '../middleware/types'\nimport type { McpResourceReadResult } from '../mcp/types'\nimport type {\n ContextFromTool,\n DefinedContext,\n MergeContext,\n UnionToIntersection,\n} from '../runtime-context-types'\n\nfunction safeJsonParse(value: string): unknown {\n try {\n return JSON.parse(value)\n } catch {\n return value\n }\n}\n\n/** Marks the synthetic tool that runs a subagent. */\nexport const SUBAGENT_TOOL = Symbol.for('tanstack.ai.subagentTool')\n\n/** Set on the tool context of a subagent tool. Streams a child chunk live. */\nexport const EMIT_STREAM_CHUNK = Symbol.for('tanstack.ai.emitStreamChunk')\n\n/** What a subagent tool's `execute` returns. */\nexport interface SubagentToolOutcome {\n subagentRunId: string\n text: string\n error?: string\n /** Set when the child stopped for outside input. The tool call stays open. */\n interrupts?: Array<Interrupt>\n}\n\nfunction isSubagentTool(tool: AnyTool): boolean {\n return (tool as { [SUBAGENT_TOOL]?: true })[SUBAGENT_TOOL] === true\n}\n\n/**\n * MCP Apps metadata attached to a server tool at discovery (see\n * `@tanstack/ai-mcp` discovery + `MCPManager.discover()`).\n *\n * - `uiResourceUri` / `serverId` are stamped by ai-mcp at tool discovery.\n * - `readResource` is bound by `MCPManager.discover()` (the one site that has\n * both the tool and its originating source) so the resource can be eagerly\n * read at the emit site. Under `chat()`-managed MCP lifecycle\n * (`connection:'close'`), the MCP source is not disposed until the run\n * drains, so `readResource` is still live at this emit point. Note: a caller\n * who closes the MCP source early (outside `chat()`'s managed lifecycle)\n * degrades fail-soft — `readResource` may reject, the widget is absent, but\n * the tool result still flows to the model.\n * `@tanstack/ai` never imports `@tanstack/ai-mcp`; this travels structurally\n * on the tool.\n */\ninterface McpToolAppMeta {\n uiResourceUri?: string\n serverId?: string\n /** Server-native (unprefixed) MCP tool name — used as the renderer's toolName. */\n serverToolName?: string\n readResource?: (uri: string) => Promise<McpResourceReadResult>\n}\n\nfunction readMcpAppMeta(tool: AnyTool): McpToolAppMeta | undefined {\n const meta = (tool.metadata as { mcp?: McpToolAppMeta } | undefined)?.mcp\n return meta\n}\n\n/**\n * Eagerly read a tool's linked `ui://` resource (MCP Apps) and emit a\n * `ui-resource` CUSTOM event so the client can render the widget. The model\n * still receives the normal text tool-result; the widget rides alongside and\n * never enters model input.\n *\n * Fail-soft: any read error logs a warning and emits nothing — it never throws,\n * so the normal tool-result still flows and a broken widget cannot break the run.\n */\nasync function emitUiResourceIfLinked<TContext>(\n tool: AnyTool,\n context: ToolExecutionContext<TContext>,\n): Promise<void> {\n const mcp = readMcpAppMeta(tool)\n const uiUri = mcp?.uiResourceUri\n if (!uiUri || !mcp.readResource) return\n\n // The try covers ONLY the fallible read — keep `emitCustomEvent` out of it so\n // an exception from the emit path can't be mislabeled as a read failure.\n let matched: McpResourceReadResult['contents'][number] | undefined\n try {\n const res = await mcp.readResource(uiUri)\n // Emit ONLY the content whose uri matches the requested `uiUri`. A source\n // can return unrelated contents; falling back to `contents[0]` would risk\n // rendering a widget that doesn't correspond to the linked resource. This\n // is a display widget — a mismatched resource is worse than none, so if no\n // content matches we fail-soft (warn + return) rather than emit.\n matched = res.contents.find((c) => c.uri === uiUri)\n } catch (err) {\n // fail-soft — the text tool-result already flows; a broken widget must\n // not break the run.\n console.warn(`[mcp-apps] failed to read ui resource ${uiUri}:`, err)\n return\n }\n if (!matched) {\n console.warn(\n `[mcp-apps] ui resource ${uiUri} returned no content matching that uri; not emitting`,\n )\n return\n }\n // NOTE: `toolCallId` is intentionally NOT set here — it is stamped onto\n // every emitted event by the `executeToolCalls` context wrapper, so the\n // UIResourceEvent.value.toolCallId / UIResourcePart.toolCallId contract is\n // still satisfied downstream.\n context.emitCustomEvent('ui-resource', {\n resource: {\n uri: matched.uri,\n mimeType: matched.mimeType ?? 'text/html',\n text: matched.text,\n blob: matched.blob,\n },\n serverId: mcp.serverId,\n toolName: mcp.serverToolName ?? tool.name,\n meta: undefined,\n })\n}\n\n/**\n * Optional middleware hooks for tool execution.\n * When provided, these callbacks are invoked before/after each tool execution.\n */\nexport interface ToolExecutionMiddlewareHooks {\n onBeforeToolCall?: (\n toolCall: ToolCall,\n tool: Tool | undefined,\n args: unknown,\n ) => Promise<BeforeToolCallDecision>\n onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>\n}\n\n/**\n * Error thrown when middleware decides to abort the chat run during tool execution.\n */\nexport class MiddlewareAbortError extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'MiddlewareAbortError'\n }\n}\n\n// The leaf context-inference primitives (ContextFromTool, MergeContext,\n// UnionToIntersection, DefinedContext) are shared with the chat activity\n// options layer — see ../runtime-context-types.\ntype RequiredContextFromToolUnion<T> = T extends unknown\n ? undefined extends ContextFromTool<T>\n ? never\n : ContextFromTool<T>\n : never\n\ntype ContextFromToolUnion<T> = [\n UnionToIntersection<DefinedContext<ContextFromTool<T>>>,\n] extends [never]\n ? unknown\n : [RequiredContextFromToolUnion<T>] extends [never]\n ? UnionToIntersection<DefinedContext<ContextFromTool<T>>> | undefined\n : UnionToIntersection<DefinedContext<ContextFromTool<T>>>\n\ntype ContextFromTools<TTools> = TTools extends readonly [\n infer THead,\n ...infer TTail,\n]\n ? MergeContext<ContextFromTool<THead>, ContextFromTools<TTail>>\n : TTools extends ReadonlyArray<infer TTool>\n ? ContextFromToolUnion<TTool>\n : unknown\n\ntype ExecuteToolsContextArgs<TContext> = undefined extends TContext\n ? [userContext?: TContext]\n : [userContext: TContext]\n\n/**\n * Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.\n *\n * Responsibilities:\n * - Accumulates streaming tool call events (ID, name, arguments)\n * - Validates tool calls (filters out incomplete ones)\n * - Executes tool `execute` functions with parsed arguments\n * - Emits `TOOL_CALL_END` events for client visibility\n * - Returns tool result messages for conversation history\n *\n * This class is used internally by the AI.chat() method to handle the automatic\n * tool execution loop. It can also be used independently for custom tool execution logic.\n *\n * @example\n * ```typescript\n * const manager = new ToolCallManager(tools);\n *\n * // During streaming, accumulate tool calls\n * for await (const chunk of stream) {\n * if (chunk.type === 'TOOL_CALL_START') {\n * manager.addToolCallStartEvent(chunk);\n * } else if (chunk.type === 'TOOL_CALL_ARGS') {\n * manager.addToolCallArgsEvent(chunk);\n * }\n * }\n *\n * // After stream completes, execute tools\n * if (manager.hasToolCalls()) {\n * const toolResults = yield* manager.executeTools(finishEvent);\n * messages = [...messages, ...toolResults];\n * manager.clear();\n * }\n * ```\n */\nexport class ToolCallManager<\n TToolsOrContext = ReadonlyArray<AnyTool>,\n TContext = TToolsOrContext extends ReadonlyArray<AnyTool>\n ? ContextFromTools<TToolsOrContext>\n : TToolsOrContext,\n> {\n private readonly toolCallsMap = new Map<number, ToolCall>()\n private readonly tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>\n\n constructor(\n tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>,\n ) {\n this.tools = tools\n }\n\n /**\n * Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)\n */\n addToolCallStartEvent(event: ToolCallStartEvent): void {\n // AG-UI's TOOL_CALL_START carries no index, and a non-first-party or\n // malformed producer can send a second START for a toolCallId that is\n // already tracked. Without this guard, a repeat with the same index\n // overwrites the slot (wiping any TOOL_CALL_ARGS already accumulated),\n // and a repeat with a missing/different index inserts a duplicate row\n // that getToolCalls() returns twice, running the tool twice.\n for (const toolCall of this.toolCallsMap.values()) {\n if (toolCall.id === event.toolCallId) return\n }\n const index = (event as AdapterYieldChunk).index ?? this.toolCallsMap.size\n const name = event.toolCallName ?? event.toolName\n this.toolCallsMap.set(index, {\n id: event.toolCallId,\n type: 'function',\n function: {\n name,\n arguments: '',\n },\n ...(event.metadata !== undefined && { metadata: event.metadata }),\n })\n }\n\n /**\n * Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)\n */\n addToolCallArgsEvent(event: ToolCallArgsEvent): void {\n const extra = event as AdapterYieldChunk\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n if (typeof extra.args === 'string' && extra.args !== '') {\n toolCall.function.arguments = extra.args\n } else {\n toolCall.function.arguments += event.delta\n }\n break\n }\n }\n }\n\n /**\n * Complete a tool call with its final input\n * Called when TOOL_CALL_END is received\n */\n completeToolCall(event: ToolCallEndEvent): void {\n for (const toolCall of this.toolCallsMap.values()) {\n if (toolCall.id !== event.toolCallId) continue\n if (event.input === undefined) return\n const normalized =\n event.input && typeof event.input === 'object' ? event.input : {}\n toolCall.function.arguments = JSON.stringify(normalized)\n return\n }\n }\n\n /**\n * Check if there are any complete tool calls to execute\n */\n hasToolCalls(): boolean {\n return this.getToolCalls().length > 0\n }\n\n /**\n * Get all complete tool calls (filtered for valid ID and name)\n */\n getToolCalls(): Array<ToolCall> {\n return Array.from(this.toolCallsMap.values()).filter(\n (tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0,\n )\n }\n\n /**\n * Execute all tool calls and return tool result messages\n * Yields TOOL_CALL_END events for streaming\n * @param finishEvent - RUN_FINISHED event from the stream\n */\n async *executeTools(\n finishEvent: RunFinishedEvent,\n ...contextArgs: ExecuteToolsContextArgs<TContext>\n ): AsyncGenerator<AdapterYieldChunk, Array<ModelMessage>, void> {\n const toolCallsArray = this.getToolCalls()\n const toolResults: Array<ModelMessage> = []\n const hasRuntimeContext = contextArgs.length > 0\n const userContext = contextArgs[0]\n\n for (const toolCall of toolCallsArray) {\n const tool = this.tools.find((t) => t.name === toolCall.function.name)\n\n let toolResultContent: string | Array<ContentPart>\n let toolResultState: ToolOutputState | undefined\n // Holds the parsed/validated execution output before serialization.\n // Stays `undefined` when the tool has no `execute` (client-only\n // tools) or when execution throws.\n let toolOutput: unknown\n if (tool?.execute) {\n try {\n // Parse arguments (normalize null/non-object to {} for empty tool_use blocks)\n let args: unknown\n try {\n const argsString = toolCall.function.arguments.trim() || '{}'\n const parsed = JSON.parse(argsString)\n args = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n throw new Error(\n `Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`,\n )\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n args = parseWithStandardSchema(tool.inputSchema, args)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Input validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n // Execute the tool\n const executionContext = {\n toolCallId: toolCall.id,\n context: userContext,\n emitCustomEvent: () => {},\n } as ToolExecutionContext<TContext>\n let result = hasRuntimeContext\n ? await tool.execute(args, executionContext)\n : await tool.execute(args)\n\n // Validate output against outputSchema if provided (for Standard\n // Schema compliant schemas). Unlike the previous implementation we\n // intentionally validate `undefined`/`null` results too, so a tool\n // whose schema forbids them surfaces a validation error instead of\n // silently passing — the schema itself decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n try {\n result = parseWithStandardSchema(tool.outputSchema, result)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Output validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n toolOutput = result\n toolResultContent = normalizeToolResult(result)\n } catch (error: unknown) {\n // If tool execution fails, add error message\n const message =\n error instanceof Error ? error.message : 'Unknown error'\n toolResultContent = `Error executing tool: ${message}`\n toolResultState = 'output-error'\n }\n } else {\n // Tool doesn't have execute function, add placeholder\n toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`\n }\n\n // Emit TOOL_CALL_END event\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.function.name,\n toolName: toolCall.function.name,\n model: (() => {\n const model = tanstackMetadata(finishEvent)?.model\n return typeof model === 'string' ? model : undefined\n })(),\n timestamp: Date.now(),\n // Typed parsed output (undefined for failed exec / client-only tools).\n ...(toolOutput !== undefined ? { output: toolOutput } : {}),\n result: toolResultContent,\n ...(toolResultState !== undefined && { state: toolResultState }),\n }\n\n // Add tool result message\n toolResults.push({\n role: 'tool',\n content: toolResultContent,\n toolCallId: toolCall.id,\n })\n }\n\n return toolResults\n }\n\n /**\n * Clear the tool calls map for the next iteration\n */\n clear(): void {\n this.toolCallsMap.clear()\n }\n}\n\nexport interface ToolResult {\n toolCallId: string\n toolName: string\n result: any\n state?: 'output-available' | 'output-error'\n /** Set when the user or middleware cancelled or denied the tool call; state is output-error. */\n outcome?: ToolResultOutcome\n /** Duration of tool execution in milliseconds (only for server-executed tools) */\n duration?: number\n /**\n * Parsed tool input (after JSON parse + optional Standard Schema validation).\n * Parsed tool input after JSON parse + optional Standard Schema validation.\n */\n input?: unknown\n /**\n * Parsed tool output before wire serialization. Surfaced on engine-emitted\n * `TOOL_CALL_END` events so consumers can read typed `output` without\n * re-parsing `result`. Undefined on error paths and when execution is skipped.\n */\n output?: unknown\n}\n\nexport interface ApprovalRequest {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n}\n\nexport interface ClientToolRequest {\n toolCallId: string\n toolName: string\n input: any\n}\n\n/** Form or sampling input that paused a server tool. */\nexport interface McpInputRequest {\n toolCallId: string\n toolName: string\n kind: 'form' | 'sampling'\n request: unknown\n}\n\ninterface McpInputRequiredThrow {\n name: 'MCPInputRequiredError'\n kind: 'form' | 'sampling'\n request: unknown\n}\n\nfunction isMcpInputRequired(value: unknown): value is McpInputRequiredThrow {\n if (typeof value !== 'object' || value === null) return false\n if (!('name' in value) || value.name !== 'MCPInputRequiredError') {\n return false\n }\n if (!('kind' in value)) return false\n const kindIsFormOrSampling =\n value.kind === 'form' || value.kind === 'sampling'\n if (!kindIsFormOrSampling) return false\n return 'request' in value\n}\n\nexport interface ToolResumeExecutionState {\n clientToolErrors?: ReadonlyMap<string, string>\n deniedToolResults?: ReadonlyMap<string, unknown>\n cancelledToolCallIds?: ReadonlySet<string>\n /** Answers to `mcp_input` interrupts, by tool call id. */\n inputResponses?: ReadonlyMap<string, ToolInputResponse>\n}\n\nfunction approvalResolution(\n approvals: ReadonlyMap<string, ToolApprovalResolution>,\n toolCallId: string,\n): ToolApprovalResolution | undefined {\n return approvals.get(toolCallId) ?? approvals.get(`approval_${toolCallId}`)\n}\n\nfunction isApproved(resolution: ToolApprovalResolution): boolean {\n return typeof resolution === 'boolean' ? resolution : resolution.approved\n}\n\nfunction editedApprovalArgs(\n resolution: ToolApprovalResolution,\n): unknown | undefined {\n return typeof resolution === 'object' && resolution.approved\n ? resolution.editedArgs\n : undefined\n}\n\nfunction deniedApprovalResult(resolution: ToolApprovalResolution): unknown {\n return typeof resolution === 'object' && !resolution.approved\n ? (resolution.payload ?? { error: 'User declined tool execution' })\n : { error: 'User declined tool execution' }\n}\n\ninterface ExecuteToolCallsResult {\n /** Tool results ready to send to LLM */\n results: Array<ToolResult>\n /** Tools that need user approval before execution */\n needsApproval: Array<ApprovalRequest>\n /** Tools that need client-side execution */\n needsClientExecution: Array<ClientToolRequest>\n /** Server tools that paused for MCP form or sampling input */\n inputRequired: Array<McpInputRequest>\n /** Interrupts raised by subagents that run as tools */\n subagentInterrupts: Array<Interrupt>\n}\n\n/**\n * Helper that runs a tool execution promise while polling for pending custom events.\n * Yields any custom events that are emitted during execution, then returns the\n * execution result.\n */\nasync function* executeWithEventPolling<T>(\n executionPromise: Promise<T>,\n pendingEvents: Array<CustomEvent | StreamChunk>,\n): AsyncGenerator<CustomEvent | StreamChunk, T, void> {\n // Use an object to track mutable state across the async boundary\n const state = { done: false, result: undefined as T }\n const executionWithFlag = executionPromise.then((r) => {\n state.done = true\n state.result = r\n return r\n })\n\n while (!state.done) {\n // Wait for either the execution to complete or a short timeout\n await Promise.race([\n executionWithFlag,\n new Promise((resolve) => setTimeout(resolve, 10)),\n ])\n\n // Flush any pending events\n let event: CustomEvent | StreamChunk | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n }\n\n // Final flush in case events were emitted right at completion\n let event: CustomEvent | StreamChunk | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n\n return state.result\n}\n\n/**\n * Apply a middleware onBeforeToolCall decision.\n * Returns the (possibly transformed) input if execution should proceed,\n * or undefined if the tool call was skipped (result already pushed).\n * Throws MiddlewareAbortError if the decision is 'abort'.\n */\nasync function applyBeforeToolCallDecision(\n toolCall: ToolCall,\n tool: Tool,\n input: unknown,\n toolName: string,\n middlewareHooks: ToolExecutionMiddlewareHooks,\n results: Array<ToolResult>,\n): Promise<{ proceed: true; input: unknown } | { proceed: false }> {\n if (!middlewareHooks.onBeforeToolCall) {\n return { proceed: true, input }\n }\n\n const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input)\n if (!decision) {\n return { proceed: true, input }\n }\n\n if (decision.type === 'abort') {\n throw new MiddlewareAbortError(decision.reason || 'Aborted by middleware')\n }\n\n if (decision.type === 'skip') {\n const skipResult = decision.result\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n typeof skipResult === 'string'\n ? safeJsonParse(skipResult)\n : (skipResult ?? null),\n duration: 0,\n })\n if (middlewareHooks.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration: 0,\n result: skipResult,\n })\n }\n return { proceed: false }\n }\n\n return { proceed: true, input: decision.args }\n}\n\n/**\n * Execute a server-side tool with event polling, output validation, and middleware hooks.\n * Yields CustomEvent chunks during execution and pushes the result to the results array.\n */\nexport async function* executeServerTool<TContext = unknown>(\n toolCall: ToolCall,\n tool: AnyTool,\n toolName: string,\n input: unknown,\n context: ToolExecutionContext<TContext>,\n pendingEvents: Array<CustomEvent | StreamChunk>,\n results: Array<ToolResult>,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n inputRequired?: Array<McpInputRequest>,\n subagentInterrupts?: Array<Interrupt>,\n): AsyncGenerator<CustomEvent | StreamChunk, void, void> {\n const startTime = Date.now()\n try {\n if (!tool.execute) {\n throw new Error(`Tool ${toolName} has no execute() implementation`)\n }\n const subagent = isSubagentTool(tool)\n if (subagent) {\n Object.assign(context, {\n [EMIT_STREAM_CHUNK]: (chunk: StreamChunk) => pendingEvents.push(chunk),\n })\n }\n const executionPromise = Promise.resolve(tool.execute(input, context))\n let result = yield* executeWithEventPolling(executionPromise, pendingEvents)\n const duration = Date.now() - startTime\n\n if (subagent) {\n const outcome = result as SubagentToolOutcome\n if (outcome.interrupts?.length) {\n // The child waits for outside input. Leave the call open so the\n // resume run executes it again and continues the same child.\n subagentInterrupts?.push(...outcome.interrupts)\n return\n }\n const modelResult = outcome.error\n ? { subagentRunId: outcome.subagentRunId, error: outcome.error }\n : { subagentRunId: outcome.subagentRunId, result: outcome.text }\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: modelResult,\n input,\n output: modelResult,\n duration,\n ...(outcome.error ? { state: 'output-error' as const } : {}),\n })\n await middlewareHooks?.onAfterToolCall?.({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n duration,\n ...(outcome.error\n ? { ok: false as const, error: new Error(outcome.error) }\n : { ok: true as const, result: modelResult }),\n })\n return\n }\n\n // MCP Apps: if this tool links a ui:// resource, eagerly read it and queue\n // a `ui-resource` CUSTOM event. The MCP source stays live until the run\n // drains (MCPManager's `connection:'close'` policy disposes on completion),\n // so `readResource` is callable here. Fail-soft: a read error warns and\n // emits nothing — the text result still flows.\n await emitUiResourceIfLinked(tool, context)\n\n // Flush remaining events (including any queued ui-resource event)\n let pendingEvent: CustomEvent | StreamChunk | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n // Validate output against outputSchema if provided. Validates\n // `undefined`/`null` too — the schema decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n const finalResult =\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null)\n\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: finalResult,\n input,\n output: finalResult,\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration,\n result: finalResult,\n })\n }\n } catch (error: unknown) {\n const duration = Date.now() - startTime\n\n // Flush remaining events\n let pendingEvent: CustomEvent | StreamChunk | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n if (error instanceof MiddlewareAbortError) {\n throw error\n }\n\n // Same shape as MCPInputRequiredError. Pause instead of a tool error.\n if (isMcpInputRequired(error)) {\n if (!inputRequired) throw error\n inputRequired.push({\n toolCallId: toolCall.id,\n toolName,\n kind: error.kind,\n request: error.request,\n })\n return\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: message },\n input,\n state: 'output-error',\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: false,\n duration,\n error,\n })\n }\n }\n}\n\nasync function buildClientToolResult(\n toolCallId: string,\n toolName: string,\n tool: AnyTool,\n rawResult: unknown,\n input?: unknown,\n errorText?: string,\n): Promise<ToolResult> {\n if (errorText !== undefined) {\n return {\n toolCallId,\n toolName,\n result: { error: errorText },\n input,\n state: 'output-error',\n }\n }\n\n try {\n let result = rawResult\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n const validation = await validateWithStandardSchema<unknown>(\n tool.outputSchema,\n result,\n )\n if (!validation.success) {\n throw new StandardSchemaValidationError(validation.issues)\n }\n result = validation.data\n }\n\n const parsed =\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null)\n return {\n toolCallId,\n toolName,\n result: parsed,\n input,\n output: parsed,\n }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Validation failed'\n return {\n toolCallId,\n toolName,\n result: { error: message },\n input,\n state: 'output-error',\n }\n }\n}\n\n/**\n * Execute tool calls based on their configuration.\n * Yields CustomEvent chunks during tool execution for real-time progress updates.\n *\n * Handles three cases:\n * 1. Client tools (no execute) - request client to execute\n * 2. Server tools with approval - check approval before executing\n * 3. Normal server tools - execute immediately\n *\n * @param toolCalls - Tool calls from the LLM\n * @param tools - Available tools with their configurations\n * @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution\n * @param clientResults - Map of client-side execution results (toolCallId -> result)\n * @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)\n * @param toolExecution - `'parallel'` (default) prepares every call in call\n * order, then starts the server tools together. `'sequential'` runs one\n * call at a time. Results come back in call order either way.\n */\nexport async function* executeToolCalls<TContext = unknown>(\n toolCalls: Array<ToolCall>,\n tools: ReadonlyArray<AnyTool>,\n approvals: Map<string, ToolApprovalResolution> = new Map(),\n clientResults: Map<string, any> = new Map(),\n createCustomEventChunk?: (\n eventName: string,\n value: Record<string, any>,\n options?: EmitCustomEventOptions,\n ) => CustomEvent,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n userContext?: TContext,\n abortSignal?: AbortSignal,\n resumeState?: ToolResumeExecutionState,\n toolExecution: NonNullable<TextOptions['toolExecution']> = 'parallel',\n): AsyncGenerator<CustomEvent | StreamChunk, ExecuteToolCallsResult, void> {\n const results: Array<ToolResult> = []\n const needsApproval: Array<ApprovalRequest> = []\n const needsClientExecution: Array<ClientToolRequest> = []\n const inputRequired: Array<McpInputRequest> = []\n\n // Create tool lookup map\n const toolMap = new Map<string, AnyTool>()\n for (const tool of tools) {\n toolMap.set(tool.name, tool)\n }\n\n const runsInOrder = toolExecution === 'sequential'\n // Parallel mode collects the server runs here. `mergeStreams` starts them\n // together after the loop.\n const runs: Array<AsyncGenerator<CustomEvent | StreamChunk, void, void>> = []\n // Errors thrown by a run or a before-hook. They are rethrown only after every\n // started tool has finished, so no tool outlives the batch.\n const failures: Array<unknown> = []\n // Each call's subagent interrupts, so they come back in call order.\n const interruptsByCall = new Map<string, Array<Interrupt>>()\n\n // A tool that has not started when the run aborts never starts. It gets an\n // error result, so every call of the batch still has a result.\n async function* runServerTool(\n toolCall: ToolCall,\n tool: AnyTool,\n toolName: string,\n input: unknown,\n context: ToolExecutionContext<TContext>,\n pendingEvents: Array<CustomEvent | StreamChunk>,\n ): AsyncGenerator<CustomEvent | StreamChunk, void, void> {\n try {\n if (abortSignal?.aborted) {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'Operation aborted' },\n input,\n state: 'output-error',\n duration: 0,\n })\n await middlewareHooks?.onAfterToolCall?.({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: false,\n duration: 0,\n error: new Error('Operation aborted'),\n })\n return\n }\n const interrupts: Array<Interrupt> = []\n interruptsByCall.set(toolCall.id, interrupts)\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n inputRequired,\n interrupts,\n )\n } catch (error) {\n failures.push(error)\n }\n }\n\n // Batch gating: when any tool in the batch still needs an approval decision,\n // defer all execution so side effects don't happen before the user decides.\n const hasPendingApprovals = toolCalls.some((tc) => {\n const t = toolMap.get(tc.function.name)\n return (\n t?.needsApproval &&\n approvalResolution(approvals, tc.id) === undefined &&\n !resumeState?.cancelledToolCallIds?.has(tc.id)\n )\n })\n\n for (const toolCall of toolCalls) {\n // Provider-executed tools (Anthropic web_search / web_fetch) already ran\n // inside the provider response and carry their result on the call's\n // metadata. They have no execute() and must not become client requests.\n if (isProviderExecutedToolCall(toolCall)) continue\n\n const tool = toolMap.get(toolCall.function.name)\n const toolName = toolCall.function.name\n\n if (!tool) {\n // Unknown tool - return error\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: `Unknown tool: ${toolName}` },\n state: 'output-error',\n })\n continue\n }\n\n // Skip non-pending tools while approvals are outstanding\n if (hasPendingApprovals) {\n const isPendingApproval =\n tool.needsApproval &&\n approvalResolution(approvals, toolCall.id) === undefined\n const isPlainClientRequest = !tool.needsApproval && !tool.execute\n if (!isPendingApproval && !isPlainClientRequest) {\n continue\n }\n }\n\n if (resumeState?.cancelledToolCallIds?.has(toolCall.id)) {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'Tool execution cancelled' },\n state: 'output-error',\n outcome: 'cancelled',\n })\n continue\n }\n\n // Parse arguments\n let input: unknown = {}\n const argsStr = toolCall.function.arguments.trim() || '{}'\n if (argsStr) {\n try {\n const parsed = JSON.parse(argsStr)\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n input = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Failed to parse tool arguments as JSON: ${argsStr}`,\n },\n input,\n state: 'output-error',\n })\n continue\n }\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n input = parseWithStandardSchema(tool.inputSchema, input)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Input validation failed for tool ${tool.name}: ${message}`,\n },\n // raw parse may have failed validation — still attach best-effort input\n input,\n state: 'output-error',\n })\n continue\n }\n }\n\n // Create a ToolExecutionContext for this tool call with event emission\n const pendingEvents: Array<CustomEvent | StreamChunk> = []\n const inputResponse = resumeState?.inputResponses?.get(toolCall.id)\n const context = {\n toolCallId: toolCall.id,\n context: userContext,\n abortSignal,\n ...(inputResponse !== undefined ? { inputResponse } : {}),\n emitCustomEvent: (\n eventName: string,\n value: Record<string, any>,\n options?: EmitCustomEventOptions,\n ) => {\n if (createCustomEventChunk) {\n pendingEvents.push(\n createCustomEventChunk(\n eventName,\n {\n ...value,\n toolCallId: toolCall.id,\n },\n options,\n ),\n )\n }\n },\n } as ToolExecutionContext<TContext>\n\n // CASE 1: Client-side tool (no execute function)\n if (!tool.execute) {\n // Check if tool needs approval\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n const resolution = approvalResolution(approvals, toolCall.id)\n\n // Check if approval decision exists\n if (resolution !== undefined) {\n const approved = isApproved(resolution)\n\n if (approved) {\n input = editedApprovalArgs(resolution) ?? input\n // Approved - check if client has executed\n const clientError = resumeState?.clientToolErrors?.get(toolCall.id)\n if (clientResults.has(toolCall.id) || clientError !== undefined) {\n results.push(\n await buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n input,\n clientError,\n ),\n )\n } else {\n // Approved but not executed yet - request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n resumeState?.deniedToolResults?.get(toolCall.id) ??\n deniedApprovalResult(resolution),\n input,\n state: 'output-error',\n outcome: 'denied',\n })\n }\n } else {\n // Need approval first\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n input,\n approvalId,\n })\n }\n } else {\n // No approval needed - check if client has executed\n const clientError = resumeState?.clientToolErrors?.get(toolCall.id)\n if (clientResults.has(toolCall.id) || clientError !== undefined) {\n results.push(\n await buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n input,\n clientError,\n ),\n )\n } else {\n // Request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n }\n continue\n }\n\n // CASE 2: Server tool with approval required\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n const resolution = approvalResolution(approvals, toolCall.id)\n\n if (resolution === undefined) {\n // Need approval\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n approvalId,\n })\n continue\n }\n if (!isApproved(resolution)) {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n resumeState?.deniedToolResults?.get(toolCall.id) ??\n deniedApprovalResult(resolution),\n input,\n state: 'output-error',\n outcome: 'denied',\n })\n continue\n }\n // Approved: run it like any other server tool below.\n input = editedApprovalArgs(resolution) ?? input\n }\n\n // CASE 3: Server tool, approved or with no approval\n if (middlewareHooks) {\n let decision: Awaited<ReturnType<typeof applyBeforeToolCallDecision>>\n try {\n decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n } catch (error) {\n // The calls prepared so far still run, as they would one at a time.\n failures.push(error)\n break\n }\n if (!decision.proceed) continue\n input = decision.input\n }\n\n const run = runServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n )\n if (!runsInOrder) {\n runs.push(run)\n continue\n }\n yield* run\n if (failures.length > 0) break\n }\n\n yield* mergeStreams(runs)\n if (failures.length > 0) throw failures[0]\n\n // Parallel tools finish in any order. The model gets the results, input\n // requests, and interrupts in the order it made the calls.\n const callOrder = new Map(toolCalls.map((tc, index) => [tc.id, index]))\n const byCallOrder = (a: { toolCallId: string }, b: { toolCallId: string }) =>\n (callOrder.get(a.toolCallId) ?? 0) - (callOrder.get(b.toolCallId) ?? 0)\n results.sort(byCallOrder)\n inputRequired.sort(byCallOrder)\n\n return {\n results,\n needsApproval,\n needsClientExecution,\n inputRequired,\n subagentInterrupts: toolCalls.flatMap(\n (tc) => interruptsByCall.get(tc.id) ?? [],\n ),\n }\n}\n"],"mappings":"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"}
|
|
@@ -152,7 +152,7 @@ export interface ListVoicesActivityOptions<TAdapter extends TTSAdapter<string, T
|
|
|
152
152
|
* import { elevenlabsSpeech } from '@tanstack/ai-elevenlabs'
|
|
153
153
|
*
|
|
154
154
|
* const { voices } = await listVoices({
|
|
155
|
-
* adapter: elevenlabsSpeech('
|
|
155
|
+
* adapter: elevenlabsSpeech('eleven_v4'),
|
|
156
156
|
* origins: ['generated', 'cloned'],
|
|
157
157
|
* })
|
|
158
158
|
* ```
|
|
@@ -208,7 +208,7 @@ async function runGenerateSpeech(options) {
|
|
|
208
208
|
* import { elevenlabsSpeech } from '@tanstack/ai-elevenlabs'
|
|
209
209
|
*
|
|
210
210
|
* const { voices } = await listVoices({
|
|
211
|
-
* adapter: elevenlabsSpeech('
|
|
211
|
+
* adapter: elevenlabsSpeech('eleven_v4'),
|
|
212
212
|
* origins: ['generated', 'cloned'],
|
|
213
213
|
* })
|
|
214
214
|
* ```
|