@tanstack/ai 0.15.0 → 0.17.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 (54) hide show
  1. package/dist/esm/activities/chat/adapter.d.ts +20 -3
  2. package/dist/esm/activities/chat/adapter.js.map +1 -1
  3. package/dist/esm/activities/chat/index.d.ts +16 -6
  4. package/dist/esm/activities/chat/index.js +235 -9
  5. package/dist/esm/activities/chat/index.js.map +1 -1
  6. package/dist/esm/activities/chat/messages.js +4 -2
  7. package/dist/esm/activities/chat/messages.js.map +1 -1
  8. package/dist/esm/activities/chat/stream/message-updaters.d.ts +1 -0
  9. package/dist/esm/activities/chat/stream/message-updaters.js +3 -1
  10. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  11. package/dist/esm/activities/chat/stream/processor.js +12 -4
  12. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  13. package/dist/esm/activities/chat/stream/types.d.ts +5 -0
  14. package/dist/esm/activities/chat/tools/tool-calls.js +1 -3
  15. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  16. package/dist/esm/activities/error-payload.d.ts +0 -8
  17. package/dist/esm/activities/error-payload.js +20 -2
  18. package/dist/esm/activities/error-payload.js.map +1 -1
  19. package/dist/esm/activities/generateImage/adapter.d.ts +2 -2
  20. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  21. package/dist/esm/activities/generateVideo/adapter.d.ts +2 -2
  22. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  23. package/dist/esm/activities/index.d.ts +1 -0
  24. package/dist/esm/activities/index.js +2 -0
  25. package/dist/esm/activities/index.js.map +1 -1
  26. package/dist/esm/activities/stream-generation-result.js +0 -2
  27. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  28. package/dist/esm/activities/summarize/adapter.d.ts +4 -4
  29. package/dist/esm/activities/summarize/adapter.js.map +1 -1
  30. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +45 -0
  31. package/dist/esm/activities/summarize/chat-stream-summarize.js +148 -0
  32. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -0
  33. package/dist/esm/activities/summarize/index.d.ts +1 -0
  34. package/dist/esm/activities/summarize/index.js +4 -2
  35. package/dist/esm/activities/summarize/index.js.map +1 -1
  36. package/dist/esm/types.d.ts +109 -10
  37. package/package.json +2 -2
  38. package/skills/ai-core/structured-outputs/SKILL.md +92 -1
  39. package/src/activities/chat/adapter.ts +25 -2
  40. package/src/activities/chat/index.ts +368 -26
  41. package/src/activities/chat/messages.ts +6 -0
  42. package/src/activities/chat/stream/message-updaters.ts +8 -0
  43. package/src/activities/chat/stream/processor.ts +12 -0
  44. package/src/activities/chat/stream/types.ts +5 -0
  45. package/src/activities/chat/tools/tool-calls.ts +1 -3
  46. package/src/activities/error-payload.ts +31 -2
  47. package/src/activities/generateImage/adapter.ts +8 -2
  48. package/src/activities/generateVideo/adapter.ts +8 -2
  49. package/src/activities/index.ts +5 -0
  50. package/src/activities/stream-generation-result.ts +4 -6
  51. package/src/activities/summarize/adapter.ts +8 -4
  52. package/src/activities/summarize/chat-stream-summarize.ts +238 -0
  53. package/src/activities/summarize/index.ts +12 -9
  54. package/src/types.ts +122 -10
@@ -60,6 +60,11 @@ export {
60
60
  type SummarizeAdapterConfig,
61
61
  type AnySummarizeAdapter,
62
62
  } from './summarize/adapter'
63
+ export {
64
+ ChatStreamSummarizeAdapter,
65
+ type ChatStreamCapable,
66
+ type InferTextProviderOptions,
67
+ } from './summarize/chat-stream-summarize'
63
68
 
64
69
  // ===========================
65
70
  // Image Activity
@@ -34,7 +34,7 @@ export async function* streamGenerationResult<TResult>(
34
34
  runId,
35
35
  threadId,
36
36
  timestamp: Date.now(),
37
- } as StreamChunk
37
+ }
38
38
 
