@tanstack/ai 0.15.0 → 0.16.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.
@@ -70,15 +70,17 @@ export type SchemaInput = StandardJSONSchemaV1<any, any> | JSONSchema;
70
70
  * For plain JSONSchema, returns `any` since we can't infer types from JSON Schema at compile time.
71
71
  */
72
72
  export type InferSchemaType<T> = T extends StandardJSONSchemaV1<infer TInput, unknown> ? TInput : unknown;
73
- export interface ToolCall {
73
+ export interface ToolCall<TMetadata = unknown> {
74
74
  id: string;
75
75
  type: 'function';
76
76
  function: {
77
77
  name: string;
78
78
  arguments: string;
79
79
  };
80
- /** Provider-specific metadata to carry through the tool call lifecycle */
81
- providerMetadata?: Record<string, unknown>;
80
+ /** Provider-specific metadata to carry through the tool call lifecycle.
81
+ * Typed per-adapter via `TToolCallMetadata`. For example,
82
+ * `@tanstack/ai-gemini` sets this to `{ thoughtSignature?: string }`. */
83
+ metadata?: TMetadata;
82
84
  }
83
85
  /**
84
86
  * Supported input modality types for multimodal content.
@@ -221,7 +223,7 @@ export interface TextPart<TMetadata = unknown> {
221
223
  content: string;
222
224
  metadata?: TMetadata;
223
225
  }
224
- export interface ToolCallPart {
226
+ export interface ToolCallPart<TMetadata = unknown> {
225
227
  type: 'tool-call';
226
228
  id: string;
227
229
  name: string;
@@ -235,6 +237,9 @@ export interface ToolCallPart {
235
237
  };
236
238
  /** Tool execution output (for client tools or after approval) */
237
239
  output?: any;
240
+ /** Provider-specific metadata that round-trips with the tool call.
241
+ * Typed per-adapter via `TToolCallMetadata`. */
242
+ metadata?: TMetadata;
238
243
  }
239
244
  export interface ToolResultPart {
240
245
  type: 'tool-result';
@@ -740,7 +745,7 @@ export interface TextMessageEndEvent extends AGUITextMessageEndEvent {
740
745
  * Emitted when a tool call starts.
741
746
  *
742
747
  * @ag-ui/core provides: `toolCallId`, `toolCallName`, `parentMessageId?`
743
- * TanStack AI adds: `model?`, `toolName` (deprecated alias), `index?`, `providerMetadata?`
748
+ * TanStack AI adds: `model?`, `toolName` (deprecated alias), `index?`, `metadata?`
744
749
  */
745
750
  export interface ToolCallStartEvent extends AGUIToolCallStartEvent {
746
751
  /** Model identifier for multi-model support */
@@ -752,8 +757,11 @@ export interface ToolCallStartEvent extends AGUIToolCallStartEvent {
752
757
  toolName: string;
753
758
  /** Index for parallel tool calls */
754
759
  index?: number;
755
- /** Provider-specific metadata to carry into the ToolCall */
756
- providerMetadata?: Record<string, unknown>;
760
+ /** Provider-specific metadata to carry into the ToolCall.
761
+ * Untyped at the event layer because events flow through a discriminated
762
+ * union that does not survive generics; adapters cast it to their typed
763
+ * `TToolCallMetadata` shape when emitting. */
764
+ metadata?: Record<string, unknown>;
757
765
  }
758
766
  /**
759
767
  * Emitted when tool call arguments are streaming.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Core TanStack AI library - Open source AI SDK",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@ag-ui/core": "0.0.49",
57
57
  "partial-json": "^0.1.7",
58
- "@tanstack/ai-event-client": "0.2.9"
58
+ "@tanstack/ai-event-client": "0.3.0"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@opentelemetry/api": ">=1.9.0"
@@ -54,6 +54,7 @@ export interface StructuredOutputResult<T = unknown> {
54
54
  * - TInputModalities: Supported input modalities for this model (already resolved)
55
55
  * - TMessageMetadata: Metadata types for content parts (already resolved)
56
56
  * - TToolCapabilities: Tuple of tool-kind strings supported by this model, resolved from `supports.tools`
57
+ * - TToolCallMetadata: Metadata type that round-trips with tool calls (e.g. Gemini's `thoughtSignature`)
57
58
  */
58
59
  export interface TextAdapter<
59
60
  TModel extends string,
@@ -61,6 +62,7 @@ export interface TextAdapter<
61
62
  TInputModalities extends ReadonlyArray<Modality>,
62
63
  TMessageMetadataByModality extends DefaultMessageMetadataByModality,
63
64
  TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,
65
+ TToolCallMetadata = unknown,
64
66
  > {
65
67
  /** Discriminator for adapter kind */
66
68
  readonly kind: 'text'
@@ -77,6 +79,7 @@ export interface TextAdapter<
77
79
  inputModalities: TInputModalities
78
80
  messageMetadataByModality: TMessageMetadataByModality
79
81
  toolCapabilities: TToolCapabilities
82
+ toolCallMetadata: TToolCallMetadata
80
83
  }
81
84
 
82
85
  /**
@@ -103,7 +106,7 @@ export interface TextAdapter<
103
106
  * A TextAdapter with any/unknown type parameters.
104
107
  * Useful as a constraint in generic functions and interfaces.
105
108
  */
106
- export type AnyTextAdapter = TextAdapter<any, any, any, any, any>
109
+ export type AnyTextAdapter = TextAdapter<any, any, any, any, any, any>
107
110
 
108
111
  /**
109
112
  * Abstract base class for text adapters.
@@ -117,12 +120,14 @@ export abstract class BaseTextAdapter<
117
120
  TInputModalities extends ReadonlyArray<Modality>,
118
121
  TMessageMetadataByModality extends DefaultMessageMetadataByModality,
119
122
  TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,
123
+ TToolCallMetadata = unknown,
120
124
  > implements TextAdapter<
121
125
  TModel,
122
126
  TProviderOptions,
123
127
  TInputModalities,
124
128
  TMessageMetadataByModality,
125
- TToolCapabilities
129
+ TToolCapabilities,
130
+ TToolCallMetadata
126
131
  > {
127
132
  readonly kind = 'text' as const
128
133
  abstract readonly name: string
@@ -134,6 +139,7 @@ export abstract class BaseTextAdapter<
134
139
  inputModalities: TInputModalities
135
140
  messageMetadataByModality: TMessageMetadataByModality
136
141
  toolCapabilities: TToolCapabilities
142
+ toolCallMetadata: TToolCallMetadata
137
143
  }
138
144
 
139
145
  protected config: TextAdapterConfig
@@ -138,6 +138,10 @@ interface AssistantSegment {
138
138
  id: string
139
139
  type: 'function'
140
140
  function: { name: string; arguments: string }
141
+ /** Provider-specific metadata that round-trips with the tool call.
142
+ * Untyped at this framework layer; adapters narrow it via their
143
+ * `TToolCallMetadata` generic. */
144
+ metadata?: unknown
141
145
  }>
142
146
  }
143
147
 
@@ -208,6 +212,7 @@ function buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {
208
212
  name: part.name,
209
213
  arguments: part.arguments,
210
214
  },
215
+ ...(part.metadata !== undefined && { metadata: part.metadata }),
211
216
  })
