@tanstack/ai 0.23.1 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/esm/activities/chat/adapter.d.ts +3 -1
  2. package/dist/esm/activities/chat/adapter.js.map +1 -1
  3. package/dist/esm/activities/chat/index.d.ts +33 -9
  4. package/dist/esm/activities/chat/index.js +19 -9
  5. package/dist/esm/activities/chat/index.js.map +1 -1
  6. package/dist/esm/activities/chat/messages.js +2 -1
  7. package/dist/esm/activities/chat/messages.js.map +1 -1
  8. package/dist/esm/activities/chat/middleware/compose.d.ts +14 -14
  9. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  10. package/dist/esm/activities/chat/middleware/types.d.ts +21 -21
  11. package/dist/esm/activities/chat/runtime-context-types.d.ts +43 -0
  12. package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
  13. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  14. package/dist/esm/activities/chat/stream/processor.d.ts +1 -0
  15. package/dist/esm/activities/chat/stream/processor.js +35 -12
  16. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  17. package/dist/esm/activities/chat/tools/tool-calls.d.ts +15 -5
  18. package/dist/esm/activities/chat/tools/tool-calls.js +59 -19
  19. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  20. package/dist/esm/activities/chat/tools/tool-definition.d.ts +12 -8
  21. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  22. package/dist/esm/activities/error-payload.d.ts +26 -0
  23. package/dist/esm/activities/error-payload.js +12 -1
  24. package/dist/esm/activities/error-payload.js.map +1 -1
  25. package/dist/esm/activities/generateAudio/index.js +9 -0
  26. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  27. package/dist/esm/activities/generateSpeech/index.js +9 -0
  28. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  29. package/dist/esm/adapter-internals.d.ts +1 -1
  30. package/dist/esm/adapter-internals.js +3 -2
  31. package/dist/esm/client.d.ts +1 -1
  32. package/dist/esm/client.js +3 -1
  33. package/dist/esm/client.js.map +1 -1
  34. package/dist/esm/index.d.ts +3 -1
  35. package/dist/esm/index.js +9 -1
  36. package/dist/esm/index.js.map +1 -1
  37. package/dist/esm/tool-registry.d.ts +7 -7
  38. package/dist/esm/tool-registry.js +1 -1
  39. package/dist/esm/tool-registry.js.map +1 -1
  40. package/dist/esm/types.d.ts +56 -59
  41. package/dist/esm/utilities/ag-ui-wire.js +1 -1
  42. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  43. package/dist/esm/utilities/chat-params.d.ts +8 -3
  44. package/dist/esm/utilities/chat-params.js +6 -2
  45. package/dist/esm/utilities/chat-params.js.map +1 -1
  46. package/dist/esm/utilities/tool-result.d.ts +21 -0
  47. package/dist/esm/utilities/tool-result.js +37 -0
  48. package/dist/esm/utilities/tool-result.js.map +1 -0
  49. package/dist/esm/utilities/usage.d.ts +31 -0
  50. package/dist/esm/utilities/usage.js +11 -0
  51. package/dist/esm/utilities/usage.js.map +1 -0
  52. package/package.json +2 -2
  53. package/src/activities/chat/adapter.ts +3 -0
  54. package/src/activities/chat/index.ts +219 -47
  55. package/src/activities/chat/messages.ts +2 -1
  56. package/src/activities/chat/middleware/compose.ts +23 -17
  57. package/src/activities/chat/middleware/types.ts +21 -21
  58. package/src/activities/chat/runtime-context-types.ts +68 -0
  59. package/src/activities/chat/stream/message-updaters.ts +2 -1
  60. package/src/activities/chat/stream/processor.ts +48 -8
  61. package/src/activities/chat/tools/tool-calls.ts +138 -43
  62. package/src/activities/chat/tools/tool-definition.ts +25 -31
  63. package/src/activities/error-payload.ts +44 -0
  64. package/src/activities/generateAudio/index.ts +10 -0
  65. package/src/activities/generateSpeech/index.ts +10 -0
  66. package/src/adapter-internals.ts +4 -1
  67. package/src/client.ts +5 -1
  68. package/src/index.ts +10 -0
  69. package/src/tool-registry.ts +16 -14
  70. package/src/types.ts +118 -79
  71. package/src/utilities/ag-ui-wire.ts +4 -1
  72. package/src/utilities/chat-params.ts +22 -7
  73. package/src/utilities/tool-result.ts +60 -0
  74. package/src/utilities/usage.ts +41 -0