39
39
  try {
40
40
  const result = await generator()
@@ -44,7 +44,7 @@ export async function* streamGenerationResult<TResult>(
44
44
  name: 'generation:result',
45
45
  value: result as unknown,
46
46
  timestamp: Date.now(),
47
- } as StreamChunk
47
+ }
48
48
 
49
49
  yield {
50
50
  type: EventType.RUN_FINISHED,
@@ -52,18 +52,16 @@ export async function* streamGenerationResult<TResult>(
52
52
  threadId,
53
53
  finishReason: 'stop',
54
54
  timestamp: Date.now(),
55
- } as StreamChunk
55
+ }
56
56
  } catch (error: unknown) {
57
57
  const payload = toRunErrorPayload(error, 'Generation failed')
58
58
  yield {
59
59
  type: EventType.RUN_ERROR,
60
- runId,
61
- threadId,
62
60
  message: payload.message,
63
61
  code: payload.code,
64
62
  // Deprecated nested form for backward compatibility
65
63
  error: payload,
66
64
  timestamp: Date.now(),
67
- } as StreamChunk
65
+ }
68
66
  }
69
67
  }
@@ -46,7 +46,9 @@ export interface SummarizeAdapter<
46
46
  /**
47
47
  * Summarize the given text
48
48
  */
49
- summarize: (options: SummarizationOptions) => Promise<SummarizationResult>
49
+ summarize: (
50
+ options: SummarizationOptions<TProviderOptions>,
51
+ ) => Promise<SummarizationResult>
50
52
 
51
53
  /**
52
54
  * Stream summarization of the given text.
@@ -54,7 +56,7 @@ export interface SummarizeAdapter<
54
56
  * non-streaming summarize and yield the result as a single chunk.
55
57
  */
56
58
  summarizeStream?: (
57
- options: SummarizationOptions,
59
+ options: SummarizationOptions<TProviderOptions>,
58
60
  ) => AsyncIterable<StreamChunk>
59
61
  }
60
62
 
@@ -91,7 +93,7 @@ export abstract class BaseSummarizeAdapter<
91
93
  }
92
94
 
93
95
  abstract summarize(
94
- options: SummarizationOptions,
96
+ options: SummarizationOptions<TProviderOptions>,
95
97
  ): Promise<SummarizationResult>
96
98
 
97
99
  /**
@@ -99,7 +101,9 @@ export abstract class BaseSummarizeAdapter<
99
101
  * Override this method in concrete implementations to enable streaming.
100
102
  * If not overridden, the activity layer will fall back to non-streaming.
101
103
  */
102
- summarizeStream?(options: SummarizationOptions): AsyncIterable<StreamChunk>
104
+ summarizeStream?(
105
+ options: SummarizationOptions<TProviderOptions>,
106
+ ): AsyncIterable<StreamChunk>
103
107
 