212
217
  }
213
218
  break
@@ -362,6 +367,7 @@ export function modelMessageToUIMessage(
362
367
  name: toolCall.function.name,
363
368
  arguments: toolCall.function.arguments,
364
369
  state: 'input-complete', // Model messages have complete arguments
370
+ ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),
365
371
  })
366
372
  }
367
373
  }
@@ -55,6 +55,7 @@ export function updateToolCallPart(
55
55
  name: string
56
56
  arguments: string
57
57
  state: ToolCallState
58
+ metadata?: Record<string, unknown>
58
59
  },
59
60
  ): Array<UIMessage> {
60
61
  return messages.map((msg) => {
@@ -67,6 +68,12 @@ export function updateToolCallPart(
67
68
  (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCall.id,
68
69
  )
69
70
 
71
+ // Carry forward metadata from either the new toolCall or the existing
72
+ // part. Once the adapter has emitted metadata for a tool call (e.g.
73
+ // Gemini's thoughtSignature on TOOL_CALL_START) we must not lose it on
74
+ // subsequent updates that don't re-supply it.
75
+ const metadata = toolCall.metadata ?? existing?.metadata
76
+
70
77
  const toolCallPart: ToolCallPart = {
71
78
  type: 'tool-call',
72
79
  id: toolCall.id,
@@ -76,6 +83,7 @@ export function updateToolCallPart(
76
83
  // Carry forward approval and output from the existing part
77
84
  ...(existing?.approval && { approval: { ...existing.approval } }),
78
85
  ...(existing?.output !== undefined && { output: existing.output }),
86
+ ...(metadata !== undefined && { metadata }),
79
87
  }
80
88
 
81
89
  if (existing) {
@@ -936,6 +936,11 @@ export class StreamProcessor {
936
936
  const toolName =
937
937
  (chunk as { toolCallName?: string }).toolCallName ?? chunk.toolName
938
938
 
939
+ // Capture provider metadata that arrived on TOOL_CALL_START so it
940
+ // round-trips back through the assistant message on the next turn
941
+ // (e.g. Gemini's thoughtSignature).
942
+ const chunkMetadata = chunk.metadata
943
+
939
944
  const newToolCall: InternalToolCallState = {
940
945
  id: chunk.toolCallId,
941
946
  name: toolName,
@@ -943,6 +948,7 @@ export class StreamProcessor {
943
948
  state: initialState,
944
949
  parsedArguments: undefined,
945
950
  index: chunk.index ?? state.toolCalls.size,
951
+ ...(chunkMetadata !== undefined && { metadata: chunkMetadata }),
946
952
  }
947
953
 
948
954
  state.toolCalls.set(toolCallId, newToolCall)
@@ -957,6 +963,7 @@ export class StreamProcessor {
957
963
  name: toolName,
958
964
  arguments: '',
959
965
  state: initialState,
966
+ ...(chunkMetadata !== undefined && { metadata: chunkMetadata }),
960
967
  })
961
968
  this.emitMessagesChange()
962
969
 
@@ -1504,6 +1511,7 @@ export class StreamProcessor {
1504
1511
  name: toolCall.name,
1505
1512
  arguments: toolCall.arguments,
1506
1513
  state: 'input-complete',
1514
+ ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),
1507
1515
  })
1508
1516
  this.emitMessagesChange()
1509
1517
 
@@ -1619,6 +1627,10 @@ export class StreamProcessor {
1619
1627
  name: tc.name,
1620
1628
  arguments: tc.arguments,
1621
1629
  },
1630
+ // Preserve provider metadata (e.g. Gemini thoughtSignature) on
1631
+ // ProcessorResult.toolCalls so callers using process()/getResult()
1632
+ // get the same round-trip support as the streaming UI path.
1633
+ ...(tc.metadata !== undefined && { metadata: tc.metadata }),
1622
1634
  })
1623
1635
  }
1624
1636
  }
@@ -25,6 +25,11 @@ export interface InternalToolCallState {
25
25
  state: ToolCallState
26
26
  parsedArguments?: any
27
27
  index: number
28
+ /** Provider-specific metadata that round-trips with the tool call
29
+ * (e.g. Gemini's `thoughtSignature`). Untyped at this layer because
30
+ * the stream processor is provider-agnostic; adapters narrow it
31
+ * via their `TToolCallMetadata` generic. */
32
+ metadata?: Record<string, unknown>
28
33
  }
29
34
 
30
35
  /**
@@ -103,9 +103,7 @@ export class ToolCallManager {
103
103
  name,
104
104
  arguments: '',
105
105
  },
106
- ...(event.providerMetadata && {
107
- providerMetadata: event.providerMetadata,
108
- }),
106
+ ...(event.metadata !== undefined && { metadata: event.metadata }),
109
107
  })
110
108
  }
111
109
 
package/src/types.ts CHANGED
@@ -111,15 +111,17 @@ export type SchemaInput = StandardJSONSchemaV1<any, any> | JSONSchema
111
111
  export type InferSchemaType<T> =
112
112
  T extends StandardJSONSchemaV1<infer TInput, unknown> ? TInput : unknown
113
113
 
114
- export interface ToolCall {
114
+ export interface ToolCall<TMetadata = unknown> {
115
115
  id: string
116
116
  type: 'function'
117
117
  function: {
118
118
  name: string
119
119
  arguments: string // JSON string
120
120
  }
121
- /** Provider-specific metadata to carry through the tool call lifecycle */
122
- providerMetadata?: Record<string, unknown>
121
+ /** Provider-specific metadata to carry through the tool call lifecycle.
122
+ * Typed per-adapter via `TToolCallMetadata`. For example,
123
+ * `@tanstack/ai-gemini` sets this to `{ thoughtSignature?: string }`. */
124
+ metadata?: TMetadata
123
125
  }