@@ -1,4 +1,4 @@
1
- import { DefaultMessageMetadataByModality, JSONSchema, Modality, StreamChunk, TextOptions } from '../../types.js';
1
+ import { DefaultMessageMetadataByModality, JSONSchema, Modality, StreamChunk, TextOptions, TokenUsage } from '../../types.js';
2
2
  /**
3
3
  * Configuration for adapter instances
4
4
  */
@@ -31,6 +31,8 @@ export interface StructuredOutputResult<T = unknown> {
31
31
  data: T;
32
32
  /** The raw text response from the model before parsing */
33
33
  rawText: string;
34
+ /** Token usage information (if provided by the adapter) */
35
+ usage?: TokenUsage;
34
36
  }
35
37
  /**
36
38
  * Text adapter interface with pre-resolved generics.
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sources":["../../../../src/activities/chat/adapter.ts"],"sourcesContent":["import type {\n DefaultMessageMetadataByModality,\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n} from '../../types'\n\n/**\n * Configuration for adapter instances\n */\nexport interface TextAdapterConfig {\n apiKey?: string\n baseUrl?: string\n timeout?: number\n maxRetries?: number\n headers?: Record<string, string>\n}\n\n/**\n * Options for structured output generation.\n *\n * The internal logger is threaded through `chatOptions.logger` (inherited from\n * `TextOptions`). Adapter implementations must call `logger.request()` before\n * SDK calls, `logger.provider()` for each chunk received, and `logger.errors()`\n * in catch blocks.\n */\nexport interface StructuredOutputOptions<TProviderOptions extends object> {\n /** Text options for the request */\n chatOptions: TextOptions<TProviderOptions>\n /** JSON Schema for structured output - already converted from Zod in the ai layer */\n outputSchema: JSONSchema\n}\n\n/**\n * Result from structured output generation\n */\nexport interface StructuredOutputResult<T = unknown> {\n /** The parsed data conforming to the schema */\n data: T\n /** The raw text response from the model before parsing */\n rawText: string\n}\n\n/**\n * Text adapter interface with pre-resolved generics.\n *\n * An adapter is created by a provider function: `provider('model')` → `adapter`\n * All type resolution happens at the provider call site, not in this interface.\n *\n * Generic parameters:\n * - TModel: The specific model name (e.g., 'gpt-4o')\n * - TProviderOptions: Provider-specific options for this model (already resolved)\n * - TInputModalities: Supported input modalities for this model (already resolved)\n * - TMessageMetadata: Metadata types for content parts (already resolved)\n * - TToolCapabilities: Tuple of tool-kind strings supported by this model, resolved from `supports.tools`\n * - TToolCallMetadata: Metadata type that round-trips with tool calls (e.g. Gemini's `thoughtSignature`)\n * - TSystemPromptMetadata: Provider-typed metadata accepted on each\n * `systemPrompts[i]` entry (e.g. Anthropic `cache_control`). Defaults to\n * `never` — adapters without per-prompt metadata reject the `metadata`\n * field at the call site.\n */\nexport interface TextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n TSystemPromptMetadata = never,\n> {\n /** Discriminator for adapter kind */\n readonly kind: 'text'\n /** Provider name identifier (e.g., 'openai', 'anthropic') */\n readonly name: string\n /** The model this adapter is configured for */\n readonly model: TModel\n\n /**\n * @internal Type-only properties for inference. Not assigned at runtime.\n */\n '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n systemPromptMetadata: TSystemPromptMetadata\n }\n\n /**\n * Stream text completions from the model\n */\n chatStream: (\n options: TextOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * This method uses stream: false and sends the JSON schema to the provider\n * to ensure the response conforms to the expected structure.\n *\n * @param options - Structured output options containing chat options and JSON schema\n * @returns Promise with the raw data (validation is done in the chat function)\n */\n structuredOutput: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => Promise<StructuredOutputResult<unknown>>\n\n /**\n * Stream structured output using the provider's native streaming structured\n * output API (stream + response_format json_schema in a single request).\n *\n * Optional — adapters without native streaming JSON omit this method and the\n * activity layer synthesizes a stream around the non-streaming\n * `structuredOutput` call.\n *\n * Implementations must emit standard AG-UI lifecycle events (RUN_STARTED,\n * TEXT_MESSAGE_*, RUN_FINISHED) carrying raw JSON text deltas, plus a final\n * `CUSTOM` event named `structured-output.complete` whose `value` is\n * `{ object, raw, reasoning? }`.\n */\n structuredOutputStream?: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n\n /**\n * Declares whether the adapter supports combining `tools` and a\n * schema-constrained final answer in a single streaming request.\n *\n * When `true`, the engine wires `outputSchema` into the regular\n * `chatStream()` call and skips the separate `runStructuredFinalization`\n * round-trip. The model's natural final turn carries the\n * schema-constrained JSON text and the engine harvests it from the agent\n * loop's accumulated content.\n *\n * When `false`, `undefined`, or the method is omitted, the engine runs\n * the agent loop without `outputSchema` and then issues a separate\n * `structuredOutput` / `structuredOutputStream` call against the JSON\n * schema for finalization (the legacy path).\n *\n * The method receives the per-call `modelOptions` so providers whose\n * support depends on the resolved upstream model (e.g. OpenRouter) can\n * answer per-request. Most adapters can return a constant.\n */\n supportsCombinedToolsAndSchema?: (\n modelOptions?: TProviderOptions | undefined,\n ) => boolean\n}\n\n/**\n * A TextAdapter with any/unknown type parameters.\n * Useful as a constraint in generic functions and interfaces.\n */\nexport type AnyTextAdapter = TextAdapter<any, any, any, any, any, any, any>\n\n/**\n * Abstract base class for text adapters.\n * Extend this class to implement a text adapter for a specific provider.\n *\n * Generic parameters match TextAdapter - all pre-resolved by the provider function.\n */\nexport abstract class BaseTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n TSystemPromptMetadata = never,\n> implements TextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadataByModality,\n TToolCapabilities,\n TToolCallMetadata,\n TSystemPromptMetadata\n> {\n readonly kind = 'text' as const\n abstract readonly name: string\n readonly model: TModel\n\n // Type-only property - never assigned at runtime\n declare '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n systemPromptMetadata: TSystemPromptMetadata\n }\n\n protected config: TextAdapterConfig\n\n constructor(config: TextAdapterConfig = {}, model: TModel) {\n this.config = config\n this.model = model\n }\n\n abstract chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * Concrete implementations should override this to use provider-specific structured output.\n */\n abstract structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>>\n\n protected generateId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n"],"names":[],"mappings":"AAkKO,MAAe,gBAgBpB;AAAA,EACS,OAAO;AAAA,EAEP;AAAA,EAYC;AAAA,EAEV,YAAY,SAA4B,CAAA,GAAI,OAAe;AACzD,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAAA,EAcU,aAAqB;AAC7B,WAAO,GAAG,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,EAC9E;AACF;"}
1
+ {"version":3,"file":"adapter.js","sources":["../../../../src/activities/chat/adapter.ts"],"sourcesContent":["import type {\n DefaultMessageMetadataByModality,\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n TokenUsage,\n} from '../../types'\n\n/**\n * Configuration for adapter instances\n */\nexport interface TextAdapterConfig {\n apiKey?: string\n baseUrl?: string\n timeout?: number\n maxRetries?: number\n headers?: Record<string, string>\n}\n\n/**\n * Options for structured output generation.\n *\n * The internal logger is threaded through `chatOptions.logger` (inherited from\n * `TextOptions`). Adapter implementations must call `logger.request()` before\n * SDK calls, `logger.provider()` for each chunk received, and `logger.errors()`\n * in catch blocks.\n */\nexport interface StructuredOutputOptions<TProviderOptions extends object> {\n /** Text options for the request */\n chatOptions: TextOptions<TProviderOptions>\n /** JSON Schema for structured output - already converted from Zod in the ai layer */\n outputSchema: JSONSchema\n}\n\n/**\n * Result from structured output generation\n */\nexport interface StructuredOutputResult<T = unknown> {\n /** The parsed data conforming to the schema */\n data: T\n /** The raw text response from the model before parsing */\n rawText: string\n /** Token usage information (if provided by the adapter) */\n usage?: TokenUsage\n}\n\n/**\n * Text adapter interface with pre-resolved generics.\n *\n * An adapter is created by a provider function: `provider('model')` → `adapter`\n * All type resolution happens at the provider call site, not in this interface.\n *\n * Generic parameters:\n * - TModel: The specific model name (e.g., 'gpt-4o')\n * - TProviderOptions: Provider-specific options for this model (already resolved)\n * - TInputModalities: Supported input modalities for this model (already resolved)\n * - TMessageMetadata: Metadata types for content parts (already resolved)\n * - TToolCapabilities: Tuple of tool-kind strings supported by this model, resolved from `supports.tools`\n * - TToolCallMetadata: Metadata type that round-trips with tool calls (e.g. Gemini's `thoughtSignature`)\n * - TSystemPromptMetadata: Provider-typed metadata accepted on each\n * `systemPrompts[i]` entry (e.g. Anthropic `cache_control`). Defaults to\n * `never` — adapters without per-prompt metadata reject the `metadata`\n * field at the call site.\n */\nexport interface TextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n TSystemPromptMetadata = never,\n> {\n /** Discriminator for adapter kind */\n readonly kind: 'text'\n /** Provider name identifier (e.g., 'openai', 'anthropic') */\n readonly name: string\n /** The model this adapter is configured for */\n readonly model: TModel\n\n /**\n * @internal Type-only properties for inference. Not assigned at runtime.\n */\n '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n systemPromptMetadata: TSystemPromptMetadata\n }\n\n /**\n * Stream text completions from the model\n */\n chatStream: (\n options: TextOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * This method uses stream: false and sends the JSON schema to the provider\n * to ensure the response conforms to the expected structure.\n *\n * @param options - Structured output options containing chat options and JSON schema\n * @returns Promise with the raw data (validation is done in the chat function)\n */\n structuredOutput: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => Promise<StructuredOutputResult<unknown>>\n\n /**\n * Stream structured output using the provider's native streaming structured\n * output API (stream + response_format json_schema in a single request).\n *\n * Optional — adapters without native streaming JSON omit this method and the\n * activity layer synthesizes a stream around the non-streaming\n * `structuredOutput` call.\n *\n * Implementations must emit standard AG-UI lifecycle events (RUN_STARTED,\n * TEXT_MESSAGE_*, RUN_FINISHED) carrying raw JSON text deltas, plus a final\n * `CUSTOM` event named `structured-output.complete` whose `value` is\n * `{ object, raw, reasoning? }`.\n */\n structuredOutputStream?: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n\n /**\n * Declares whether the adapter supports combining `tools` and a\n * schema-constrained final answer in a single streaming request.\n *\n * When `true`, the engine wires `outputSchema` into the regular\n * `chatStream()` call and skips the separate `runStructuredFinalization`\n * round-trip. The model's natural final turn carries the\n * schema-constrained JSON text and the engine harvests it from the agent\n * loop's accumulated content.\n *\n * When `false`, `undefined`, or the method is omitted, the engine runs\n * the agent loop without `outputSchema` and then issues a separate\n * `structuredOutput` / `structuredOutputStream` call against the JSON\n * schema for finalization (the legacy path).\n *\n * The method receives the per-call `modelOptions` so providers whose\n * support depends on the resolved upstream model (e.g. OpenRouter) can\n * answer per-request. Most adapters can return a constant.\n */\n supportsCombinedToolsAndSchema?: (\n modelOptions?: TProviderOptions | undefined,\n ) => boolean\n}\n\n/**\n * A TextAdapter with any/unknown type parameters.\n * Useful as a constraint in generic functions and interfaces.\n */\nexport type AnyTextAdapter = TextAdapter<any, any, any, any, any, any, any>\n\n/**\n * Abstract base class for text adapters.\n * Extend this class to implement a text adapter for a specific provider.\n *\n * Generic parameters match TextAdapter - all pre-resolved by the provider function.\n */\nexport abstract class BaseTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n TSystemPromptMetadata = never,\n> implements TextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadataByModality,\n TToolCapabilities,\n TToolCallMetadata,\n TSystemPromptMetadata\n> {\n readonly kind = 'text' as const\n abstract readonly name: string\n readonly model: TModel\n\n // Type-only property - never assigned at runtime\n declare '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n systemPromptMetadata: TSystemPromptMetadata\n }\n\n protected config: TextAdapterConfig\n\n constructor(config: TextAdapterConfig = {}, model: TModel) {\n this.config = config\n this.model = model\n }\n\n abstract chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * Concrete implementations should override this to use provider-specific structured output.\n */\n abstract structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>>\n\n protected generateId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n"],"names":[],"mappings":"AAqKO,MAAe,gBAgBpB;AAAA,EACS,OAAO;AAAA,EAEP;AAAA,EAYC;AAAA,EAEV,YAAY,SAA4B,CAAA,GAAI,OAAe;AACzD,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAAA,EAcU,aAAqB;AAC7B,WAAO,GAAG,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,EAC9E;AACF;"}
@@ -1,11 +1,36 @@
1
1
  import { AnyTextAdapter } from './adapter.js';
2
- import { ConstrainedModelMessage, InferSchemaType, ModelMessage, SchemaInput, StreamChunk, StructuredOutputStream, TextOptions, Tool, UIMessage } from '../../types.js';
2
+ import { AnyTool, ConstrainedModelMessage, InferSchemaType, ModelMessage, SchemaInput, StreamChunk, StructuredOutputStream, TextOptions, UIMessage } from '../../types.js';
3
3
  import { ChatMiddleware } from './middleware/types.js';
4
4
  import { SystemPrompt } from '../../system-prompts.js';
5
5
  import { DebugOption } from '../../logger/types.js';
6
6
  import { ProviderTool } from '../../tools/provider-tool.js';
7
+ import { ContextFromMiddleware, ContextFromTool, DefinedContext, MergeContext, UnionToIntersection } from './runtime-context-types.js';
7
8
  /** The adapter kind this activity handles */
8
9
  export declare const kind: "text";
10
+ type AnyRuntimeTool = AnyTool;
11
+ type ContextFromConsumer<T> = ContextFromTool<T> | ContextFromMiddleware<T>;
12
+ type RequiredContextFromConsumerUnion<T> = T extends unknown ? undefined extends ContextFromConsumer<T> ? never : ContextFromConsumer<T> : never;
13
+ type ContextFromConsumerUnion<T> = [
14
+ UnionToIntersection<DefinedContext<ContextFromConsumer<T>>>
15
+ ] extends [never] ? never : [RequiredContextFromConsumerUnion<T>] extends [never] ? UnionToIntersection<DefinedContext<ContextFromConsumer<T>>> | undefined : UnionToIntersection<DefinedContext<ContextFromConsumer<T>>>;
16
+ type ContextFromArray<T> = T extends readonly [infer THead, ...infer TTail] ? MergeContext<ContextFromConsumer<THead>, ContextFromArray<TTail>> : T extends ReadonlyArray<infer TItem> ? ContextFromConsumerUnion<TItem> : never;
17
+ type ContextFromInputs<TTools, TMiddleware> = MergeContext<ContextFromArray<NonNullable<TTools>>, ContextFromArray<NonNullable<TMiddleware>>>;
18
+ type InferredContext<TTools, TMiddleware> = [
19
+ ContextFromInputs<TTools, TMiddleware>
20
+ ] extends [never] ? unknown : ContextFromInputs<TTools, TMiddleware>;
21
+ type RequiredContextFromInputs<TTools, TMiddleware> = [
22
+ ContextFromInputs<TTools, TMiddleware>
23
+ ] extends [never] ? {
24
+ context?: unknown;
25
+ } : undefined extends ContextFromInputs<TTools, TMiddleware> ? {
26
+ context?: ContextFromInputs<TTools, TMiddleware>;
27
+ } : {
28
+ context: ContextFromInputs<TTools, TMiddleware>;
29
+ };
30
+ type TextActivityOptionsWithContext<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined, TStream extends boolean, TTools extends TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'], TMiddleware extends TextActivityOptions<TAdapter, TSchema, TStream, any>['middleware']> = Omit<TextActivityOptions<TAdapter, TSchema, TStream, any>, 'tools' | 'middleware' | 'context'> & {
31
+ tools?: TTools;
32
+ middleware?: TMiddleware;
33
+ } & RequiredContextFromInputs<TTools, TMiddleware>;
9
34
  /**
10
35
  * Options for the text activity.
11
36
  * Types are extracted directly from the adapter (which has pre-resolved generics).
@@ -14,7 +39,7 @@ export declare const kind: "text";
14
39
  * @template TSchema - Optional Standard Schema for structured output
15
40
  * @template TStream - Whether to stream the output (default: true)
16
41
  */