104
108
  protected generateId(): string {
105
109
  return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`
@@ -0,0 +1,238 @@
1
+ import { EventType } from '@ag-ui/core'
2
+ import { toRunErrorPayload } from '../error-payload'
3
+ import { BaseSummarizeAdapter } from './adapter'
4
+ import type {
5
+ StreamChunk,
6
+ SummarizationOptions,
7
+ SummarizationResult,
8
+ TextOptions,
9
+ } from '../../types'
10
+
11
+ /**
12
+ * Minimal contract for a text adapter that supports `chatStream`. Lets
13
+ * `ChatStreamSummarizeAdapter` work with any text adapter without coupling
14
+ * to a specific implementation.
15
+ *
16
+ * The provider-options shape is intentionally `any` here — the wrapper only
17
+ * forwards `modelOptions` straight through, so a text adapter with a richer
18
+ * per-model options type (e.g. `ResolveProviderOptions<TModel>`) is still
19
+ * acceptable. Summarize-level type safety is enforced via
20
+ * `SummarizationOptions<TProviderOptions>` on the wrapper itself.
21
+ */
22
+ export interface ChatStreamCapable {
23
+ chatStream: (options: TextOptions<any>) => AsyncIterable<StreamChunk>
24
+ }
25
+
26
+ /**
27
+ * Extract the per-model `modelOptions` type a text adapter accepts. Used by
28
+ * provider summarize factories so their `modelOptions` IntelliSense matches
29
+ * what the underlying text adapter actually understands.
30
+ */
31
+ export type InferTextProviderOptions<TAdapter> = TAdapter extends {
32
+ '~types': { providerOptions: infer P }
33
+ }
34
+ ? P extends object
35
+ ? P
36
+ : object
37
+ : object
38
+
39
+ /**
40
+ * Summarize adapter that wraps any `ChatStreamCapable` text adapter and
41
+ * prompts it for summarization. Not tied to any wire format.
42
+ */
43
+ export class ChatStreamSummarizeAdapter<
44
+ TModel extends string,
45
+ TProviderOptions extends object = Record<string, unknown>,
46
+ > extends BaseSummarizeAdapter<TModel, TProviderOptions> {
47
+ readonly name: string
48
+
49
+ private textAdapter: ChatStreamCapable
50
+
51
+ constructor(
52
+ textAdapter: ChatStreamCapable,
53
+ model: TModel,
54
+ name: string = 'chat-stream-summarize',
55
+ ) {
56
+ super({}, model)
57
+ this.name = name
58
+ this.textAdapter = textAdapter
59
+ }
60
+
61
+ async summarize(
62
+ options: SummarizationOptions<TProviderOptions>,
63
+ ): Promise<SummarizationResult> {
64
+ const systemPrompt = this.buildSummarizationPrompt(options)
65
+
66
+ let summary = ''
67
+ const id = this.generateId()
68
+ let model = options.model
69
+ let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }
70
+
71
+ options.logger.request(
72
+ `activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,
73
+ { provider: this.name, model: options.model },
74
+ )
75
+
76
+ try {
77
+ for await (const chunk of this.textAdapter.chatStream(
78
+ this.buildTextOptions(options, systemPrompt),
79
+ )) {
80
+ if (chunk.type === 'TEXT_MESSAGE_CONTENT') {
81
+ if (chunk.content) {
82
+ summary = chunk.content
83
+ } else if (chunk.delta) {
84
+ // Append delta only when present — a content-less chunk with no
85
+ // delta would otherwise concat literal `'undefined'`.
86
+ summary += chunk.delta
87
+ }
88
+ model = chunk.model || model
89
+ }
90
+ if (chunk.type === 'RUN_FINISHED') {
91
+ if (chunk.usage) {
92
+ usage = chunk.usage
93
+ }
94
+ }
95
+ // Surface failures: the underlying chatStream emits RUN_ERROR instead
96
+ // of throwing, so without this branch summarize() would return an
97
+ // empty summary and pretend a failed run succeeded.
98
+ if (chunk.type === 'RUN_ERROR') {
99
+ const message =
100
+ (chunk.error && typeof chunk.error.message === 'string'
101
+ ? chunk.error.message
102
+ : null) ?? 'Summarization failed'
103
+ const code =
104
+ chunk.error && typeof chunk.error.code === 'string'
105
+ ? chunk.error.code
106
+ : undefined
107
+ const err = new Error(message)
108
+ if (code) {
109
+ ;(err as Error & { code?: string }).code = code
110
+ }
111
+ throw err
112
+ }
113
+ }
114
+ } catch (error: unknown) {
115
+ // Narrow before logging: raw SDK errors can carry request metadata
116
+ // (including auth headers) which we must never surface to user loggers.
117
+ options.logger.errors(`${this.name}.summarize fatal`, {
118
+ error: toRunErrorPayload(error, `${this.name}.summarize failed`),
119
+ source: `${this.name}.summarize`,
120
+ })
121
+ throw error
122
+ }
123
+
124
+ return { id, model, summary, usage }
125
+ }
126
+
127
+ async *summarizeStream(
128
+ options: SummarizationOptions<TProviderOptions>,
129
+ ): AsyncIterable<StreamChunk> {
130
+ const systemPrompt = this.buildSummarizationPrompt(options)
131
+
132
+ options.logger.request(
133
+ `activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,
134
+ { provider: this.name, model: options.model },
135
+ )
136
+
137
+ const id = this.generateId()
138
+ let summary = ''
139
+ let model = options.model
140
+ let usage: SummarizationResult['usage'] = {
141
+ promptTokens: 0,
142
+ completionTokens: 0,
143
+ totalTokens: 0,
144
+ }
145
+
146
+ try {
147
+ for await (const chunk of this.textAdapter.chatStream(
148
+ this.buildTextOptions(options, systemPrompt),
149
+ )) {
150
+ // Accumulate the same way `summarize()` does so consumers see deltas
151
+ // AND the terminal `generation:result` event below carries the same
152
+ // final summary that non-streaming returns.
153
+ if (chunk.type === 'TEXT_MESSAGE_CONTENT') {
154
+ if (chunk.content) {
155
+ summary = chunk.content
156
+ } else if (chunk.delta) {
157
+ summary += chunk.delta
158
+ }
159
+ if (chunk.model) model = chunk.model
160
+ }
161
+
162
+ // Emit the GenerationClient-shaped result event just before the
163
+ // terminal RUN_FINISHED so subscribers (useSummarize) populate
164
+ // `result` before flipping `status` to success.
165
+ if (chunk.type === 'RUN_FINISHED') {
166
+ if (chunk.usage) usage = chunk.usage
167
+ if (chunk.model) model = chunk.model
168
+ yield {
169
+ type: EventType.CUSTOM,
170
+ name: 'generation:result',
171
+ value: { id, model, summary, usage } satisfies SummarizationResult,
172
+ model,
173
+ timestamp: Date.now(),
174
+ }
175
+ }
176
+
177
+ yield chunk
178
+ }
179
+ } catch (error: unknown) {
180
+ options.logger.errors(`${this.name}.summarizeStream fatal`, {
181
+ error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`),
182
+ source: `${this.name}.summarizeStream`,
183
+ })
184
+ throw error
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Build the TextOptions passed to the underlying chatStream. Provider
190
+ * `modelOptions` from the summarize call are forwarded as-is so knobs like
191
+ * Anthropic cache headers, Gemini safety settings, or Ollama tuning params
192
+ * still reach the wire layer.
193
+ */
194
+ protected buildTextOptions(
195
+ options: SummarizationOptions<TProviderOptions>,
196
+ systemPrompt: string,
197
+ ): TextOptions<TProviderOptions> {
198
+ return {
199
+ model: options.model,
200
+ messages: [{ role: 'user', content: options.text }],
201
+ systemPrompts: [systemPrompt],
202
+ maxTokens: options.maxLength,
203
+ temperature: 0.3,
204
+ modelOptions: options.modelOptions,
205
+ logger: options.logger,
206
+ }
207
+ }
208
+
209
+ protected buildSummarizationPrompt(
210
+ options: SummarizationOptions<TProviderOptions>,
211
+ ): string {
212
+ let prompt = 'You are a professional summarizer. '
213
+
214
+ switch (options.style) {
215
+ case 'bullet-points':
216
+ prompt += 'Provide a summary in bullet point format. '
217
+ break
218
+ case 'paragraph':
219
+ prompt += 'Provide a summary in paragraph format. '
220
+ break
221
+ case 'concise':
222
+ prompt += 'Provide a very concise summary in 1-2 sentences. '
223
+ break
224
+ default:
225
+ prompt += 'Provide a clear and concise summary. '
226
+ }
227
+
228
+ if (options.focus && options.focus.length > 0) {
229
+ prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `
230
+ }
231
+
232
+ if (options.maxLength) {
233
+ prompt += `Keep the summary under ${options.maxLength} tokens. `
234
+ }
235
+
236
+ return prompt
237
+ }
238
+ }
@@ -11,11 +11,7 @@ import { resolveDebugOption } from '../../logger/resolve'
11
11
  import type { InternalLogger } from '../../logger/internal-logger'
12
12
  import type { DebugOption } from '../../logger/types'
13
13
  import type { SummarizeAdapter } from './adapter'
14
- import type {
15
- StreamChunk,
16
- SummarizationOptions,
17
- SummarizationResult,
18
- } from '../../types'
14
+ import type { StreamChunk, SummarizationResult } from '../../types'
19
15
 
20
16
  // ===========================
21
17
  // Activity Kind
@@ -184,7 +180,7 @@ export function summarize<
184
180
  async function runSummarize(
185
181
  options: SummarizeActivityOptions<SummarizeAdapter<string, object>, false>,
186
182
  ): Promise<SummarizationResult> {
187
- const { adapter, text, maxLength, style, focus } = options
183
+ const { adapter, text, maxLength, style, focus, modelOptions } = options
188
184
  const model = adapter.model
189
185
  const requestId = createId('summarize')
190
186
  const inputLength = text.length
@@ -205,12 +201,13 @@ async function runSummarize(
205
201
  inputLength,
206
202
  })
207
203
 
208
- const summarizeOptions: SummarizationOptions = {
204
+ const summarizeOptions = {
209
205
  model,
210
206
  text,
211
207
  maxLength,
212
208
  style,
213
209
  focus,
210
+ modelOptions,
214
211
  logger,
215
212
  }
216
213
 
@@ -253,7 +250,7 @@ async function runSummarize(
253
250
  async function* runStreamingSummarize(
254
251
  options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>,
255
252
  ): AsyncIterable<StreamChunk> {
256
- const { adapter, text, maxLength, style, focus } = options
253
+ const { adapter, text, maxLength, style, focus, modelOptions } = options
257
254
  const model = adapter.model
258
255
  const logger: InternalLogger = resolveDebugOption(options.debug)
259
256
 
@@ -263,12 +260,13 @@ async function* runStreamingSummarize(
263
260
  stream: true,
264
261
  })
265
262
 
266
- const summarizeOptions: SummarizationOptions = {
263
+ const summarizeOptions = {
267
264
  model,
268
265
  text,
269
266
  maxLength,
270
267
  style,
271
268
  focus,
269
+ modelOptions,
272
270
  logger,
273
271
  }
274
272
 
@@ -313,3 +311,8 @@ export type {
313
311
  AnySummarizeAdapter,
314
312
  } from './adapter'
315
313
  export { BaseSummarizeAdapter } from './adapter'
314
+ export {
315
+ ChatStreamSummarizeAdapter,
316
+ type ChatStreamCapable,
317
+ type InferTextProviderOptions,
318
+ } from './chat-stream-summarize'
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
  /**
@@ -1049,6 +1057,106 @@ export interface CustomEvent extends AGUICustomEvent {
1049
1057
  model?: string
1050
1058
  }
1051
1059
 
1060
+ /**
1061
+ * Final event of a streaming structured-output run. Carries the validated
1062
+ * `object` (typed as `T` after the orchestrator runs Standard Schema parsing),
1063
+ * the `raw` JSON text that produced it, and — for thinking/reasoning models —
1064
+ * the accumulated reasoning text. Adapters emit this with `T = unknown`; the
1065
+ * chat orchestrator narrows to the schema's inferred type after validation.
1066
+ *
1067
+ * `reasoning` is `undefined` when the model produced none (most non-thinking
1068
+ * models) and when the underlying adapter doesn't expose reasoning streams.
1069
+ *
1070
+ * `name` is a string literal so consumers can narrow directly:
1071
+ *
1072
+ * ```ts
1073
+ * if (chunk.type === 'CUSTOM' && chunk.name === 'structured-output.complete') {
1074
+ * chunk.value.object // typed as T
1075
+ * }
1076
+ * ```
1077
+ */
1078
+ export interface StructuredOutputCompleteEvent<T = unknown> extends Omit<
1079
+ CustomEvent,
1080
+ 'name' | 'value'
1081
+ > {
1082
+ name: 'structured-output.complete'
1083
+ value: { object: T; raw: string; reasoning?: string }
1084
+ }
1085
+
1086
+ /**
1087
+ * Emitted when a server tool requires approval before execution. The agent
1088
+ * loop yields this and pauses — `structured-output.complete` will not fire
1089
+ * for that run. The shape is fixed by the orchestrator's tool-approval flow
1090
+ * (see `buildApprovalChunks` in `activities/chat/index.ts`).
1091
+ */
1092
+ export interface ApprovalRequestedEvent extends Omit<
1093
+ CustomEvent,
1094
+ 'name' | 'value'
1095
+ > {
1096
+ name: 'approval-requested'
1097
+ value: {
1098
+ toolCallId: string
1099
+ toolName: string
1100
+ input: unknown
1101
+ approval: { id: string; needsApproval: true }
1102
+ }
1103
+ }
1104
+
1105
+ /**
1106
+ * Emitted when a client tool is invoked. The agent loop yields this and
1107
+ * pauses to let the caller run the tool client-side — `structured-output.complete`
1108
+ * will not fire for that run. Shape fixed by `buildClientToolChunks` in
1109
+ * `activities/chat/index.ts`.
1110
+ */
1111
+ export interface ToolInputAvailableEvent extends Omit<
1112
+ CustomEvent,
1113
+ 'name' | 'value'
1114
+ > {
1115
+ name: 'tool-input-available'
1116
+ value: {
1117
+ toolCallId: string
1118
+ toolName: string
1119
+ input: unknown
1120
+ }
1121
+ }
1122
+
1123
+ /**
1124
+ * Public type for streams returned by `chat({ outputSchema, stream: true })`.
1125
+ *
1126
+ * Yields all standard `StreamChunk` lifecycle events plus the three tagged
1127
+ * `CUSTOM` events the orchestrator can emit through this path:
1128
+ * - `structured-output.complete` — terminal event with typed `value.object: T`
1129
+ * - `approval-requested` — server tool needs approval (pauses the run)
1130
+ * - `tool-input-available` — client tool invocation (pauses the run)
1131
+ *
1132
+ * Each variant has a literal `name`, so a single discriminated narrow gives
1133
+ * you a typed `value` with no helper or cast:
1134
+ *
1135
+ * ```ts
1136
+ * for await (const chunk of stream) {
1137
+ * if (chunk.type === 'CUSTOM' && chunk.name === 'structured-output.complete') {
1138
+ * chunk.value.object // typed as T
1139
+ * } else if (chunk.type === 'CUSTOM' && chunk.name === 'approval-requested') {
1140
+ * chunk.value.toolCallId // typed as string
1141
+ * }
1142
+ * }
1143
+ * ```
1144
+ *
1145
+ * Caveat: tools can emit arbitrary user-defined custom events via the
1146
+ * `emitCustomEvent(name, value)` context API. Those flow through this stream
1147
+ * at runtime but are intentionally absent from this type — including a bare
1148
+ * `CustomEvent` (whose `value: any` would poison the union) would collapse
1149
+ * `chunk.value` back to `any` after the narrow. If you rely on
1150
+ * `emitCustomEvent` plus `outputSchema + stream: true`, branch on `CUSTOM`
1151
+ * outside the literal-`name` narrows or cast explicitly.
1152
+ */
1153
+ export type StructuredOutputStream<T = unknown> = AsyncIterable<
1154
+ | Exclude<StreamChunk, CustomEvent>
1155
+ | StructuredOutputCompleteEvent<T>
1156
+ | ApprovalRequestedEvent
1157
+ | ToolInputAvailableEvent
1158
+ >
1159
+
1052
1160
  // ============================================================================
1053
1161
  // AG-UI Reasoning Event Interfaces
1054
1162
  // ============================================================================
@@ -1171,12 +1279,16 @@ export interface TextCompletionChunk {
1171
1279
  }
1172
1280
  }
1173
1281
 
1174
- export interface SummarizationOptions {
1282
+ export interface SummarizationOptions<
1283
+ TProviderOptions extends object = Record<string, unknown>,
1284
+ > {
1175
1285
  model: string
1176
1286
  text: string
1177
1287
  maxLength?: number
1178
1288
  style?: 'bullet-points' | 'paragraph' | 'concise'
1179
1289
  focus?: Array<string>
1290
+ /** Provider-specific options forwarded by the summarize() activity. */
1291
+ modelOptions?: TProviderOptions
1180
1292
  /**
1181
1293
  * Internal logger threaded from the summarize() entry point. Adapters must
1182
1294
  * call logger.request() before the SDK call and logger.errors() in catch blocks.
@@ -1205,7 +1317,7 @@ export interface SummarizationResult {
1205
1317
  */
1206
1318
  export interface ImageGenerationOptions<
1207
1319
  TProviderOptions extends object = object,
1208
- TSize extends string = string,
1320
+ TSize extends string | undefined = string,
1209
1321
  > {
1210
1322
  /** The model to use for image generation */
1211
1323
  model: string
@@ -1335,7 +1447,7 @@ export interface AudioGenerationResult {
1335
1447
  */
1336
1448
  export interface VideoGenerationOptions<
1337
1449
  TProviderOptions extends object = object,
1338
- TSize extends string = string,
1450
+ TSize extends string | undefined = string,
1339
1451
  > {
1340
1452
  /** The model to use for video generation */
1341
1453
  model: string