@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
@@ -4,8 +4,9 @@ import { SystemPrompt } from './system-prompts.js';
4
4
  import { CapabilityContext } from './activities/chat/middleware/capabilities.js';
5
5
  import { InterruptSubmissionError } from './interrupts.js';
6
6
  import { BilledUsage, BillingUnit, CompletionTokensDetails, PromptTokensDetails, ProviderUsageDetails, TokenUsage, UsageCostBreakdown } from '@tanstack/ai-event-client';
7
- import { BaseEvent as AGUIBaseEvent, CustomEvent as AGUICustomEvent, Interrupt as AGUIInterrupt, MessagesSnapshotEvent as AGUIMessagesSnapshotEvent, ReasoningEncryptedValueEvent as AGUIReasoningEncryptedValueEvent, ReasoningEndEvent as AGUIReasoningEndEvent, ReasoningMessageContentEvent as AGUIReasoningMessageContentEvent, ReasoningMessageEndEvent as AGUIReasoningMessageEndEvent, ReasoningMessageStartEvent as AGUIReasoningMessageStartEvent, ReasoningStartEvent as AGUIReasoningStartEvent, ResumeEntry as AGUIResumeEntry, RunErrorEvent as AGUIRunErrorEvent, RunFinishedEvent as AGUIRunFinishedEvent, RunFinishedOutcome as AGUIRunFinishedOutcome, RunStartedEvent as AGUIRunStartedEvent, StateDeltaEvent as AGUIStateDeltaEvent, StateSnapshotEvent as AGUIStateSnapshotEvent, StepFinishedEvent as AGUIStepFinishedEvent, StepStartedEvent as AGUIStepStartedEvent, TextMessageContentEvent as AGUITextMessageContentEvent, TextMessageEndEvent as AGUITextMessageEndEvent, TextMessageStartEvent as AGUITextMessageStartEvent, ToolCallArgsEvent as AGUIToolCallArgsEvent, ToolCallEndEvent as AGUIToolCallEndEvent, ToolCallResultEvent as AGUIToolCallResultEvent, ToolCallStartEvent as AGUIToolCallStartEvent, EventType } from '@ag-ui/core';
7
+ import { ActivityDeltaEvent as AGUIActivityDeltaEvent, ActivitySnapshotEvent as AGUIActivitySnapshotEvent, AudioPart as AGUIAudioPart, BaseEvent as AGUIBaseEvent, ContentPart as AGUIContentPart, CustomEvent as AGUICustomEvent, DataSource as AGUIDataSource, DocumentPart as AGUIDocumentPart, FileSource as AGUIFileSource, ImagePart as AGUIImagePart, Interrupt as AGUIInterrupt, MessagesSnapshotEvent as AGUIMessagesSnapshotEvent, ReasoningEncryptedValueEvent as AGUIReasoningEncryptedValueEvent, ReasoningEndEvent as AGUIReasoningEndEvent, RawEvent as AGUIRawEvent, ReasoningMessageChunkEvent as AGUIReasoningMessageChunkEvent, ReasoningMessageContentEvent as AGUIReasoningMessageContentEvent, ReasoningMessageEndEvent as AGUIReasoningMessageEndEvent, ReasoningMessageStartEvent as AGUIReasoningMessageStartEvent, ReasoningStartEvent as AGUIReasoningStartEvent, ResumeEntry as AGUIResumeEntry, RunErrorEvent as AGUIRunErrorEvent, RunFinishedEvent as AGUIRunFinishedEvent, RunFinishedOutcome as AGUIRunFinishedOutcome, RunStartedEvent as AGUIRunStartedEvent, StateDeltaEvent as AGUIStateDeltaEvent, StateSnapshotEvent as AGUIStateSnapshotEvent, StepFinishedEvent as AGUIStepFinishedEvent, StepStartedEvent as AGUIStepStartedEvent, SubagentErrorEvent as AGUISubagentErrorEvent, SubagentFinishedEvent as AGUISubagentFinishedEvent, SubagentFinishedSuspendedOutcome as AGUISubagentFinishedSuspendedOutcome, SubagentInfo as AGUISubagentInfo, SubagentRunId as AGUISubagentRunId, SubagentStartedEvent as AGUISubagentStartedEvent, TextMessageChunkEvent as AGUITextMessageChunkEvent, TextMessageContentEvent as AGUITextMessageContentEvent, TextMessageEndEvent as AGUITextMessageEndEvent, TextMessageStartEvent as AGUITextMessageStartEvent, ToolCall as AGUIToolCall, ToolCallArgsEvent as AGUIToolCallArgsEvent, ToolCallChunkEvent as AGUIToolCallChunkEvent, ToolCallEndEvent as AGUIToolCallEndEvent, ToolCallResultEvent as AGUIToolCallResultEvent, ToolCallStartEvent as AGUIToolCallStartEvent, UrlSource as AGUIUrlSource, VideoPart as AGUIVideoPart, EventType } from '@ag-ui/core';
8
8
  import { SpecTokenUsage, TokenUsageLeftover } from './utilities/ag-ui-usage.js';
9
+ import { SubagentWireInfo } from './utilities/subagent-wire.js';
9
10
  export type { ProviderTool } from './tools/provider-tool.js';
10
11
  /**
11
12
  * Tool call states - track the lifecycle of a tool call
@@ -87,13 +88,8 @@ export type SchemaInput = StandardJSONSchemaV1<any, any> | StandardSchemaV1<any,
87
88
  * JSON Schema at compile time.
88
89
  */
89
90
  export type InferSchemaType<T> = T extends StandardJSONSchemaV1<infer TInput, unknown> ? TInput : T extends StandardSchemaV1<infer TInput, unknown> ? TInput : unknown;
