@tanstack/ai 0.17.0 → 0.19.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 (43) hide show
  1. package/README.md +0 -4
  2. package/dist/esm/activities/chat/index.d.ts +12 -3
  3. package/dist/esm/activities/chat/index.js +75 -7
  4. package/dist/esm/activities/chat/index.js.map +1 -1
  5. package/dist/esm/activities/chat/messages.js +41 -2
  6. package/dist/esm/activities/chat/messages.js.map +1 -1
  7. package/dist/esm/activities/chat/middleware/compose.js +1 -1
  8. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  9. package/dist/esm/activities/chat/middleware/types.d.ts +12 -1
  10. package/dist/esm/activities/chat/stream/message-updaters.d.ts +35 -0
  11. package/dist/esm/activities/chat/stream/message-updaters.js +95 -0
  12. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  13. package/dist/esm/activities/chat/stream/processor.d.ts +1 -0
  14. package/dist/esm/activities/chat/stream/processor.js +90 -2
  15. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  16. package/dist/esm/activities/chat/tools/schema-converter.js +5 -0
  17. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
  18. package/dist/esm/adapter-internals.d.ts +1 -0
  19. package/dist/esm/index.d.ts +3 -0
  20. package/dist/esm/index.js +8 -2
  21. package/dist/esm/index.js.map +1 -1
  22. package/dist/esm/types.d.ts +86 -16
  23. package/dist/esm/utilities/ag-ui-wire.d.ts +44 -0
  24. package/dist/esm/utilities/ag-ui-wire.js +104 -0
  25. package/dist/esm/utilities/ag-ui-wire.js.map +1 -0
  26. package/dist/esm/utilities/chat-params.d.ts +80 -0
  27. package/dist/esm/utilities/chat-params.js +96 -0
  28. package/dist/esm/utilities/chat-params.js.map +1 -0
  29. package/package.json +3 -3
  30. package/skills/ai-core/ag-ui-protocol/SKILL.md +46 -3
  31. package/skills/ai-core/structured-outputs/SKILL.md +240 -47
  32. package/src/activities/chat/index.ts +144 -10
  33. package/src/activities/chat/messages.ts +70 -4
  34. package/src/activities/chat/middleware/compose.ts +1 -1
  35. package/src/activities/chat/middleware/types.ts +12 -1
  36. package/src/activities/chat/stream/message-updaters.ts +171 -0
  37. package/src/activities/chat/stream/processor.ts +137 -2
  38. package/src/activities/chat/tools/schema-converter.ts +14 -0
  39. package/src/adapter-internals.ts +1 -0
  40. package/src/index.ts +11 -0
  41. package/src/types.ts +104 -15
  42. package/src/utilities/ag-ui-wire.ts +201 -0
  43. package/src/utilities/chat-params.ts +199 -0
package/README.md CHANGED
@@ -49,10 +49,6 @@ A powerful, type-safe AI SDK for building AI-powered applications.
49
49
 
50
50
  ### <a href="https://tanstack.com/ai">Read the docs →</a>
51
51
 
52
- ## Requirements
53
-
54
- - **Node.js v24+** is required to avoid compatibility issues with `isolated-vm`.
55
-
56
52
  ## Tree-Shakeable Adapters
57
53
 
58
54
  Import only the functionality you need for smaller bundle sizes:
@@ -1,5 +1,5 @@
1
1
  import { AnyTextAdapter } from './adapter.js';
2
- import { ConstrainedModelMessage, InferSchemaType, SchemaInput, StreamChunk, StructuredOutputStream, TextOptions, Tool } from '../../types.js';
2
+ import { ConstrainedModelMessage, InferSchemaType, ModelMessage, SchemaInput, StreamChunk, StructuredOutputStream, TextOptions, Tool, UIMessage } from '../../types.js';
3
3
  import { ChatMiddleware } from './middleware/types.js';
4
4
  import { DebugOption } from '../../logger/types.js';
5
5
  import { ProviderTool } from '../../tools/provider-tool.js';
