@tanstack/ai 0.57.0 → 0.59.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 (95) hide show
  1. package/dist/esm/activities/chat/agents/define-agent.d.ts +81 -0
  2. package/dist/esm/activities/chat/agents/define-agent.js +34 -0
  3. package/dist/esm/activities/chat/agents/define-agent.js.map +1 -0
  4. package/dist/esm/activities/chat/agents/route.d.ts +53 -0
  5. package/dist/esm/activities/chat/agents/route.js +59 -0
  6. package/dist/esm/activities/chat/agents/route.js.map +1 -0
  7. package/dist/esm/activities/chat/agents/spawn.d.ts +124 -0
  8. package/dist/esm/activities/chat/agents/spawn.js +490 -0
  9. package/dist/esm/activities/chat/agents/spawn.js.map +1 -0
  10. package/dist/esm/activities/chat/agents/turn.d.ts +36 -0
  11. package/dist/esm/activities/chat/agents/turn.js +78 -0
  12. package/dist/esm/activities/chat/agents/turn.js.map +1 -0
  13. package/dist/esm/activities/chat/index.d.ts +13 -3
  14. package/dist/esm/activities/chat/index.js +345 -19
  15. package/dist/esm/activities/chat/index.js.map +1 -1
  16. package/dist/esm/activities/chat/messages.d.ts +7 -1
  17. package/dist/esm/activities/chat/messages.js +94 -18
  18. package/dist/esm/activities/chat/messages.js.map +1 -1
  19. package/dist/esm/activities/chat/middleware/run-store.d.ts +43 -7
  20. package/dist/esm/activities/chat/middleware/run-store.js +8 -1
  21. package/dist/esm/activities/chat/middleware/run-store.js.map +1 -1
  22. package/dist/esm/activities/chat/middleware/types.d.ts +50 -3
  23. package/dist/esm/activities/chat/middleware/types.js.map +1 -1
  24. package/dist/esm/activities/chat/stream/processor.d.ts +46 -0
  25. package/dist/esm/activities/chat/stream/processor.js +280 -7
  26. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  27. package/dist/esm/activities/chat/tools/tool-calls.d.ts +17 -3
  28. package/dist/esm/activities/chat/tools/tool-calls.js +56 -7
  29. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  30. package/dist/esm/activities/generateAudio/index.js +1 -1
  31. package/dist/esm/activities/generateImage/index.js +1 -1
  32. package/dist/esm/activities/generateLiveVideo/index.js +1 -1
  33. package/dist/esm/activities/generateSpeech/index.js +1 -1
  34. package/dist/esm/activities/generateTranscription/index.js +1 -1
  35. package/dist/esm/activities/generateVoice/index.js +1 -1
  36. package/dist/esm/activities/generateWorld/index.js +1 -1
  37. package/dist/esm/activities/index.d.ts +3 -0
  38. package/dist/esm/activities/index.js +5 -3
  39. package/dist/esm/activities/summarize/index.js +1 -1
  40. package/dist/esm/client.d.ts +5 -36
  41. package/dist/esm/client.js +4 -37
  42. package/dist/esm/client.js.map +1 -1
  43. package/dist/esm/index.d.ts +5 -0
  44. package/dist/esm/index.js +7 -4
  45. package/dist/esm/middlewares/content-guard.js.map +1 -1
  46. package/dist/esm/stream-to-response.js +12 -6
  47. package/dist/esm/stream-to-response.js.map +1 -1
  48. package/dist/esm/strip-to-spec-middleware.js +2 -1
  49. package/dist/esm/strip-to-spec-middleware.js.map +1 -1
  50. package/dist/esm/types.d.ts +128 -89
  51. package/dist/esm/utilities/adapter-yield-chunk.d.ts +5 -1
  52. package/dist/esm/utilities/ag-ui-usage.d.ts +9 -9
  53. package/dist/esm/utilities/ag-ui-usage.js +66 -3
  54. package/dist/esm/utilities/ag-ui-usage.js.map +1 -1
  55. package/dist/esm/utilities/ag-ui-wire.js +56 -0
  56. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  57. package/dist/esm/utilities/durability-batch.d.ts +8 -0
  58. package/dist/esm/utilities/durability-batch.js +45 -0
  59. package/dist/esm/utilities/durability-batch.js.map +1 -0
  60. package/dist/esm/utilities/normalize-stream-chunk.js +7 -2
  61. package/dist/esm/utilities/normalize-stream-chunk.js.map +1 -1
  62. package/dist/esm/utilities/spec-event-keys.js +13 -8
  63. package/dist/esm/utilities/spec-event-keys.js.map +1 -1
  64. package/dist/esm/utilities/subagent-wire.d.ts +36 -0
  65. package/dist/esm/utilities/subagent-wire.js +131 -0
  66. package/dist/esm/utilities/subagent-wire.js.map +1 -0
  67. package/package.json +4 -4
  68. package/skills/ai-core/adapter-configuration/references/grok-adapter.md +1 -1
  69. package/skills/ai-core/media-generation/SKILL.md +2 -2
  70. package/skills/ai-core/middleware/SKILL.md +7 -4
  71. package/src/activities/chat/agents/define-agent.ts +121 -0
  72. package/src/activities/chat/agents/route.ts +115 -0
  73. package/src/activities/chat/agents/spawn.ts +806 -0
  74. package/src/activities/chat/agents/turn.ts +151 -0
  75. package/src/activities/chat/index.ts +540 -16
  76. package/src/activities/chat/messages.ts +125 -15
  77. package/src/activities/chat/middleware/run-store.ts +56 -7
  78. package/src/activities/chat/middleware/types.ts +55 -2
  79. package/src/activities/chat/stream/processor.ts +428 -8
  80. package/src/activities/chat/tools/tool-calls.ts +92 -16
  81. package/src/activities/index.ts +15 -0
  82. package/src/client.ts +22 -35
  83. package/src/index.ts +23 -0
  84. package/src/middlewares/content-guard.ts +7 -5
  85. package/src/stream-to-response.ts +16 -6
  86. package/src/strip-to-spec-middleware.ts +2 -1
  87. package/src/types.ts +177 -95
  88. package/src/utilities/adapter-yield-chunk.ts +10 -2
  89. package/src/utilities/ag-ui-usage.test.ts +38 -0
  90. package/src/utilities/ag-ui-usage.ts +98 -11
  91. package/src/utilities/ag-ui-wire.ts +74 -0
  92. package/src/utilities/durability-batch.ts +48 -0
  93. package/src/utilities/normalize-stream-chunk.ts +10 -2
  94. package/src/utilities/spec-event-keys.ts +34 -7
  95. package/src/utilities/subagent-wire.ts +184 -0