17
- export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined, TStream extends boolean> {
42
+ export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined, TStream extends boolean, TContext = unknown> {
18
43
  /** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */
19
44
  adapter: TAdapter;
20
45
  /**
@@ -49,7 +74,7 @@ export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema ex
49
74
  * `supports.tools` list. Passing an unsupported tool produces a
50
75
  * compile-time error on the array element.
51
76
  */
52
- tools?: Array<(Tool & {
77
+ tools?: Array<(AnyRuntimeTool & {
53
78
  readonly '~toolKind'?: never;
54
79
  }) | ProviderTool<string, TAdapter['~types']['toolCapabilities'][number]>> | undefined;
55
80
  /** Controls the randomness of the output. Higher values make output more random. Range: [0.0, 2.0] */
@@ -127,12 +152,11 @@ export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema ex
127
152
  * })
128
153
  * ```
129
154
  */
130
- middleware?: Array<ChatMiddleware>;
155
+ middleware?: Array<ChatMiddleware<TContext>>;
131
156
  /**
132
- * Opaque user-provided context value passed to middleware hooks.
133
- * Can be used to pass request-scoped data (e.g., user ID, request context).
157
+ * Runtime context value passed to middleware hooks and server tools.
134
158
  */
135
- context?: unknown;
159
+ context?: TContext;
136
160
  /**
137
161
  * Enable debug logging. Pass `true` to enable all categories with the default
138
162
  * console logger, `false` to silence everything, or a `DebugConfig` object for
@@ -154,7 +178,7 @@ export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema ex
154
178
  * const stream = chat({ ...chatOptions, messages })
155
179
  * ```
156
180
  */
157
- export declare function createChatOptions<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined = undefined, TStream extends boolean = true>(options: TextActivityOptions<TAdapter, TSchema, TStream>): TextActivityOptions<TAdapter, TSchema, TStream>;
181
+ export declare function createChatOptions<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined = undefined, TStream extends boolean = true, const TTools extends TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'] = TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'], const TMiddleware extends TextActivityOptions<TAdapter, TSchema, TStream, any>['middleware'] = TextActivityOptions<TAdapter, TSchema, TStream, any>['middleware']>(options: TextActivityOptionsWithContext<TAdapter, TSchema, TStream, TTools, TMiddleware>): TextActivityOptions<TAdapter, TSchema, TStream, InferredContext<TTools, TMiddleware>>;
158
182
  /**
159
183
  * Result type for the text activity.
160
184
  * - If outputSchema is provided AND stream is explicitly true:
@@ -233,6 +257,6 @@ export type TextActivityResult<TSchema extends SchemaInput | undefined, TStream
233
257
  * // result is { summary: string, keyPoints: string[] }
234
258
  * ```
235
259
  */
236
- export declare function chat<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined = undefined, TStream extends boolean = boolean>(options: TextActivityOptions<TAdapter, TSchema, TStream>): TextActivityResult<TSchema, TStream>;
260
+ export declare function chat<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined = undefined, TStream extends boolean = boolean, const TTools extends TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'] = TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'], const TMiddleware extends TextActivityOptions<TAdapter, TSchema, TStream, any>['middleware'] = TextActivityOptions<TAdapter, TSchema, TStream, any>['middleware']>(options: TextActivityOptionsWithContext<TAdapter, TSchema, TStream, TTools, TMiddleware>): TextActivityResult<TSchema, TStream>;
237
261
  export type { TextAdapter, TextAdapterConfig, StructuredOutputOptions, StructuredOutputResult, } from './adapter.js';