@@ -16,8 +16,15 @@ export declare const kind: "text";
16
16
  export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined, TStream extends boolean> {
17
17
  /** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */
18
18
  adapter: TAdapter;
19
- /** Conversation messages - content types are constrained by the adapter's input modalities and metadata */
20
- messages?: Array<ConstrainedModelMessage<{
19
+ /**
20
+ * Conversation messages. Accepts:
21
+ * - `ConstrainedModelMessage` — content types constrained by the adapter's input modalities.
22
+ * - `ModelMessage` — unconstrained model message (e.g., forwarded from an AG-UI wire payload).
23
+ * - `UIMessage` — parts-based UI representation; converted internally via `convertMessagesToModelMessages`.
24
+ *
25
+ * The three shapes can be mixed in a single array (e.g., when forwarding a wire payload that includes both anchor UIMessages and AG-UI fan-out ModelMessages).
26
+ */
27
+ messages?: Array<UIMessage | ModelMessage | ConstrainedModelMessage<{
21
28
  inputModalities: TAdapter['~types']['inputModalities'];
22
29
  messageMetadataByModality: TAdapter['~types']['messageMetadataByModality'];
23
30
  }>>;
@@ -56,6 +63,8 @@ export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema ex
56
63
  threadId?: TextOptions['threadId'];
57
64
  /** Run ID override for AG-UI protocol. Auto-generated by adapter if not provided. */
58
65
  runId?: TextOptions['runId'];
66
+ /** Parent run ID for AG-UI protocol nested run correlation. */
67
+ parentRunId?: TextOptions['parentRunId'];
59
68
  /**
60
69
  * Optional Standard Schema for structured output.
61
70
  * When provided, the activity will:
@@ -6,7 +6,7 @@ import { LazyToolManager } from "./tools/lazy-tool-manager.js";
6
6
  import { ToolCallManager, MiddlewareAbortError, executeToolCalls } from "./tools/tool-calls.js";
7
7
  import { convertSchemaToJsonSchema, isStandardSchema, parseWithStandardSchema } from "./tools/schema-converter.js";
8
8
  import { maxIterations } from "./agent-loop-strategies.js";
9
- import { convertMessagesToModelMessages } from "./messages.js";
9
+ import { convertMessagesToModelMessages, generateMessageId } from "./messages.js";
10
10
  import { MiddlewareRunner } from "./middleware/compose.js";
11
11
  import { EventType } from "@ag-ui/core";
12
12
  const kind = "text";
@@ -54,8 +54,9 @@ class TextEngine {
54
54
  this.streamId = this.createId("stream");
55
55
  this.effectiveRequest = config.params.abortController ? { signal: config.params.abortController.signal } : void 0;
56
56
  this.effectiveSignal = config.params.abortController?.signal;
57
- this.threadId = config.params.threadId || this.createId("thread");
57
+ this.threadId = config.params.threadId || config.params.conversationId || this.createId("thread");
58
58
  this.runIdOverride = config.params.runId;
59
+ this.parentRunIdOverride = config.params.parentRunId;
59
60
  const allMiddleware = [
60
61
  devtoolsMiddleware(),
61
62
  ...config.middleware || [],
@@ -66,7 +67,10 @@ class TextEngine {
66
67
  this.middlewareCtx = {
67
68
  requestId: this.requestId,
68
69
  streamId: this.streamId,
69
- conversationId: config.params.conversationId,
70
+ threadId: this.threadId,
71
+ // Legacy alias kept on the ctx so middleware that reads
72
+ // `ctx.conversationId` keeps working. Always equals `threadId`.
73
+ conversationId: this.threadId,
70
74
  phase: "init",
71
75
  iteration: 0,
72
76
  chunkIndex: 0,
@@ -111,7 +115,7 @@ class TextEngine {
111
115
  async *run() {
112
116
  this.beforeRun();
113
117
  this.logger.agentLoop("run started", {
114
- conversationId: this.middlewareCtx.conversationId
118
+ threadId: this.middlewareCtx.threadId
115
119
  });
116
120
  try {
117
121
  this.middlewareCtx.phase = "init";
@@ -173,7 +177,7 @@ class TextEngine {
173
177
  } else {
174
178
  this.logger.errors("chat run failed", {
175
179
  error,
176
- conversationId: this.middlewareCtx.conversationId
180
+ threadId: this.middlewareCtx.threadId
177
181
  });
178
182
  await this.middlewareRunner.runOnError(this.middlewareCtx, {
179
183
  error,
@@ -269,7 +273,8 @@ class TextEngine {
269
273
  systemPrompts: this.systemPrompts,
270
274
  logger: this.logger,
271
275
  threadId: this.threadId,
272
- runId: this.runIdOverride
276
+ runId: this.runIdOverride,
277
+ parentRunId: this.parentRunIdOverride
273
278
  })) {
274
279
  if (this.isCancelled()) {
275
280
  break;
@@ -1189,7 +1194,57 @@ async function* runStreamingStructuredOutputImpl(options, jsonSchema) {
1189
1194
  chatOptions: structuredChatOptions,
1190
1195
  outputSchema: jsonSchema
1191
1196
  });
1197
+ let structuredMessageId = null;
1198
+ let startEmitted = false;
1199
+ const extractMessageId = (c) => {
1200
+ const id = c.messageId;
1201
+ return typeof id === "string" && id !== "" ? id : null;
1202
+ };
1203
+ const emitStartIfNeeded = function* (referenceChunk) {
1204
+ if (startEmitted) return;
1205
+ const idForStart = structuredMessageId ?? generateMessageId();
1206
+ structuredMessageId = idForStart;
1207
+ startEmitted = true;
1208
+ yield {
1209
+ type: EventType.CUSTOM,
1210
+ name: "structured-output.start",
1211
+ value: { messageId: idForStart },
1212
+ model: "model" in referenceChunk ? referenceChunk.model ?? model : model,
1213
+ timestamp: "timestamp" in referenceChunk ? referenceChunk.timestamp ?? Date.now() : Date.now(),
1214
+ runId
1215
+ };
1216
+ };
1192
1217
  for await (const chunk of stream) {
1218
+ if (!structuredMessageId) {
1219
+ if (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT) {
1220
+ structuredMessageId = extractMessageId(chunk);
1221
+ }
1222
+ }
1223
+ if (chunk.type === EventType.RUN_ERROR && !startEmitted) {
1224
+ yield* emitStartIfNeeded(chunk);
1225
+ }
1226
+ if (!structuredMessageId && chunk.type === EventType.TEXT_MESSAGE_CONTENT) {
1227
+ yield {
1228
+ type: EventType.RUN_ERROR,
1229
+ runId,
1230
+ model,
1231
+ timestamp: Date.now(),
1232
+ message: "Structured-output stream produced text content without a messageId; adapter is not honoring the AG-UI contract.",
1233
+ code: "structured-output-missing-message-id"
1234
+ };
1235
+ return;
1236
+ }
1237
+ if (!startEmitted && structuredMessageId && (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT)) {
1238
+ startEmitted = true;
1239
+ yield {
1240
+ type: EventType.CUSTOM,
1241
+ name: "structured-output.start",
1242
+ value: { messageId: structuredMessageId },
1243
+ model: "model" in chunk ? chunk.model ?? model : model,
1244
+ timestamp: "timestamp" in chunk ? chunk.timestamp ?? Date.now() : Date.now(),
1245
+ runId
1246
+ };
1247
+ }
1193
1248
  if (chunk.type === EventType.CUSTOM && chunk.name === "structured-output.complete") {
1194
1249
  const value = chunk.value;
1195
1250
  if (isStandardSchema(outputSchema)) {
@@ -1202,10 +1257,13 @@ async function* runStreamingStructuredOutputImpl(options, jsonSchema) {
1202
1257
  ...chunk,
1203
1258
  // Forward `reasoning` through schema validation so consumers that
1204
1259
  // only listen for the terminal event don't lose chain-of-thought.
1260
+ // Tag with messageId so the client processor can snap the right
1261
+ // assistant message's structured-output part.
1205
1262
  value: {
1206
1263
  object: validated,
1207
1264
  raw: value.raw,
1208
- ...value.reasoning ? { reasoning: value.reasoning } : {}
1265
+ ...value.reasoning ? { reasoning: value.reasoning } : {},
1266
+ ...structuredMessageId ? { messageId: structuredMessageId } : {}
1209
1267
  }
1210
1268
  };
1211
1269
  continue;
@@ -1237,6 +1295,16 @@ async function* runStreamingStructuredOutputImpl(options, jsonSchema) {
1237
1295
  return;
1238
1296
  }
1239
1297
  }
1298
+ if (structuredMessageId) {
1299
+ yield {
1300
+ ...chunk,
1301
+ value: {
1302
+ ...chunk.value,
1303
+ messageId: structuredMessageId
1304
+ }
1305
+ };
1306
+ continue;
1307
+ }
1240
1308
  yield chunk;
1241
1309
  continue;
1242
1310
  }