package/src/types.ts CHANGED
@@ -19,12 +19,22 @@ import type {
19
19
  UsageCostBreakdown,
20
20
  } from '@tanstack/ai-event-client'
21
21
  import type {
22
+ ActivityDeltaEvent as AGUIActivityDeltaEvent,
23
+ ActivitySnapshotEvent as AGUIActivitySnapshotEvent,
24
+ AudioPart as AGUIAudioPart,
22
25
  BaseEvent as AGUIBaseEvent,
26
+ ContentPart as AGUIContentPart,
23
27
  CustomEvent as AGUICustomEvent,
28
+ DataSource as AGUIDataSource,
29
+ DocumentPart as AGUIDocumentPart,
30
+ FileSource as AGUIFileSource,
31
+ ImagePart as AGUIImagePart,
24
32
  Interrupt as AGUIInterrupt,
25
33
  MessagesSnapshotEvent as AGUIMessagesSnapshotEvent,
26
34
  ReasoningEncryptedValueEvent as AGUIReasoningEncryptedValueEvent,
27
35
  ReasoningEndEvent as AGUIReasoningEndEvent,
36
+ RawEvent as AGUIRawEvent,
37
+ ReasoningMessageChunkEvent as AGUIReasoningMessageChunkEvent,
28
38
  ReasoningMessageContentEvent as AGUIReasoningMessageContentEvent,
29
39
  ReasoningMessageEndEvent as AGUIReasoningMessageEndEvent,
30
40
  ReasoningMessageStartEvent as AGUIReasoningMessageStartEvent,
@@ -38,19 +48,31 @@ import type {
38
48
  StateSnapshotEvent as AGUIStateSnapshotEvent,
39
49
  StepFinishedEvent as AGUIStepFinishedEvent,
40
50
  StepStartedEvent as AGUIStepStartedEvent,
51
+ SubagentErrorEvent as AGUISubagentErrorEvent,
52
+ SubagentFinishedEvent as AGUISubagentFinishedEvent,
53
+ SubagentFinishedSuspendedOutcome as AGUISubagentFinishedSuspendedOutcome,
54
+ SubagentInfo as AGUISubagentInfo,
55
+ SubagentRunId as AGUISubagentRunId,
56
+ SubagentStartedEvent as AGUISubagentStartedEvent,
57
+ TextMessageChunkEvent as AGUITextMessageChunkEvent,
41
58
  TextMessageContentEvent as AGUITextMessageContentEvent,
42
59
  TextMessageEndEvent as AGUITextMessageEndEvent,
43
60
  TextMessageStartEvent as AGUITextMessageStartEvent,
61
+ ToolCall as AGUIToolCall,
44
62
  ToolCallArgsEvent as AGUIToolCallArgsEvent,
63
+ ToolCallChunkEvent as AGUIToolCallChunkEvent,
45
64
  ToolCallEndEvent as AGUIToolCallEndEvent,
46
65
  ToolCallResultEvent as AGUIToolCallResultEvent,
47
66
  ToolCallStartEvent as AGUIToolCallStartEvent,
67
+ UrlSource as AGUIUrlSource,
68
+ VideoPart as AGUIVideoPart,
48
69
  EventType,
49
70
  } from '@ag-ui/core'
50
71
  import type {
51
72
  SpecTokenUsage,
52
73
  TokenUsageLeftover,
53
74
  } from './utilities/ag-ui-usage'
75
+ import type { SubagentWireInfo } from './utilities/subagent-wire'
54
76
 
55
77
  // Re-export ProviderTool so the type is reachable from `@tanstack/ai`'s root
56
78
  // entry via `export * from './types'` without forcing the subpath import.
@@ -163,13 +185,11 @@ export type InferSchemaType<T> =
163
185
  ? TInput
164
186
  : unknown
165
187
 
166
- export interface ToolCall<TMetadata = unknown> {
167
- id: string
168
- type: 'function'
169
- function: {
170
- name: string
171
- arguments: string // JSON string
172
- }
188
+ /** AG-UI `ToolCall` with typed metadata. `function.arguments` is a JSON string. */
189
+ export interface ToolCall<TMetadata = unknown> extends Omit<
190
+ AGUIToolCall,
191
+ 'metadata'
192
+ > {
173
193
  /** Provider-specific metadata to carry through the tool call lifecycle.
174
194
  * Typed per-adapter via `TToolCallMetadata`. For example,
175
195
  * `@tanstack/ai-gemini` sets this to `{ thoughtSignature?: string }`. */
@@ -201,106 +221,68 @@ export interface ProviderExecutedToolMetadata {
201
221
  // ============================================================================
202
222
 
203
223
  /**
204
- * Supported input modality types for multimodal content.
205
- * - 'text': Plain text content
206
- * - 'image': Image content (base64 or URL)
207
- * - 'audio': Audio content (base64 or URL)
208
- * - 'video': Video content (base64 or URL)
209
- * - 'document': Document content like PDFs (base64 or URL)
224
+ * Supported input modality types for multimodal content: the `type` of each
225
+ * AG-UI `ContentPart` (text, image, audio, video, document).
210
226
  */
211
- export type Modality = 'text' | 'image' | 'audio' | 'video' | 'document'
227
+ export type Modality = AGUIContentPart['type']
212
228
 
213
229
  /**
214
- * Source specification for inline data content (base64).
215
- * Requires a mimeType to ensure providers receive proper content type information.
230
+ * Inline base64 content. AG-UI `DataSource`: `mimeType` is required.
216
231
  */
217
- export interface ContentPartDataSource {
218
- /**
219
- * Indicates this is inline data content.
220
- */
221
- type: 'data'
222
- /**
223
- * The base64-encoded content value.
224
- */
225
- value: string
226
- /**
227
- * The MIME type of the content (e.g., 'image/png', 'audio/wav').
228
- * Required for data sources to ensure proper handling by providers.
229
- */
230
- mimeType: string
231
- }
232
+ export interface ContentPartDataSource extends AGUIDataSource {}
232
233
 
233
234
  /**
234
- * Source specification for URL-based content.
235
- * mimeType is optional as it can often be inferred from the URL or response headers.
235
+ * URL-referenced content. AG-UI `UrlSource`: `mimeType` is optional.
236
236
  */
237
- export interface ContentPartUrlSource {
238
- /**
239
- * Indicates this is URL-referenced content.
240
- */
241
- type: 'url'
242
- /**
243
- * HTTP(S) URL or data URI pointing to the content.
244
- */
245
- value: string
246
- /**
247
- * Optional MIME type hint for cases where providers can't infer it from the URL.
248
- */
249
- mimeType?: string
250
- }
237
+ export interface ContentPartUrlSource extends AGUIUrlSource {}
238
+
239
+ /**
240
+ * A provider-issued file handle (Files API). AG-UI `FileSource`: the handle
241
+ * is opaque, do not fetch or parse it.
242
+ */
243
+ export interface ContentPartFileSource extends AGUIFileSource {}
251
244
 
252
245
  /**
253
- * Source specification for multimodal content.
254
- * Discriminated union supporting both inline data (base64) and URL-based content.
255
- * - For 'data' sources: mimeType is required
256
- * - For 'url' sources: mimeType is optional
246
+ * Where a media part's bytes come from: inline data, a URL, or a provider
247
+ * file handle. Same members as AG-UI `PartSource`.
257
248
  */
258
- export type ContentPartSource = ContentPartDataSource | ContentPartUrlSource
249
+ export type ContentPartSource =
250
+ | ContentPartDataSource
251
+ | ContentPartUrlSource
252
+ | ContentPartFileSource
259
253
 
260
254
  /**
261
- * Image content part for multimodal messages.
255
+ * Image content part for multimodal messages. AG-UI `ImagePart` with typed metadata.
262
256
  * @template TMetadata - Provider-specific metadata type (e.g., OpenAI's detail level)
263
257
  */
264
- export interface ImagePart<TMetadata = unknown> {
265
- type: 'image'
266
- /** Source of the image content */
267
- source: ContentPartSource
258
+ export interface ImagePart<TMetadata = unknown> extends AGUIImagePart {
268
259
  /** Provider-specific metadata (e.g., OpenAI's detail: 'auto' | 'low' | 'high') */
269
260
  metadata?: TMetadata
270
261
  }
271
262
 
272
263
  /**
273
- * Audio content part for multimodal messages.
264
+ * Audio content part for multimodal messages. AG-UI `AudioPart` with typed metadata.
274
265
  * @template TMetadata - Provider-specific metadata type
275
266
  */
276
- export interface AudioPart<TMetadata = unknown> {
277
- type: 'audio'
278
- /** Source of the audio content */
279
- source: ContentPartSource
267
+ export interface AudioPart<TMetadata = unknown> extends AGUIAudioPart {
280
268
  /** Provider-specific metadata (e.g., format, sample rate) */
281
269
  metadata?: TMetadata
282
270
  }
283
271
 
284
272
  /**
285
- * Video content part for multimodal messages.
273
+ * Video content part for multimodal messages. AG-UI `VideoPart` with typed metadata.
286
274
  * @template TMetadata - Provider-specific metadata type
287
275
  */
288
- export interface VideoPart<TMetadata = unknown> {
289
- type: 'video'
290
- /** Source of the video content */
291
- source: ContentPartSource
276
+ export interface VideoPart<TMetadata = unknown> extends AGUIVideoPart {
292
277
  /** Provider-specific metadata (e.g., duration, resolution) */
293
278
  metadata?: TMetadata
294
279
  }
295
280
 
296
281
  /**
297
- * Document content part for multimodal messages (e.g., PDFs).
282
+ * Document content part for multimodal messages (e.g., PDFs). AG-UI `DocumentPart` with typed metadata.
298
283
  * @template TMetadata - Provider-specific metadata type (e.g., Anthropic's media_type)
299
284
  */
300
- export interface DocumentPart<TMetadata = unknown> {
301
- type: 'document'
302
- /** Source of the document content */
303
- source: ContentPartSource
285
+ export interface DocumentPart<TMetadata = unknown> extends AGUIDocumentPart {
304
286
  /** Provider-specific metadata (e.g., media_type for PDFs) */
305
287
  metadata?: TMetadata
306
288
  }
@@ -492,6 +474,37 @@ export interface StructuredOutputPart<TData = unknown> {
492
474
  errorMessage?: string
493
475
  }
494
476
 
477
+ export type SubagentStatus = 'running' | 'finished' | 'error' | 'suspended'
478
+
479
+ /**
480
+ * One child invocation as the client sees it. AG-UI `SubagentInfo` names the
481
+ * child; the other AG-UI fields come from its `SUBAGENT_STARTED`,
482
+ * `SUBAGENT_FINISHED` and `SUBAGENT_ERROR` events. `id` is the AG-UI
483
+ * `subagentRunId`. `status`, `parentRunId` and `messages` are client state the
484
+ * spec does not model.
485
+ */
486
+ export interface SubagentHandleData
487
+ extends
488
+ AGUISubagentInfo,
489
+ Pick<
490
+ AGUISubagentStartedEvent,
491
+ 'parentSubagentRunId' | 'parentToolCallId' | 'metadata'
492
+ > {
493
+ id: AGUISubagentRunId
494
+ status: SubagentStatus
495
+ /** The parent chat run that started this child. */
496
+ parentRunId?: string
497
+ /** Interrupts this child raised, while `status` is `'suspended'`. */
498
+ interruptIds?: AGUISubagentFinishedSuspendedOutcome['interruptIds']
499
+ messages: Array<UIMessage>
500
+ error?: Pick<AGUISubagentErrorEvent, 'message' | 'code'>
501
+ }
502
+
503
+ export interface SubagentPart {
504
+ type: 'subagent'
505
+ subagent: SubagentHandleData
506
+ }
507
+
495
508
  export interface UIResourcePart {
496
509
  type: 'ui-resource'
497
510
  /** The ui:// resource object in MCP-native shape — fed straight to the renderer. */
@@ -520,6 +533,7 @@ export type MessagePart<TData = unknown> =
520
533
  | ThinkingPart
521
534
  | StructuredOutputPart<TData>
522
535
  | UIResourcePart
536
+ | SubagentPart
523
537
 
524
538
  /**
525
539
  * Shape of `metadata.tanstack` on a message.
@@ -528,6 +542,10 @@ export type MessagePart<TData = unknown> =
528
542
  export interface TanStackMessageMetadata {
529
543
  createdAt?: string
530
544
  model?: string
545
+ /** Parent chat run that produced this assistant message. */
546
+ runId?: string
547
+ /** Card data on a child wire message. See `uiMessagesToWire`. */
548
+ subagent?: SubagentWireInfo
531
549
  /** Thinking signature for a `role: 'reasoning'` fan-out message. */
532
550
  signature?: string
533
551
  /** Per-tool-call provider metadata keyed by tool call id (e.g. Gemini thoughtSignature). */
@@ -629,6 +647,22 @@ type RuntimeContextField<TContext> =
629
647
  context: TContext
630
648
  }
631
649
 
650
+ /**
651
+ * Options for a single `emitCustomEvent` call, on both the tool-execution and
652
+ * middleware contexts.
653
+ */
654
+ export interface EmitCustomEventOptions {
655
+ /**
656
+ * Keep this event in the durability batch with later chunks.
657
+ * CUSTOM events flush as soon as they are emitted, so a progress
658
+ * indicator can render at emit time. Pass `{ batch: true }` for a
659
+ * high-volume stream that should share appends with later output.
660
+ * `process.stdout`, `process.stderr`, `sandbox.file`, and
661
+ * `sandbox.file.diff` already batch.
662
+ */
663
+ batch?: boolean
664
+ }
665
+
632
666
  /**
633
667
  * Context passed to tool execute functions, providing capabilities like
634
668
  * emitting custom events during execution.
@@ -649,6 +683,8 @@ export type ToolExecutionContext<TContext = unknown> =
649
683
  *
650
684
  * @param eventName - Name of the custom event
651
685
  * @param value - Event payload value
686
+ * @param options - Pass `{ batch: true }` to keep this event in the
687
+ * durability batch instead of flushing it immediately
652
688
  *
653
689
  * @example
654
690
  * ```ts
@@ -661,7 +697,11 @@ export type ToolExecutionContext<TContext = unknown> =
661
697
  * })
662
698
  * ```
663
699
  */
664
- emitCustomEvent: (eventName: string, value: Record<string, any>) => void
700
+ emitCustomEvent: (
701
+ eventName: string,
702
+ value: Record<string, any>,
703
+ options?: EmitCustomEventOptions,
704
+ ) => void
665
705
  }
666
706
 
667
707
  export type ToolExecuteFunction<
@@ -1091,6 +1131,12 @@ export interface TextOptions<
1091
1131
  * Surfaced for observability/middleware; not consumed by the LLM call.
1092
1132
  */
1093
1133
  parentRunId?: string
1134
+ /**
1135
+ * AG-UI subagent run id when this chat runs as a child of another run.
1136
+ * A child `chat()` passes `ctx.subagentRunId`. Middleware reads it as
1137
+ * `ctx.subagentRunId`. Absent on a top-level run.
1138
+ */
1139
+ subagentRunId?: string
1094
1140
 
1095
1141
  /** Application state mirrored in a STATE_SNAPSHOT before an interrupt terminal. */
1096
1142
  state?: unknown
@@ -1263,15 +1309,12 @@ export interface TextMessageEndEvent extends AGUITextMessageEndEvent {}
1263
1309
  /**
1264
1310
  * Emitted when a tool call starts.
1265
1311
  *
1266
- * @ag-ui/core provides: `toolCallId`, `toolCallName`, `parentMessageId?`
1267
- *
1268
- * Field shapes are taken from AG-UI via `Pick` (not `extends`) so Zod
1269
- * `.passthrough()` index signatures do not pollute the StreamChunk
1270
- * discriminated union — required for {@link KnownCustomEvent} narrowing.
1312
+ * @ag-ui/core provides: `toolCallId`, `toolCallName`, `parentMessageId?`,
1313
+ * `subagentRunId?`
1271
1314
  */
1272
- export interface ToolCallStartEvent extends Pick<
1315
+ export interface ToolCallStartEvent extends Omit<
1273
1316
  AGUIToolCallStartEvent,
1274
- 'toolCallId' | 'toolCallName' | 'parentMessageId' | 'timestamp' | 'rawEvent'
1317
+ 'type'
1275
1318
  > {
1276
1319
  type: 'TOOL_CALL_START'
1277
1320
  /** Alias of `toolCallName`. Kept so existing stream readers still compile. */
@@ -1290,14 +1333,9 @@ export interface ToolCallArgsEvent extends AGUIToolCallArgsEvent {}
1290
1333
  /**
1291
1334
  * Emitted when a tool call completes.
1292
1335
  *
1293
- * @ag-ui/core provides: `toolCallId`
1294
- *
1295
- * Same `Pick` (not `extends`) rationale as {@link ToolCallStartEvent}.
1336
+ * @ag-ui/core provides: `toolCallId`, `subagentRunId?`
1296
1337
  */
1297
- export interface ToolCallEndEvent extends Pick<
1298
- AGUIToolCallEndEvent,
1299
- 'toolCallId' | 'timestamp' | 'rawEvent'
1300
- > {
1338
+ export interface ToolCallEndEvent extends Omit<AGUIToolCallEndEvent, 'type'> {
1301
1339
  type: 'TOOL_CALL_END'
1302
1340
  /** Parsed tool arguments when the adapter already parsed them. */
1303
1341
  input?: unknown
@@ -1355,15 +1393,9 @@ export interface StateDeltaEvent extends AGUIStateDeltaEvent {}
1355
1393
  /**
1356
1394
  * Custom event for extensibility.
1357
1395
  *
1358
- * @ag-ui/core provides: `name`, `value`
1359
- *
1360
- * Uses `Pick` (not `extends`) so the Zod passthrough index signature does not
1361
- * erase discriminant property access on {@link KnownCustomEvent} unions.
1396
+ * @ag-ui/core provides: `name`, `value`, `subagentRunId?`
1362
1397
  */
1363
- export interface CustomEvent extends Pick<
1364
- AGUICustomEvent,
1365
- 'name' | 'value' | 'timestamp' | 'rawEvent'
1366
- > {
1398
+ export interface CustomEvent extends Omit<AGUICustomEvent, 'type'> {
1367
1399
  type: 'CUSTOM'
1368
1400
  metadata?: Record<string, any>
1369
1401
  }
@@ -1649,6 +1681,24 @@ export interface ReasoningEndEvent extends AGUIReasoningEndEvent {}
1649
1681
  */
1650
1682
  export interface ReasoningEncryptedValueEvent extends AGUIReasoningEncryptedValueEvent {}
1651
1683
 
1684
+ /** AG-UI 1.0 ActivitySnapshotEvent shape. */
1685
+ export interface ActivitySnapshotEvent extends AGUIActivitySnapshotEvent {}
1686
+
1687
+ /** AG-UI 1.0 ActivityDeltaEvent shape. */
1688
+ export interface ActivityDeltaEvent extends AGUIActivityDeltaEvent {}
1689
+
1690
+ /** AG-UI 1.0 RawEvent shape. */
1691
+ export interface RawEvent extends AGUIRawEvent {}
1692
+
1693
+ /** AG-UI 1.0 TextMessageChunkEvent shape. */
1694
+ export interface TextMessageChunkEvent extends AGUITextMessageChunkEvent {}
1695
+
1696
+ /** AG-UI 1.0 ToolCallChunkEvent shape. */
1697
+ export interface ToolCallChunkEvent extends AGUIToolCallChunkEvent {}
1698
+
1699
+ /** AG-UI 1.0 ReasoningMessageChunkEvent shape. */
1700
+ export interface ReasoningMessageChunkEvent extends AGUIReasoningMessageChunkEvent {}
1701
+
1652
1702
  // ============================================================================
1653
1703
  // AG-UI Event Union
1654
1704
  // ============================================================================
@@ -1657,6 +1707,12 @@ export interface ReasoningEncryptedValueEvent extends AGUIReasoningEncryptedValu
1657
1707
  * Union of all AG-UI events.
1658
1708
  */
1659
1709
  export type AGUIEvent =
1710
+ | ActivitySnapshotEvent
1711
+ | ActivityDeltaEvent
1712
+ | RawEvent
1713
+ | TextMessageChunkEvent
1714
+ | ToolCallChunkEvent
1715
+ | ReasoningMessageChunkEvent
1660
1716
  | RunStartedEvent
1661
1717
  | RunFinishedEvent
1662
1718
  | RunErrorEvent
@@ -1679,6 +1735,32 @@ export type AGUIEvent =
1679
1735
  | ReasoningMessageEndEvent
1680
1736
  | ReasoningEndEvent
1681
1737
  | ReasoningEncryptedValueEvent
1738
+ | SubagentStartedEvent
1739
+ | SubagentFinishedEvent
1740
+ | SubagentErrorEvent
1741
+
1742
+ /**
1743
+ * A child agent started. The later chunks for that child carry the same
1744
+ * `subagentRunId`.
1745
+ *
1746
+ * @ag-ui/core provides: `subagentRunId`, `name`, `description?`,
1747
+ * `parentSubagentRunId?`, `parentToolCallId?`, `parentMessageId?`, `metadata?`
1748
+ */
1749
+ export interface SubagentStartedEvent extends AGUISubagentStartedEvent {}
1750
+
1751
+ /**
1752
+ * A child agent's segment of this run ended.
1753
+ *
1754
+ * @ag-ui/core provides: `subagentRunId`, `result?`, `outcome?`
1755
+ */
1756
+ export interface SubagentFinishedEvent extends AGUISubagentFinishedEvent {}
1757
+
1758
+ /**
1759
+ * A child agent failed. The parent run can continue.
1760
+ *
1761
+ * @ag-ui/core provides: `subagentRunId`, `message`, `code?`
1762
+ */
1763
+ export interface SubagentErrorEvent extends AGUISubagentErrorEvent {}
1682
1764
 
1683
1765
  /**
1684
1766
  * Chunk returned by the SDK during streaming chat completions.
@@ -4,11 +4,12 @@ import type { ContentPart, StreamChunk, ToolOutputState } from '../types'
4
4
  /**
5
5
  * Adapter / engine yield before normalize. Public StreamChunk is spec-only.
6
6
  * This type still allows the old extra fields.
7
+ * Extras never override a spec field (see WithAdapterExtras).
7
8
  */
8
- export type AdapterYieldChunk = StreamChunk & {
9
+ type AdapterExtras = {
9
10
  model?: string
10
11
  finishReason?: 'stop' | 'length' | 'content_filter' | 'tool_calls' | null
11
- // `any` so TokenUsage adapter yields stay assignable after public usage[]
12
+ // `any` so TokenUsage adapter yields stay assignable after public usage[].
12
13
  usage?: any
13
14
  content?: string
14
15
  args?: string
@@ -28,3 +29,10 @@ export type AdapterYieldChunk = StreamChunk & {
28
29
  threadId?: string
29
30
  runId?: string
30
31
  }
32
+
33
+ // Preserve spec fields when an adapter extra uses the same name.
34
+ type WithAdapterExtras<T> = T extends StreamChunk
35
+ ? T & Omit<AdapterExtras, keyof T>
36
+ : never
37
+
38
+ export type AdapterYieldChunk = WithAdapterExtras<StreamChunk>
@@ -192,3 +192,41 @@ describe('rebuildTokenUsage', () => {
192
192
  })
193
193
  })
194
194
  })
195
+
196
+ it('maps cache-write usage in both directions', () => {
197
+ const usage: TokenUsage = {
198
+ promptTokens: 12,
199
+ completionTokens: 3,
200
+ totalTokens: 15,
201
+ promptTokensDetails: { cachedTokens: 4, cacheWriteTokens: 5 },
202
+ }
203
+ const wire = toSpecTokenUsage(usage)
204
+ expect(wire.usage).toEqual([
205
+ {
206
+ inputTokens: 12,
207
+ outputTokens: 3,
208
+ totalTokens: 15,
209
+ cachedInputTokens: 4,
210
+ cacheWriteInputTokens: 5,
211
+ },
212
+ ])
213
+ // Old readers only know the leftover field, so it stays there too.
214
+ expect(wire.leftover).toEqual({
215
+ promptTokensDetails: { cacheWriteTokens: 5 },
216
+ })
217
+ expect(fromSpecTokenUsage(wire.usage, wire.leftover)).toEqual(usage)
218
+ expect(fromSpecTokenUsage(wire.usage)).toEqual(usage)
219
+ })
220
+
221
+ it('sums every spec usage entry', () => {
222
+ expect(
223
+ fromSpecTokenUsage([
224
+ { inputTokens: 1, outputTokens: 2, totalTokens: 3 },
225
+ { inputTokens: 4, outputTokens: 5, totalTokens: 9 },
226
+ ]),
227
+ ).toEqual({
228
+ promptTokens: 5,
229
+ completionTokens: 7,
230
+ totalTokens: 12,
231
+ })
232
+ })
@@ -1,15 +1,8 @@
1
+ import type { TokenUsage as SpecTokenUsage } from '@ag-ui/core'
1
2
  import type { TokenUsage } from '../types'
2
3
 
3
4
  /** AG-UI spec `usage[]` item (provider/model labels + token counts only). */
4
- export interface SpecTokenUsage {
5
- provider?: string
6
- model?: string
7
- inputTokens?: number
8
- outputTokens?: number
9
- totalTokens?: number
10
- reasoningTokens?: number
11
- cachedInputTokens?: number
12
- }
5
+ export type { TokenUsage as SpecTokenUsage } from '@ag-ui/core'
13
6
 
14
7
  export interface ToSpecTokenUsageOptions {
15
8
  provider?: string
@@ -68,11 +61,16 @@ export function toSpecTokenUsage(
68
61
  if (cachedInputTokens !== undefined) {
69
62
  spec.cachedInputTokens = cachedInputTokens
70
63
  }
64
+ if (promptTokensDetails?.cacheWriteTokens !== undefined) {
65
+ spec.cacheWriteInputTokens = promptTokensDetails.cacheWriteTokens
66
+ }
71
67
  const reasoningTokens = completionTokensDetails?.reasoningTokens
72
68
  if (reasoningTokens !== undefined) {
73
69
  spec.reasoningTokens = reasoningTokens
74
70
  }
75
71
 
72
+ // cacheWriteTokens stays in the leftover too, so clients built before
73
+ // cacheWriteInputTokens existed still rebuild it.
76
74
  const leftoverPrompt = promptTokensDetails
77
75
  ? definedDetails(withoutKey(promptTokensDetails, 'cachedTokens'))
78
76
  : undefined
@@ -94,6 +92,80 @@ export function toSpecTokenUsage(
94
92
  }
95
93
  }
96
94
 
95
+ function sumNumbers<T extends object>(
96
+ current: T | undefined,
97
+ next: T | undefined,
98
+ ): T | undefined {
99
+ if (!current) return next
100
+ if (!next) return current
101
+ const result = { ...current }
102
+ for (const key of Object.keys(next) as Array<keyof T>) {
103
+ const value = next[key]
104
+ if (typeof value !== 'number') continue
105
+ const previous = current[key]
106
+ result[key] = ((typeof previous === 'number' ? previous : 0) +
107
+ value) as T[keyof T]
108
+ }
109
+ return result
110
+ }
111
+
112
+ function sumOptional(current?: number, next?: number): number | undefined {
113
+ if (current === undefined) return next
114
+ if (next === undefined) return current
115
+ return current + next
116
+ }
117
+
118
+ /**
119
+ * Add two usage totals, as for a parent run and its children. Numbers add
120
+ * up. `billed` adds up only in the same unit. `providerUsageDetails` is
121
+ * opaque, so the latest one stays. `@tanstack/ai-persistence` sums per-run
122
+ * usage with the same rules.
123
+ */
124
+ export function addTokenUsage(
125
+ current: TokenUsage,
126
+ next: TokenUsage,
127
+ ): TokenUsage {
128
+ const promptTokensDetails = sumNumbers(
129
+ current.promptTokensDetails,
130
+ next.promptTokensDetails,
131
+ )
132
+ const completionTokensDetails = sumNumbers(
133
+ current.completionTokensDetails,
134
+ next.completionTokensDetails,
135
+ )
136
+ const costDetails = sumNumbers(current.costDetails, next.costDetails)
137
+ const cost = sumOptional(current.cost, next.cost)
138
+ const durationSeconds = sumOptional(
139
+ current.durationSeconds,
140
+ next.durationSeconds,
141
+ )
142
+ const unitsBilled = sumOptional(current.unitsBilled, next.unitsBilled)
143
+ const billed =
144
+ current.billed && next.billed && current.billed.unit === next.billed.unit
145
+ ? {
146
+ quantity: current.billed.quantity + next.billed.quantity,
147
+ unit: current.billed.unit,
148
+ }
149
+ : (next.billed ?? current.billed)
150
+ const providerUsageDetails =
151
+ next.providerUsageDetails ?? current.providerUsageDetails
152
+ return {
153
+ ...current,
154
+ ...next,
155
+ promptTokens: current.promptTokens + next.promptTokens,
156
+ completionTokens: current.completionTokens + next.completionTokens,
157
+ totalTokens: current.totalTokens + next.totalTokens,
158
+ ...(promptTokensDetails && { promptTokensDetails }),
159
+ ...(completionTokensDetails && { completionTokensDetails }),
160
+ ...(cost !== undefined && { cost }),
161
+ ...(costDetails && { costDetails }),
162
+ ...(durationSeconds !== undefined && { durationSeconds }),
163
+ ...(unitsBilled !== undefined && { unitsBilled }),
164
+ ...(billed && { billed }),
165
+ ...(providerUsageDetails && { providerUsageDetails }),
166
+ }
167
+ }
168
+
97
169
  export function rebuildTokenUsage(
98
170
  usage: unknown,
99
171
  leftover?: TokenUsageLeftover,
@@ -111,8 +183,20 @@ export function fromSpecTokenUsage(
111
183
  usage: ReadonlyArray<SpecTokenUsage> | undefined,
112
184
  leftover?: TokenUsageLeftover,
113
185
  ): TokenUsage | undefined {
114
- const spec = usage?.[0]
115
- if (spec == null && leftover == null) {
186
+ const spec = usage?.reduce<SpecTokenUsage>((total, entry) => {
187
+ for (const key of [
188
+ 'inputTokens',
189
+ 'outputTokens',
190
+ 'totalTokens',
191
+ 'cachedInputTokens',
192
+ 'cacheWriteInputTokens',
193
+ 'reasoningTokens',
194
+ ] as const) {
195
+ if (entry[key] !== undefined) total[key] = (total[key] ?? 0) + entry[key]
196
+ }
197
+ return total
198
+ }, {})
199
+ if ((usage === undefined || usage.length === 0) && leftover == null) {
116
200
  return undefined
117
201
  }
118
202
 
@@ -126,6 +210,9 @@ export function fromSpecTokenUsage(
126
210
  ...(spec?.cachedInputTokens !== undefined
127
211
  ? { cachedTokens: spec.cachedInputTokens }
128
212
  : {}),
213
+ ...(spec?.cacheWriteInputTokens !== undefined
214
+ ? { cacheWriteTokens: spec.cacheWriteInputTokens }
215
+ : {}),
129
216
  ...leftoverPromptDetails,
130
217
  })
131
218
  const completionTokensDetails = definedDetails({