124
126
 
125
127
  // ============================================================================
@@ -309,7 +311,7 @@ export interface TextPart<TMetadata = unknown> {
309
311
  metadata?: TMetadata
310
312
  }
311
313
 
312
- export interface ToolCallPart {
314
+ export interface ToolCallPart<TMetadata = unknown> {
313
315
  type: 'tool-call'
314
316
  id: string
315
317
  name: string
@@ -323,6 +325,9 @@ export interface ToolCallPart {
323
325
  }
324
326
  /** Tool execution output (for client tools or after approval) */
325
327
  output?: any
328
+ /** Provider-specific metadata that round-trips with the tool call.
329
+ * Typed per-adapter via `TToolCallMetadata`. */
330
+ metadata?: TMetadata
326
331
  }
327
332
 
328
333
  export interface ToolResultPart {
@@ -892,7 +897,7 @@ export interface TextMessageEndEvent extends AGUITextMessageEndEvent {
892
897
  * Emitted when a tool call starts.
893
898
  *
894
899
  * @ag-ui/core provides: `toolCallId`, `toolCallName`, `parentMessageId?`
895
- * TanStack AI adds: `model?`, `toolName` (deprecated alias), `index?`, `providerMetadata?`
900
+ * TanStack AI adds: `model?`, `toolName` (deprecated alias), `index?`, `metadata?`
896
901
  */
897
902
  export interface ToolCallStartEvent extends AGUIToolCallStartEvent {
898
903
  /** Model identifier for multi-model support */
@@ -904,8 +909,11 @@ export interface ToolCallStartEvent extends AGUIToolCallStartEvent {
904
909
  toolName: string
905
910
  /** Index for parallel tool calls */
906
911
  index?: number
907
- /** Provider-specific metadata to carry into the ToolCall */
908
- providerMetadata?: Record<string, unknown>
912
+ /** Provider-specific metadata to carry into the ToolCall.
913
+ * Untyped at the event layer because events flow through a discriminated
914
+ * union that does not survive generics; adapters cast it to their typed
915
+ * `TToolCallMetadata` shape when emitting. */
916
+ metadata?: Record<string, unknown>
909
917
  }
910
918
 
911
919
  /**