238
262
  export { BaseTextAdapter } from './adapter.js';
@@ -2,6 +2,7 @@ import { devtoolsMiddleware } from "@tanstack/ai-event-client";
2
2
  import { stripToSpecMiddleware } from "../../strip-to-spec-middleware.js";
3
3
  import { streamToText } from "../../stream-to-response.js";
4
4
  import { resolveDebugOption } from "../../logger/resolve.js";
5
+ import { normalizeToolResult } from "../../utilities/tool-result.js";
5
6
  import { LazyToolManager } from "./tools/lazy-tool-manager.js";
6
7
  import { ToolCallManager, MiddlewareAbortError, executeToolCalls } from "./tools/tool-calls.js";
7
8
  import { convertSchemaToJsonSchema, isStandardSchema, parseWithStandardSchema } from "./tools/schema-converter.js";
@@ -567,7 +568,8 @@ class TextEngine {
567
568
  info
568
569
  );
569
570
  }
570
- }
571
+ },
572
+ this.middlewareCtx.context
571
573
  );
572
574
  const executionResult = yield* this.drainToolCallGenerator(generator);
573
575
  if (this.isMiddlewareAborted()) {
@@ -697,7 +699,8 @@ class TextEngine {
697
699
  info
698
700
  );
699
701
  }