90
- export interface ToolCall<TMetadata = unknown> {
91
- id: string;
92
- type: 'function';
93
- function: {
94
- name: string;
95
- arguments: string;
96
- };
91
+ /** AG-UI `ToolCall` with typed metadata. `function.arguments` is a JSON string. */
92
+ export interface ToolCall<TMetadata = unknown> extends Omit<AGUIToolCall, 'metadata'> {
97
93
  /** Provider-specific metadata to carry through the tool call lifecycle.
98
94
  * Typed per-adapter via `TToolCallMetadata`. For example,
99
95
  * `@tanstack/ai-gemini` sets this to `{ thoughtSignature?: string }`. */
@@ -119,99 +115,60 @@ export interface ProviderExecutedToolMetadata {
119
115
  [key: string]: unknown;
120
116
  }
121
117
  /**
122
- * Supported input modality types for multimodal content.
123
- * - 'text': Plain text content
124
- * - 'image': Image content (base64 or URL)
125
- * - 'audio': Audio content (base64 or URL)
126
- * - 'video': Video content (base64 or URL)
127
- * - 'document': Document content like PDFs (base64 or URL)
118
+ * Supported input modality types for multimodal content: the `type` of each
119
+ * AG-UI `ContentPart` (text, image, audio, video, document).
128
120
  */
129
- export type Modality = 'text' | 'image' | 'audio' | 'video' | 'document';
121
+ export type Modality = AGUIContentPart['type'];
130
122
  /**
131
- * Source specification for inline data content (base64).
132
- * Requires a mimeType to ensure providers receive proper content type information.
123
+ * Inline base64 content. AG-UI `DataSource`: `mimeType` is required.
133
124
  */
134
- export interface ContentPartDataSource {
135
- /**
136
- * Indicates this is inline data content.
137
- */
138
- type: 'data';
139
- /**
140
- * The base64-encoded content value.
141
- */
142
- value: string;
143
- /**
144
- * The MIME type of the content (e.g., 'image/png', 'audio/wav').
145
- * Required for data sources to ensure proper handling by providers.
146
- */
147
- mimeType: string;
125
+ export interface ContentPartDataSource extends AGUIDataSource {
148
126
  }
149
127
  /**
150
- * Source specification for URL-based content.
151
- * mimeType is optional as it can often be inferred from the URL or response headers.
128
+ * URL-referenced content. AG-UI `UrlSource`: `mimeType` is optional.
152
129
  */
153
- export interface ContentPartUrlSource {
154
- /**
155
- * Indicates this is URL-referenced content.
156
- */
157
- type: 'url';
158
- /**
159
- * HTTP(S) URL or data URI pointing to the content.
160
- */
161
- value: string;
162
- /**
163
- * Optional MIME type hint for cases where providers can't infer it from the URL.
164
- */
165
- mimeType?: string;
130
+ export interface ContentPartUrlSource extends AGUIUrlSource {
166
131
  }
167
132
  /**
168
- * Source specification for multimodal content.
169
- * Discriminated union supporting both inline data (base64) and URL-based content.
170
- * - For 'data' sources: mimeType is required
171
- * - For 'url' sources: mimeType is optional
133
+ * A provider-issued file handle (Files API). AG-UI `FileSource`: the handle
134
+ * is opaque, do not fetch or parse it.
172
135
  */
173
- export type ContentPartSource = ContentPartDataSource | ContentPartUrlSource;
136
+ export interface ContentPartFileSource extends AGUIFileSource {
137
+ }
174
138
  /**
175
- * Image content part for multimodal messages.
139
+ * Where a media part's bytes come from: inline data, a URL, or a provider
140
+ * file handle. Same members as AG-UI `PartSource`.
141
+ */
142
+ export type ContentPartSource = ContentPartDataSource | ContentPartUrlSource | ContentPartFileSource;
143
+ /**
144
+ * Image content part for multimodal messages. AG-UI `ImagePart` with typed metadata.
176
145
  * @template TMetadata - Provider-specific metadata type (e.g., OpenAI's detail level)
177
146
  */
178
- export interface ImagePart<TMetadata = unknown> {
179
- type: 'image';
180
- /** Source of the image content */
181
- source: ContentPartSource;
147
+ export interface ImagePart<TMetadata = unknown> extends AGUIImagePart {
182
148
  /** Provider-specific metadata (e.g., OpenAI's detail: 'auto' | 'low' | 'high') */
183
149
  metadata?: TMetadata;
184
150
  }
185
151
  /**
186
- * Audio content part for multimodal messages.
152
+ * Audio content part for multimodal messages. AG-UI `AudioPart` with typed metadata.
187
153
  * @template TMetadata - Provider-specific metadata type
188
154
  */
189
- export interface AudioPart<TMetadata = unknown> {
190
- type: 'audio';
191
- /** Source of the audio content */
192
- source: ContentPartSource;
155
+ export interface AudioPart<TMetadata = unknown> extends AGUIAudioPart {
193
156
  /** Provider-specific metadata (e.g., format, sample rate) */
194
157
  metadata?: TMetadata;
195
158
  }
196
159
  /**
197
- * Video content part for multimodal messages.
160
+ * Video content part for multimodal messages. AG-UI `VideoPart` with typed metadata.
198
161
  * @template TMetadata - Provider-specific metadata type
199
162
  */
200
- export interface VideoPart<TMetadata = unknown> {
201
- type: 'video';
202
- /** Source of the video content */
203
- source: ContentPartSource;
163
+ export interface VideoPart<TMetadata = unknown> extends AGUIVideoPart {
204
164
  /** Provider-specific metadata (e.g., duration, resolution) */
205
165
  metadata?: TMetadata;
206
166
  }
207
167
  /**
208
- * Document content part for multimodal messages (e.g., PDFs).
168
+ * Document content part for multimodal messages (e.g., PDFs). AG-UI `DocumentPart` with typed metadata.
209
169
  * @template TMetadata - Provider-specific metadata type (e.g., Anthropic's media_type)
210
170
  */
211
- export interface DocumentPart<TMetadata = unknown> {
212
- type: 'document';
213
- /** Source of the document content */
214
- source: ContentPartSource;
171
+ export interface DocumentPart<TMetadata = unknown> extends AGUIDocumentPart {
215
172
  /** Provider-specific metadata (e.g., media_type for PDFs) */
216
173
  metadata?: TMetadata;
217
174
  }
@@ -360,6 +317,28 @@ export interface StructuredOutputPart<TData = unknown> {
360
317
  /** Populated when `status === 'error'`. */
361
318
  errorMessage?: string;
362
319
  }
320
+ export type SubagentStatus = 'running' | 'finished' | 'error' | 'suspended';
321
+ /**
322
+ * One child invocation as the client sees it. AG-UI `SubagentInfo` names the
323
+ * child; the other AG-UI fields come from its `SUBAGENT_STARTED`,
324
+ * `SUBAGENT_FINISHED` and `SUBAGENT_ERROR` events. `id` is the AG-UI
325
+ * `subagentRunId`. `status`, `parentRunId` and `messages` are client state the
326
+ * spec does not model.
327
+ */
328
+ export interface SubagentHandleData extends AGUISubagentInfo, Pick<AGUISubagentStartedEvent, 'parentSubagentRunId' | 'parentToolCallId' | 'metadata'> {
329
+ id: AGUISubagentRunId;
330
+ status: SubagentStatus;
331
+ /** The parent chat run that started this child. */
332
+ parentRunId?: string;
333
+ /** Interrupts this child raised, while `status` is `'suspended'`. */
334
+ interruptIds?: AGUISubagentFinishedSuspendedOutcome['interruptIds'];
335
+ messages: Array<UIMessage>;
336
+ error?: Pick<AGUISubagentErrorEvent, 'message' | 'code'>;
337
+ }
338
+ export interface SubagentPart {
339
+ type: 'subagent';
340
+ subagent: SubagentHandleData;
341
+ }
363
342
  export interface UIResourcePart {
364
343
  type: 'ui-resource';
365
344
  /** The ui:// resource object in MCP-native shape — fed straight to the renderer. */
@@ -381,7 +360,7 @@ export interface UIResourcePart {
381
360
  * Currently always `undefined` — nothing populates this field yet. */
382
361
  meta?: Record<string, unknown>;
383
362
  }
384
- export type MessagePart<TData = unknown> = TextPart | ImagePart | AudioPart | VideoPart | DocumentPart | ToolCallPart | ToolResultPart | ThinkingPart | StructuredOutputPart<TData> | UIResourcePart;
363
+ export type MessagePart<TData = unknown> = TextPart | ImagePart | AudioPart | VideoPart | DocumentPart | ToolCallPart | ToolResultPart | ThinkingPart | StructuredOutputPart<TData> | UIResourcePart | SubagentPart;
385
364
  /**
386
365
  * Shape of `metadata.tanstack` on a message.
387
366
  * `createdAt` is an ISO-8601 string.
@@ -389,6 +368,10 @@ export type MessagePart<TData = unknown> = TextPart | ImagePart | AudioPart | Vi
389
368
  export interface TanStackMessageMetadata {
390
369
  createdAt?: string;
391
370
  model?: string;
371
+ /** Parent chat run that produced this assistant message. */
372
+ runId?: string;
373
+ /** Card data on a child wire message. See `uiMessagesToWire`. */
374
+ subagent?: SubagentWireInfo;
392
375
  /** Thinking signature for a `role: 'reasoning'` fan-out message. */
393
376
  signature?: string;
394
377
  /** Per-tool-call provider metadata keyed by tool call id (e.g. Gemini thoughtSignature). */
@@ -474,6 +457,21 @@ type RuntimeContextField<TContext> = IsUnknown<TContext> extends true ? {
474
457
  */
475
458
  context: TContext;
476
459
  };
460
+ /**
461
+ * Options for a single `emitCustomEvent` call, on both the tool-execution and
462
+ * middleware contexts.
463
+ */
464
+ export interface EmitCustomEventOptions {
465
+ /**
466
+ * Keep this event in the durability batch with later chunks.
467
+ * CUSTOM events flush as soon as they are emitted, so a progress
468
+ * indicator can render at emit time. Pass `{ batch: true }` for a
469
+ * high-volume stream that should share appends with later output.
470
+ * `process.stdout`, `process.stderr`, `sandbox.file`, and
471
+ * `sandbox.file.diff` already batch.
472
+ */
473
+ batch?: boolean;
474
+ }
477
475
  /**
478
476
  * Context passed to tool execute functions, providing capabilities like
479
477
  * emitting custom events during execution.
@@ -493,6 +491,8 @@ export type ToolExecutionContext<TContext = unknown> = RuntimeContextField<TCont
493
491
  *
494
492
  * @param eventName - Name of the custom event
495
493
  * @param value - Event payload value
494
+ * @param options - Pass `{ batch: true }` to keep this event in the
495
+ * durability batch instead of flushing it immediately
496
496
  *
497
497
  * @example
498
498
  * ```ts
@@ -505,7 +505,7 @@ export type ToolExecutionContext<TContext = unknown> = RuntimeContextField<TCont
505
505
  * })
506
506
  * ```
507
507
  */
508
- emitCustomEvent: (eventName: string, value: Record<string, any>) => void;
508
+ emitCustomEvent: (eventName: string, value: Record<string, any>, options?: EmitCustomEventOptions) => void;
509
509
  };
510
510
  export type ToolExecuteFunction<TInput extends SchemaInput | undefined = SchemaInput, TOutput extends SchemaInput | undefined = SchemaInput, TContext = unknown> = undefined extends TContext ? (args: InferSchemaType<TInput>, context?: ToolExecutionContext<TContext>) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput> : (args: InferSchemaType<TInput>, context: ToolExecutionContext<TContext>) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>;
511
511
  /**
@@ -890,6 +890,12 @@ export interface TextOptions<TProviderOptionsSuperset extends Record<string, any
890
890
  * Surfaced for observability/middleware; not consumed by the LLM call.
891
891
  */
892
892
  parentRunId?: string;
893
+ /**
894
+ * AG-UI subagent run id when this chat runs as a child of another run.
895
+ * A child `chat()` passes `ctx.subagentRunId`. Middleware reads it as
896
+ * `ctx.subagentRunId`. Absent on a top-level run.
897
+ */
898
+ subagentRunId?: string;
893
899
  /** Application state mirrored in a STATE_SNAPSHOT before an interrupt terminal. */
894
900
  state?: unknown;
895
901
  /**
@@ -1029,13 +1035,10 @@ export interface TextMessageEndEvent extends AGUITextMessageEndEvent {
1029
1035
  /**
1030
1036
  * Emitted when a tool call starts.
1031
1037
  *
1032
- * @ag-ui/core provides: `toolCallId`, `toolCallName`, `parentMessageId?`
1033
- *
1034
- * Field shapes are taken from AG-UI via `Pick` (not `extends`) so Zod
1035
- * `.passthrough()` index signatures do not pollute the StreamChunk
1036
- * discriminated union — required for {@link KnownCustomEvent} narrowing.
1038
+ * @ag-ui/core provides: `toolCallId`, `toolCallName`, `parentMessageId?`,
1039
+ * `subagentRunId?`
1037
1040
  */
1038
- export interface ToolCallStartEvent extends Pick<AGUIToolCallStartEvent, 'toolCallId' | 'toolCallName' | 'parentMessageId' | 'timestamp' | 'rawEvent'> {
1041
+ export interface ToolCallStartEvent extends Omit<AGUIToolCallStartEvent, 'type'> {
1039
1042
  type: 'TOOL_CALL_START';
1040
1043
  /** Alias of `toolCallName`. Kept so existing stream readers still compile. */
1041
1044
  toolName?: string;
@@ -1052,11 +1055,9 @@ export interface ToolCallArgsEvent extends AGUIToolCallArgsEvent {
1052
1055
  /**
1053
1056
  * Emitted when a tool call completes.
1054
1057
  *
1055
- * @ag-ui/core provides: `toolCallId`
1056
- *
1057
- * Same `Pick` (not `extends`) rationale as {@link ToolCallStartEvent}.
1058
+ * @ag-ui/core provides: `toolCallId`, `subagentRunId?`
1058
1059
  */
1059
- export interface ToolCallEndEvent extends Pick<AGUIToolCallEndEvent, 'toolCallId' | 'timestamp' | 'rawEvent'> {
1060
+ export interface ToolCallEndEvent extends Omit<AGUIToolCallEndEvent, 'type'> {
1060
1061
  type: 'TOOL_CALL_END';
1061
1062
  /** Parsed tool arguments when the adapter already parsed them. */
1062
1063
  input?: unknown;
@@ -1113,12 +1114,9 @@ export interface StateDeltaEvent extends AGUIStateDeltaEvent {
1113
1114
  /**
1114
1115
  * Custom event for extensibility.
1115
1116
  *
1116
- * @ag-ui/core provides: `name`, `value`
1117
- *
1118
- * Uses `Pick` (not `extends`) so the Zod passthrough index signature does not
1119
- * erase discriminant property access on {@link KnownCustomEvent} unions.
1117
+ * @ag-ui/core provides: `name`, `value`, `subagentRunId?`
1120
1118
  */
1121
- export interface CustomEvent extends Pick<AGUICustomEvent, 'name' | 'value' | 'timestamp' | 'rawEvent'> {
1119
+ export interface CustomEvent extends Omit<AGUICustomEvent, 'type'> {
1122
1120
  type: 'CUSTOM';
1123
1121
  metadata?: Record<string, any>;
1124
1122
  }
@@ -1403,10 +1401,51 @@ export interface ReasoningEndEvent extends AGUIReasoningEndEvent {
1403
1401
  */
1404
1402
  export interface ReasoningEncryptedValueEvent extends AGUIReasoningEncryptedValueEvent {
1405
1403
  }
1404
+ /** AG-UI 1.0 ActivitySnapshotEvent shape. */
1405
+ export interface ActivitySnapshotEvent extends AGUIActivitySnapshotEvent {
1406
+ }
1407
+ /** AG-UI 1.0 ActivityDeltaEvent shape. */
1408
+ export interface ActivityDeltaEvent extends AGUIActivityDeltaEvent {
1409
+ }
1410
+ /** AG-UI 1.0 RawEvent shape. */
1411
+ export interface RawEvent extends AGUIRawEvent {
1412
+ }
1413
+ /** AG-UI 1.0 TextMessageChunkEvent shape. */
1414
+ export interface TextMessageChunkEvent extends AGUITextMessageChunkEvent {
1415
+ }
1416
+ /** AG-UI 1.0 ToolCallChunkEvent shape. */
1417
+ export interface ToolCallChunkEvent extends AGUIToolCallChunkEvent {
1418
+ }
1419
+ /** AG-UI 1.0 ReasoningMessageChunkEvent shape. */
1420
+ export interface ReasoningMessageChunkEvent extends AGUIReasoningMessageChunkEvent {
1421
+ }
1406
1422
  /**
1407
1423
  * Union of all AG-UI events.
1408
1424
  */
1409
- export type AGUIEvent = RunStartedEvent | RunFinishedEvent | RunErrorEvent | TextMessageStartEvent | TextMessageContentEvent | TextMessageEndEvent | ToolCallStartEvent | ToolCallArgsEvent | ToolCallEndEvent | ToolCallResultEvent | StepStartedEvent | StepFinishedEvent | MessagesSnapshotEvent | StateSnapshotEvent | StateDeltaEvent | CustomEvent | ReasoningStartEvent | ReasoningMessageStartEvent | ReasoningMessageContentEvent | ReasoningMessageEndEvent | ReasoningEndEvent | ReasoningEncryptedValueEvent;
1425
+ export type AGUIEvent = ActivitySnapshotEvent | ActivityDeltaEvent | RawEvent | TextMessageChunkEvent | ToolCallChunkEvent | ReasoningMessageChunkEvent | RunStartedEvent | RunFinishedEvent | RunErrorEvent | TextMessageStartEvent | TextMessageContentEvent | TextMessageEndEvent | ToolCallStartEvent | ToolCallArgsEvent | ToolCallEndEvent | ToolCallResultEvent | StepStartedEvent | StepFinishedEvent | MessagesSnapshotEvent | StateSnapshotEvent | StateDeltaEvent | CustomEvent | ReasoningStartEvent | ReasoningMessageStartEvent | ReasoningMessageContentEvent | ReasoningMessageEndEvent | ReasoningEndEvent | ReasoningEncryptedValueEvent | SubagentStartedEvent | SubagentFinishedEvent | SubagentErrorEvent;
1426
+ /**
1427
+ * A child agent started. The later chunks for that child carry the same
1428
+ * `subagentRunId`.
1429
+ *
1430
+ * @ag-ui/core provides: `subagentRunId`, `name`, `description?`,
1431
+ * `parentSubagentRunId?`, `parentToolCallId?`, `parentMessageId?`, `metadata?`
1432
+ */
1433
+ export interface SubagentStartedEvent extends AGUISubagentStartedEvent {
1434
+ }
1435
+ /**
1436
+ * A child agent's segment of this run ended.
1437
+ *
1438
+ * @ag-ui/core provides: `subagentRunId`, `result?`, `outcome?`
1439
+ */
1440
+ export interface SubagentFinishedEvent extends AGUISubagentFinishedEvent {
1441
+ }
1442
+ /**
1443
+ * A child agent failed. The parent run can continue.
1444
+ *
1445
+ * @ag-ui/core provides: `subagentRunId`, `message`, `code?`
1446
+ */
1447
+ export interface SubagentErrorEvent extends AGUISubagentErrorEvent {
1448
+ }
1410
1449
  /**
1411
1450
  * Chunk returned by the SDK during streaming chat completions.
1412
1451
  * Uses the AG-UI protocol event format.
@@ -3,8 +3,9 @@ import { ContentPart, StreamChunk, ToolOutputState } from '../types.js';
3
3
  /**
4
4
  * Adapter / engine yield before normalize. Public StreamChunk is spec-only.
5
5
  * This type still allows the old extra fields.
6
+ * Extras never override a spec field (see WithAdapterExtras).
6
7
  */
7
- export type AdapterYieldChunk = StreamChunk & {
8
+ type AdapterExtras = {
8
9
  model?: string;
9
10
  finishReason?: 'stop' | 'length' | 'content_filter' | 'tool_calls' | null;
10
11
  usage?: any;
@@ -29,3 +30,6 @@ export type AdapterYieldChunk = StreamChunk & {
29
30
  threadId?: string;
30
31
  runId?: string;
31
32
  };
33
+ type WithAdapterExtras<T> = T extends StreamChunk ? T & Omit<AdapterExtras, keyof T> : never;
34
+ export type AdapterYieldChunk = WithAdapterExtras<StreamChunk>;
35
+ export {};
@@ -1,14 +1,7 @@
1
+ import { TokenUsage as SpecTokenUsage } from '@ag-ui/core';
1
2
  import { TokenUsage } from '../types.js';
2
3
  /** AG-UI spec `usage[]` item (provider/model labels + token counts only). */
3
- export interface SpecTokenUsage {
4
- provider?: string;
5
- model?: string;
6
- inputTokens?: number;
7
- outputTokens?: number;
8
- totalTokens?: number;
9
- reasoningTokens?: number;
10
- cachedInputTokens?: number;
11
- }
4
+ export type { TokenUsage as SpecTokenUsage } from '@ag-ui/core';
12
5
  export interface ToSpecTokenUsageOptions {
13
6
  provider?: string;
14
7
  model?: string;
@@ -20,5 +13,12 @@ export declare function toSpecTokenUsage(usage: TokenUsage, options?: ToSpecToke
20
13
  usage: Array<SpecTokenUsage>;
21
14
  leftover?: TokenUsageLeftover;
22
15
  };
16
+ /**
17
+ * Add two usage totals, as for a parent run and its children. Numbers add
18
+ * up. `billed` adds up only in the same unit. `providerUsageDetails` is
19
+ * opaque, so the latest one stays. `@tanstack/ai-persistence` sums per-run
20
+ * usage with the same rules.
21
+ */
22
+ export declare function addTokenUsage(current: TokenUsage, next: TokenUsage): TokenUsage;
23
23
  export declare function rebuildTokenUsage(usage: unknown, leftover?: TokenUsageLeftover): TokenUsage | undefined;
24
24
  export declare function fromSpecTokenUsage(usage: ReadonlyArray<SpecTokenUsage> | undefined, leftover?: TokenUsageLeftover): TokenUsage | undefined;
@@ -21,6 +21,7 @@ function toSpecTokenUsage(usage, options) {
21
21
  };
22
22
  const cachedInputTokens = promptTokensDetails?.cachedTokens;
23
23
  if (cachedInputTokens !== void 0) spec.cachedInputTokens = cachedInputTokens;
24
+ if (promptTokensDetails?.cacheWriteTokens !== void 0) spec.cacheWriteInputTokens = promptTokensDetails.cacheWriteTokens;
24
25
  const reasoningTokens = completionTokensDetails?.reasoningTokens;
25
26
  if (reasoningTokens !== void 0) spec.reasoningTokens = reasoningTokens;
26
27
  const leftoverPrompt = promptTokensDetails ? definedDetails(withoutKey(promptTokensDetails, "cachedTokens")) : void 0;
@@ -34,17 +35,79 @@ function toSpecTokenUsage(usage, options) {
34
35
  })
35
36
  };
36
37
  }
38
+ function sumNumbers(current, next) {
39
+ if (!current) return next;
40
+ if (!next) return current;
41
+ const result = { ...current };
42
+ for (const key of Object.keys(next)) {
43
+ const value = next[key];
44
+ if (typeof value !== "number") continue;
45
+ const previous = current[key];
46
+ result[key] = (typeof previous === "number" ? previous : 0) + value;
47
+ }
48
+ return result;
49
+ }
50
+ function sumOptional(current, next) {
51
+ if (current === void 0) return next;
52
+ if (next === void 0) return current;
53
+ return current + next;
54
+ }
55
+ /**
56
+ * Add two usage totals, as for a parent run and its children. Numbers add
57
+ * up. `billed` adds up only in the same unit. `providerUsageDetails` is
58
+ * opaque, so the latest one stays. `@tanstack/ai-persistence` sums per-run
59
+ * usage with the same rules.
60
+ */
61
+ function addTokenUsage(current, next) {
62
+ const promptTokensDetails = sumNumbers(current.promptTokensDetails, next.promptTokensDetails);
63
+ const completionTokensDetails = sumNumbers(current.completionTokensDetails, next.completionTokensDetails);
64
+ const costDetails = sumNumbers(current.costDetails, next.costDetails);
65
+ const cost = sumOptional(current.cost, next.cost);
66
+ const durationSeconds = sumOptional(current.durationSeconds, next.durationSeconds);
67
+ const unitsBilled = sumOptional(current.unitsBilled, next.unitsBilled);
68
+ const billed = current.billed && next.billed && current.billed.unit === next.billed.unit ? {
69
+ quantity: current.billed.quantity + next.billed.quantity,
70
+ unit: current.billed.unit
71
+ } : next.billed ?? current.billed;
72
+ const providerUsageDetails = next.providerUsageDetails ?? current.providerUsageDetails;
73
+ return {
74
+ ...current,
75
+ ...next,
76
+ promptTokens: current.promptTokens + next.promptTokens,
77
+ completionTokens: current.completionTokens + next.completionTokens,
78
+ totalTokens: current.totalTokens + next.totalTokens,
79
+ ...promptTokensDetails && { promptTokensDetails },
80
+ ...completionTokensDetails && { completionTokensDetails },
81
+ ...cost !== void 0 && { cost },
82
+ ...costDetails && { costDetails },
83
+ ...durationSeconds !== void 0 && { durationSeconds },
84
+ ...unitsBilled !== void 0 && { unitsBilled },
85
+ ...billed && { billed },
86
+ ...providerUsageDetails && { providerUsageDetails }
87
+ };
88
+ }
37
89
  function rebuildTokenUsage(usage, leftover) {
38
90
  if (isTanstackUsage(usage)) return usage;
39
91
  if (Array.isArray(usage)) return fromSpecTokenUsage(usage, leftover);
40
92
  return fromSpecTokenUsage(void 0, leftover);
41
93
  }
42
94
  function fromSpecTokenUsage(usage, leftover) {
43
- const spec = usage?.[0];
44
- if (spec == null && leftover == null) return;
95
+ const spec = usage?.reduce((total, entry) => {
96
+ for (const key of [
97
+ "inputTokens",
98
+ "outputTokens",
99
+ "totalTokens",
100
+ "cachedInputTokens",
101
+ "cacheWriteInputTokens",
102
+ "reasoningTokens"
103
+ ]) if (entry[key] !== void 0) total[key] = (total[key] ?? 0) + entry[key];
104
+ return total;
105
+ }, {});
106
+ if ((usage === void 0 || usage.length === 0) && leftover == null) return;
45
107
  const { promptTokensDetails: leftoverPromptDetails, completionTokensDetails: leftoverCompletionDetails, ...leftoverRest } = leftover ?? {};
46
108
  const promptTokensDetails = definedDetails({
47
109
  ...spec?.cachedInputTokens !== void 0 ? { cachedTokens: spec.cachedInputTokens } : {},
110
+ ...spec?.cacheWriteInputTokens !== void 0 ? { cacheWriteTokens: spec.cacheWriteInputTokens } : {},
48
111
  ...leftoverPromptDetails
49
112
  });
50
113
  const completionTokensDetails = definedDetails({
@@ -61,6 +124,6 @@ function fromSpecTokenUsage(usage, leftover) {
61
124
  };
62
125
  }
63
126
  //#endregion
64
- export { fromSpecTokenUsage, isTanstackUsage, rebuildTokenUsage, toSpecTokenUsage };
127
+ export { addTokenUsage, fromSpecTokenUsage, isTanstackUsage, rebuildTokenUsage, toSpecTokenUsage };
65
128
 
66
129
  //# sourceMappingURL=ag-ui-usage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ag-ui-usage.js","names":[],"sources":["../../../src/utilities/ag-ui-usage.ts"],"sourcesContent":["import type { TokenUsage } from '../types'\n\n/** AG-UI spec `usage[]` item (provider/model labels + token counts only). */\nexport interface SpecTokenUsage {\n provider?: string\n model?: string\n inputTokens?: number\n outputTokens?: number\n totalTokens?: number\n reasoningTokens?: number\n cachedInputTokens?: number\n}\n\nexport interface ToSpecTokenUsageOptions {\n provider?: string\n model?: string\n}\n\n/** TokenUsage fields that have no AG-UI `usage[]` equivalent. */\nexport type TokenUsageLeftover = Omit<\n TokenUsage,\n 'promptTokens' | 'completionTokens' | 'totalTokens'\n>\n\nfunction definedDetails<T extends object>(value: T): T | undefined {\n return Object.keys(value).length > 0 ? value : undefined\n}\n\nfunction withoutKey<T extends object, K extends keyof T>(\n value: T,\n key: K,\n): Omit<T, K> {\n const next = { ...value }\n delete next[key]\n return next\n}\n\nexport function isTanstackUsage(usage: unknown): usage is TokenUsage {\n return (\n typeof usage === 'object' &&\n usage != null &&\n !Array.isArray(usage) &&\n 'promptTokens' in usage\n )\n}\n\nexport function toSpecTokenUsage(\n usage: TokenUsage,\n options?: ToSpecTokenUsageOptions,\n): { usage: Array<SpecTokenUsage>; leftover?: TokenUsageLeftover } {\n const {\n promptTokens,\n completionTokens,\n totalTokens,\n promptTokensDetails,\n completionTokensDetails,\n ...rest\n } = usage\n\n const spec: SpecTokenUsage = {\n ...(options?.provider !== undefined ? { provider: options.provider } : {}),\n ...(options?.model !== undefined ? { model: options.model } : {}),\n inputTokens: promptTokens,\n outputTokens: completionTokens,\n totalTokens,\n }\n const cachedInputTokens = promptTokensDetails?.cachedTokens\n if (cachedInputTokens !== undefined) {\n spec.cachedInputTokens = cachedInputTokens\n }\n const reasoningTokens = completionTokensDetails?.reasoningTokens\n if (reasoningTokens !== undefined) {\n spec.reasoningTokens = reasoningTokens\n }\n\n const leftoverPrompt = promptTokensDetails\n ? definedDetails(withoutKey(promptTokensDetails, 'cachedTokens'))\n : undefined\n const leftoverCompletion = completionTokensDetails\n ? definedDetails(withoutKey(completionTokensDetails, 'reasoningTokens'))\n : undefined\n\n return {\n usage: [spec],\n leftover: definedDetails({\n ...rest,\n ...(leftoverPrompt !== undefined\n ? { promptTokensDetails: leftoverPrompt }\n : {}),\n ...(leftoverCompletion !== undefined\n ? { completionTokensDetails: leftoverCompletion }\n : {}),\n }),\n }\n}\n\nexport function rebuildTokenUsage(\n usage: unknown,\n leftover?: TokenUsageLeftover,\n): TokenUsage | undefined {\n if (isTanstackUsage(usage)) {\n return usage\n }\n if (Array.isArray(usage)) {\n return fromSpecTokenUsage(usage, leftover)\n }\n return fromSpecTokenUsage(undefined, leftover)\n}\n\nexport function fromSpecTokenUsage(\n usage: ReadonlyArray<SpecTokenUsage> | undefined,\n leftover?: TokenUsageLeftover,\n): TokenUsage | undefined {\n const spec = usage?.[0]\n if (spec == null && leftover == null) {\n return undefined\n }\n\n const {\n promptTokensDetails: leftoverPromptDetails,\n completionTokensDetails: leftoverCompletionDetails,\n ...leftoverRest\n } = leftover ?? {}\n\n const promptTokensDetails = definedDetails({\n ...(spec?.cachedInputTokens !== undefined\n ? { cachedTokens: spec.cachedInputTokens }\n : {}),\n ...leftoverPromptDetails,\n })\n const completionTokensDetails = definedDetails({\n ...(spec?.reasoningTokens !== undefined\n ? { reasoningTokens: spec.reasoningTokens }\n : {}),\n ...leftoverCompletionDetails,\n })\n\n return {\n promptTokens: spec?.inputTokens ?? 0,\n completionTokens: spec?.outputTokens ?? 0,\n totalTokens: spec?.totalTokens ?? 0,\n ...leftoverRest,\n ...(promptTokensDetails !== undefined ? { promptTokensDetails } : {}),\n ...(completionTokensDetails !== undefined\n ? { completionTokensDetails }\n : {}),\n }\n}\n"],"mappings":";AAwBA,SAAS,eAAiC,OAAyB;CACjE,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ,KAAA;AACjD;AAEA,SAAS,WACP,OACA,KACY;CACZ,MAAM,OAAO,EAAE,GAAG,MAAM;CACxB,OAAO,KAAK;CACZ,OAAO;AACT;AAEA,SAAgB,gBAAgB,OAAqC;CACnE,OACE,OAAO,UAAU,YACjB,SAAS,QACT,CAAC,MAAM,QAAQ,KAAK,KACpB,kBAAkB;AAEtB;AAEA,SAAgB,iBACd,OACA,SACiE;CACjE,MAAM,EACJ,cACA,kBACA,aACA,qBACA,yBACA,GAAG,SACD;CAEJ,MAAM,OAAuB;EAC3B,GAAI,SAAS,aAAa,KAAA,IAAY,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;EACxE,GAAI,SAAS,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;EAC/D,aAAa;EACb,cAAc;EACd;CACF;CACA,MAAM,oBAAoB,qBAAqB;CAC/C,IAAI,sBAAsB,KAAA,GACxB,KAAK,oBAAoB;CAE3B,MAAM,kBAAkB,yBAAyB;CACjD,IAAI,oBAAoB,KAAA,GACtB,KAAK,kBAAkB;CAGzB,MAAM,iBAAiB,sBACnB,eAAe,WAAW,qBAAqB,cAAc,CAAC,IAC9D,KAAA;CACJ,MAAM,qBAAqB,0BACvB,eAAe,WAAW,yBAAyB,iBAAiB,CAAC,IACrE,KAAA;CAEJ,OAAO;EACL,OAAO,CAAC,IAAI;EACZ,UAAU,eAAe;GACvB,GAAG;GACH,GAAI,mBAAmB,KAAA,IACnB,EAAE,qBAAqB,eAAe,IACtC,CAAC;GACL,GAAI,uBAAuB,KAAA,IACvB,EAAE,yBAAyB,mBAAmB,IAC9C,CAAC;EACP,CAAC;CACH;AACF;AAEA,SAAgB,kBACd,OACA,UACwB;CACxB,IAAI,gBAAgB,KAAK,GACvB,OAAO;CAET,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,mBAAmB,OAAO,QAAQ;CAE3C,OAAO,mBAAmB,KAAA,GAAW,QAAQ;AAC/C;AAEA,SAAgB,mBACd,OACA,UACwB;CACxB,MAAM,OAAO,QAAQ;CACrB,IAAI,QAAQ,QAAQ,YAAY,MAC9B;CAGF,MAAM,EACJ,qBAAqB,uBACrB,yBAAyB,2BACzB,GAAG,iBACD,YAAY,CAAC;CAEjB,MAAM,sBAAsB,eAAe;EACzC,GAAI,MAAM,sBAAsB,KAAA,IAC5B,EAAE,cAAc,KAAK,kBAAkB,IACvC,CAAC;EACL,GAAG;CACL,CAAC;CACD,MAAM,0BAA0B,eAAe;EAC7C,GAAI,MAAM,oBAAoB,KAAA,IAC1B,EAAE,iBAAiB,KAAK,gBAAgB,IACxC,CAAC;EACL,GAAG;CACL,CAAC;CAED,OAAO;EACL,cAAc,MAAM,eAAe;EACnC,kBAAkB,MAAM,gBAAgB;EACxC,aAAa,MAAM,eAAe;EAClC,GAAG;EACH,GAAI,wBAAwB,KAAA,IAAY,EAAE,oBAAoB,IAAI,CAAC;EACnE,GAAI,4BAA4B,KAAA,IAC5B,EAAE,wBAAwB,IAC1B,CAAC;CACP;AACF"}
1
+ {"version":3,"file":"ag-ui-usage.js","names":[],"sources":["../../../src/utilities/ag-ui-usage.ts"],"sourcesContent":["import type { TokenUsage as SpecTokenUsage } from '@ag-ui/core'\nimport type { TokenUsage } from '../types'\n\n/** AG-UI spec `usage[]` item (provider/model labels + token counts only). */\nexport type { TokenUsage as SpecTokenUsage } from '@ag-ui/core'\n\nexport interface ToSpecTokenUsageOptions {\n provider?: string\n model?: string\n}\n\n/** TokenUsage fields that have no AG-UI `usage[]` equivalent. */\nexport type TokenUsageLeftover = Omit<\n TokenUsage,\n 'promptTokens' | 'completionTokens' | 'totalTokens'\n>\n\nfunction definedDetails<T extends object>(value: T): T | undefined {\n return Object.keys(value).length > 0 ? value : undefined\n}\n\nfunction withoutKey<T extends object, K extends keyof T>(\n value: T,\n key: K,\n): Omit<T, K> {\n const next = { ...value }\n delete next[key]\n return next\n}\n\nexport function isTanstackUsage(usage: unknown): usage is TokenUsage {\n return (\n typeof usage === 'object' &&\n usage != null &&\n !Array.isArray(usage) &&\n 'promptTokens' in usage\n )\n}\n\nexport function toSpecTokenUsage(\n usage: TokenUsage,\n options?: ToSpecTokenUsageOptions,\n): { usage: Array<SpecTokenUsage>; leftover?: TokenUsageLeftover } {\n const {\n promptTokens,\n completionTokens,\n totalTokens,\n promptTokensDetails,\n completionTokensDetails,\n ...rest\n } = usage\n\n const spec: SpecTokenUsage = {\n ...(options?.provider !== undefined ? { provider: options.provider } : {}),\n ...(options?.model !== undefined ? { model: options.model } : {}),\n inputTokens: promptTokens,\n outputTokens: completionTokens,\n totalTokens,\n }\n const cachedInputTokens = promptTokensDetails?.cachedTokens\n if (cachedInputTokens !== undefined) {\n spec.cachedInputTokens = cachedInputTokens\n }\n if (promptTokensDetails?.cacheWriteTokens !== undefined) {\n spec.cacheWriteInputTokens = promptTokensDetails.cacheWriteTokens\n }\n const reasoningTokens = completionTokensDetails?.reasoningTokens\n if (reasoningTokens !== undefined) {\n spec.reasoningTokens = reasoningTokens\n }\n\n // cacheWriteTokens stays in the leftover too, so clients built before\n // cacheWriteInputTokens existed still rebuild it.\n const leftoverPrompt = promptTokensDetails\n ? definedDetails(withoutKey(promptTokensDetails, 'cachedTokens'))\n : undefined\n const leftoverCompletion = completionTokensDetails\n ? definedDetails(withoutKey(completionTokensDetails, 'reasoningTokens'))\n : undefined\n\n return {\n usage: [spec],\n leftover: definedDetails({\n ...rest,\n ...(leftoverPrompt !== undefined\n ? { promptTokensDetails: leftoverPrompt }\n : {}),\n ...(leftoverCompletion !== undefined\n ? { completionTokensDetails: leftoverCompletion }\n : {}),\n }),\n }\n}\n\nfunction sumNumbers<T extends object>(\n current: T | undefined,\n next: T | undefined,\n): T | undefined {\n if (!current) return next\n if (!next) return current\n const result = { ...current }\n for (const key of Object.keys(next) as Array<keyof T>) {\n const value = next[key]\n if (typeof value !== 'number') continue\n const previous = current[key]\n result[key] = ((typeof previous === 'number' ? previous : 0) +\n value) as T[keyof T]\n }\n return result\n}\n\nfunction sumOptional(current?: number, next?: number): number | undefined {\n if (current === undefined) return next\n if (next === undefined) return current\n return current + next\n}\n\n/**\n * Add two usage totals, as for a parent run and its children. Numbers add\n * up. `billed` adds up only in the same unit. `providerUsageDetails` is\n * opaque, so the latest one stays. `@tanstack/ai-persistence` sums per-run\n * usage with the same rules.\n */\nexport function addTokenUsage(\n current: TokenUsage,\n next: TokenUsage,\n): TokenUsage {\n const promptTokensDetails = sumNumbers(\n current.promptTokensDetails,\n next.promptTokensDetails,\n )\n const completionTokensDetails = sumNumbers(\n current.completionTokensDetails,\n next.completionTokensDetails,\n )\n const costDetails = sumNumbers(current.costDetails, next.costDetails)\n const cost = sumOptional(current.cost, next.cost)\n const durationSeconds = sumOptional(\n current.durationSeconds,\n next.durationSeconds,\n )\n const unitsBilled = sumOptional(current.unitsBilled, next.unitsBilled)\n const billed =\n current.billed && next.billed && current.billed.unit === next.billed.unit\n ? {\n quantity: current.billed.quantity + next.billed.quantity,\n unit: current.billed.unit,\n }\n : (next.billed ?? current.billed)\n const providerUsageDetails =\n next.providerUsageDetails ?? current.providerUsageDetails\n return {\n ...current,\n ...next,\n promptTokens: current.promptTokens + next.promptTokens,\n completionTokens: current.completionTokens + next.completionTokens,\n totalTokens: current.totalTokens + next.totalTokens,\n ...(promptTokensDetails && { promptTokensDetails }),\n ...(completionTokensDetails && { completionTokensDetails }),\n ...(cost !== undefined && { cost }),\n ...(costDetails && { costDetails }),\n ...(durationSeconds !== undefined && { durationSeconds }),\n ...(unitsBilled !== undefined && { unitsBilled }),\n ...(billed && { billed }),\n ...(providerUsageDetails && { providerUsageDetails }),\n }\n}\n\nexport function rebuildTokenUsage(\n usage: unknown,\n leftover?: TokenUsageLeftover,\n): TokenUsage | undefined {\n if (isTanstackUsage(usage)) {\n return usage\n }\n if (Array.isArray(usage)) {\n return fromSpecTokenUsage(usage, leftover)\n }\n return fromSpecTokenUsage(undefined, leftover)\n}\n\nexport function fromSpecTokenUsage(\n usage: ReadonlyArray<SpecTokenUsage> | undefined,\n leftover?: TokenUsageLeftover,\n): TokenUsage | undefined {\n const spec = usage?.reduce<SpecTokenUsage>((total, entry) => {\n for (const key of [\n 'inputTokens',\n 'outputTokens',\n 'totalTokens',\n 'cachedInputTokens',\n 'cacheWriteInputTokens',\n 'reasoningTokens',\n ] as const) {\n if (entry[key] !== undefined) total[key] = (total[key] ?? 0) + entry[key]\n }\n return total\n }, {})\n if ((usage === undefined || usage.length === 0) && leftover == null) {\n return undefined\n }\n\n const {\n promptTokensDetails: leftoverPromptDetails,\n completionTokensDetails: leftoverCompletionDetails,\n ...leftoverRest\n } = leftover ?? {}\n\n const promptTokensDetails = definedDetails({\n ...(spec?.cachedInputTokens !== undefined\n ? { cachedTokens: spec.cachedInputTokens }\n : {}),\n ...(spec?.cacheWriteInputTokens !== undefined\n ? { cacheWriteTokens: spec.cacheWriteInputTokens }\n : {}),\n ...leftoverPromptDetails,\n })\n const completionTokensDetails = definedDetails({\n ...(spec?.reasoningTokens !== undefined\n ? { reasoningTokens: spec.reasoningTokens }\n : {}),\n ...leftoverCompletionDetails,\n })\n\n return {\n promptTokens: spec?.inputTokens ?? 0,\n completionTokens: spec?.outputTokens ?? 0,\n totalTokens: spec?.totalTokens ?? 0,\n ...leftoverRest,\n ...(promptTokensDetails !== undefined ? { promptTokensDetails } : {}),\n ...(completionTokensDetails !== undefined\n ? { completionTokensDetails }\n : {}),\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { tanstackMetadata } from "./merge-metadata.js";
2
2
  import { normalizeToolResult } from "./tool-result.js";
3
+ import { wireSubagentInfo, wireSubagentRunId } from "./subagent-wire.js";
3
4
  import { coerceCreatedAt, modelMessageToUIMessage } from "../activities/chat/messages.js";
4
5
  //#region src/utilities/ag-ui-wire.ts
5
6
  function isRecord(value) {
@@ -110,6 +111,7 @@ function uiMessagesToWire(messages, options) {
110
111
  metadata: rebuiltToolMetadata(void 0, void 0, void 0, result)
111
112
  });
112
113
  }
114
+ for (const part of parts) if (part.type === "subagent") wire.push(...subagentToWire(part, options));
113
115
  }
114
116
  return wire;
115
117
  }
@@ -142,6 +144,7 @@ function messageMetadata(msg, parts, includeSnapshotStructuredOutput) {
142
144
  const previousTanstack = tanstackMetadata(msg);
143
145
  const tanstack = {};
144
146
  if (previousTanstack?.model !== void 0) tanstack.model = previousTanstack.model;
147
+ if (previousTanstack?.runId !== void 0) tanstack.runId = previousTanstack.runId;
145
148
  if (previousTanstack?.signature !== void 0) tanstack.signature = previousTanstack.signature;
146
149
  const createdAt = coerceCreatedAt(msg.createdAt);
147
150
  if (createdAt !== void 0) tanstack.createdAt = createdAt.toISOString();
@@ -175,6 +178,59 @@ function collectText(parts) {
175
178
  else if (p.type === "structured-output" && p.status === "complete" && p.raw !== "") out.push(p.raw);
176
179
  return out.join("");
177
180
  }
181
+ /**
182
+ * A child's messages, tagged with its AG-UI `subagentRunId`. The card data
183
+ * rides in `metadata.tanstack.subagent`, so the other side can rebuild the
184
+ * card and continue a suspended child.
185
+ */
186
+ function subagentToWire(part, options) {
187
+ const { subagent } = part;
188
+ const info = {
189
+ name: subagent.name,
190
+ status: subagent.status,
191
+ ...subagent.description !== void 0 && { description: subagent.description },
192
+ ...subagent.error !== void 0 && { error: subagent.error },
193
+ ...subagent.interruptIds !== void 0 && { interruptIds: subagent.interruptIds },
194
+ ...subagent.parentSubagentRunId !== void 0 && { parentSubagentRunId: subagent.parentSubagentRunId },
195
+ ...subagent.parentToolCallId !== void 0 && { parentToolCallId: subagent.parentToolCallId },
196
+ ...subagent.metadata !== void 0 && { metadata: subagent.metadata }
197
+ };
198
+ const child = uiMessagesToWire(subagent.messages, options);
199
+ const own = child.some((message) => wireSubagentRunId(message) === void 0);
200
+ return (own ? child : [{
201
+ id: `subagent:${subagent.id}`,
202
+ role: "assistant"
203
+ }, ...child]).map((message, index) => {
204
+ const nestedId = wireSubagentRunId(message);
205
+ if (nestedId !== void 0) {
206
+ const nested = wireSubagentInfo(message);
207
+ if (!nested || nested.parentSubagentRunId !== void 0) return message;
208
+ return withSubagentInfo(message, nestedId, {
209
+ ...nested,
210
+ parentSubagentRunId: subagent.id
211
+ });
212
+ }
213
+ return withSubagentInfo(message, subagent.id, !own && index === 0 ? {
214
+ ...info,
215
+ placeholder: true
216
+ } : info);
217
+ });
218
+ }
219
+ function withSubagentInfo(message, subagentRunId, info) {
220
+ const metadata = isRecord(message.metadata) ? message.metadata : {};
221
+ const tanstack = isRecord(metadata.tanstack) ? metadata.tanstack : {};
222
+ return {
223
+ ...message,
224
+ subagentRunId,
225
+ metadata: {
226
+ ...metadata,
227
+ tanstack: {
228
+ ...tanstack,
229
+ subagent: info
230
+ }
231
+ }
232
+ };
233
+ }
178
234
  function collectUserContent(parts) {
179
235
  if (!parts.some((p) => p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document")) return collectText(parts);
180
236
  const out = [];