700
- }
702
+ },
703
+ this.middlewareCtx.context
701
704
  );
702
705
  const executionResult = yield* this.drainToolCallGenerator(generator);
703
706
  this.middlewareCtx.phase = "afterTools";
@@ -798,10 +801,14 @@ class TextEngine {
798
801
  for (const message of this.messages) {
799
802
  if (message.role === "tool" && message.toolCallId) {
800
803
  let output;
801
- try {
802
- output = JSON.parse(message.content);
803
- } catch {
804
+ if (Array.isArray(message.content)) {
804
805
  output = message.content;
806
+ } else {
807
+ try {
808
+ output = JSON.parse(message.content);
809
+ } catch {
810
+ output = message.content;
811
+ }
805
812
  }
806
813
  if (output && typeof output === "object" && output.pendingExecution === true) {
807
814
  continue;
@@ -852,7 +859,8 @@ class TextEngine {
852
859
  buildToolResultChunks(results, finishEvent, argsMap) {
853
860
  const chunks = [];
854
861
  for (const result of results) {
855
- const content = JSON.stringify(result.result);
862
+ const content = normalizeToolResult(result.result);
863
+ const wireContent = typeof content === "string" ? content : JSON.stringify(content);
856
864
  if (argsMap) {
857
865
  chunks.push({
858
866
  type: "TOOL_CALL_START",
@@ -879,7 +887,8 @@ class TextEngine {
879
887
  toolCallId: result.toolCallId,
880
888
  toolCallName: result.toolName,
881
889
  toolName: result.toolName,
882
- result: content
890
+ result: wireContent,
891
+ ...result.state !== void 0 && { state: result.state }
883
892
  });
884
893
  chunks.push({
885
894
  type: "TOOL_CALL_RESULT",
@@ -887,8 +896,9 @@ class TextEngine {
887
896
  model: finishEvent.model,
888
897
  messageId: this.createId("tool-result"),
889
898
  toolCallId: result.toolCallId,
890
- content,
891
- role: "tool"
899
+ content: wireContent,
900
+ role: "tool",
901
+ ...result.state !== void 0 && { state: result.state }
892
902
  });
893
903
  const placeholderIdx = this.messages.findIndex((m) => {
894
904
  if (m.role !== "tool" || m.toolCallId !== result.toolCallId) {