@ai-sdk/workflow 2.0.69 → 2.0.71

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.
@@ -0,0 +1,177 @@
1
+ import type { LanguageModelV4Prompt } from '@ai-sdk/provider';
2
+ import { createIdGenerator } from '@ai-sdk/provider-utils';
3
+ import type { LanguageModel, ModelMessage } from 'ai';
4
+ import { createTelemetryDispatcher } from 'ai/internal';
5
+ import { buildModelStepResult } from './build-model-step-result.js';
6
+ import type {
7
+ ModelCallOptions,
8
+ ModelCallResult,
9
+ ModelCallTelemetry,
10
+ } from './model-call.js';
11
+ import {
12
+ resolveSerializableTools,
13
+ type SerializableToolDef,
14
+ } from './serializable-schema.js';
15
+
16
+ const generateCallId = createIdGenerator({ prefix: 'call', size: 24 });
17
+
18
+ /**
19
+ * Telemetry hooks for the language model call inside a durable model step.
20
+ * Matches the options accepted by `streamLanguageModelCall`.
21
+ */
22
+ export type LanguageModelCallTelemetry = {
23
+ callId: string;
24
+ onLanguageModelCallStart?: (event: any) => PromiseLike<void> | void;
25
+ onLanguageModelCallEnd?: (event: any) => PromiseLike<void> | void;
26
+ executeLanguageModelCallInTelemetryContext?: <T>(options: {
27
+ callId: string;
28
+ execute: () => PromiseLike<T>;
29
+ }) => PromiseLike<T>;
30
+ };
31
+
32
+ /**
33
+ * Global telemetry integrations are not visible in the workflow VM, so the
34
+ * workflow body sends a serializable descriptor and the model step, which
35
+ * runs in Node, dispatches the model call lifecycle itself.
36
+ *
37
+ * Each model call is reported as its own operation with a unique `callId`.
38
+ */
39
+ export function shouldDispatchModelCallTelemetryInStep(telemetry?: {
40
+ isEnabled?: boolean;
41
+ integrations?: unknown;
42
+ }): boolean {
43
+ return (
44
+ telemetry?.isEnabled !== false &&
45
+ telemetry?.integrations == null &&
46
+ globalThis.AI_SDK_TELEMETRY_INTEGRATIONS == null
47
+ );
48
+ }
49
+
50
+ export async function runModelCallWithTelemetry({
51
+ telemetry,
52
+ modelInit,
53
+ prompt,
54
+ serializedTools,
55
+ options,
56
+ execute,
57
+ }: {
58
+ telemetry: ModelCallTelemetry | undefined;
59
+ modelInit: LanguageModel;
60
+ prompt: LanguageModelV4Prompt;
61
+ serializedTools: Record<string, SerializableToolDef> | undefined;
62
+ options: ModelCallOptions | undefined;
63
+ execute: (
64
+ languageModelCallTelemetry: LanguageModelCallTelemetry | undefined,
65
+ ) => Promise<ModelCallResult>;
66
+ }): Promise<ModelCallResult> {
67
+ if (telemetry == null) {
68
+ return execute(undefined);
69
+ }
70
+
71
+ const dispatcher = createTelemetryDispatcher({ telemetry });
72
+ const callId = generateCallId();
73
+ const { provider, modelId } =
74
+ typeof modelInit === 'string'
75
+ ? { provider: 'gateway', modelId: modelInit }
76
+ : modelInit;
77
+ const tools =
78
+ serializedTools == null
79
+ ? undefined
80
+ : resolveSerializableTools(serializedTools);
81
+ const messages = prompt as unknown as ModelMessage[];
82
+
83
+ await dispatcher.onStart?.({
84
+ callId,
85
+ operationId: 'ai.workflowAgent.stream',
86
+ provider,
87
+ modelId,
88
+ system: undefined,
89
+ messages,
90
+ tools,
91
+ toolChoice: options?.toolChoice,
92
+ activeTools: undefined,
93
+ maxOutputTokens: options?.maxOutputTokens,
94
+ temperature: options?.temperature,
95
+ topP: options?.topP,
96
+ topK: options?.topK,
97
+ presencePenalty: options?.presencePenalty,
98
+ frequencyPenalty: options?.frequencyPenalty,
99
+ stopSequences: options?.stopSequences,
100
+ seed: options?.seed,
101
+ maxRetries: options?.maxRetries ?? 2,
102
+ timeout: undefined,
103
+ headers: options?.headers,
104
+ reasoning: options?.reasoning,
105
+ providerOptions: options?.providerOptions,
106
+ output: undefined,
107
+ runtimeContext: {},
108
+ toolsContext: {},
109
+ } as never);
110
+ await dispatcher.onStepStart?.({
111
+ callId,
112
+ provider,
113
+ modelId,
114
+ stepNumber: telemetry.stepNumber,
115
+ system: undefined,
116
+ messages,
117
+ tools,
118
+ toolChoice: options?.toolChoice,
119
+ activeTools: undefined,
120
+ steps: [],
121
+ providerOptions: options?.providerOptions,
122
+ output: undefined,
123
+ runtimeContext: {},
124
+ toolsContext: {},
125
+ } as never);
126
+
127
+ let result: ModelCallResult;
128
+ try {
129
+ result = await execute({
130
+ callId,
131
+ onLanguageModelCallStart: dispatcher.onLanguageModelCallStart,
132
+ onLanguageModelCallEnd: dispatcher.onLanguageModelCallEnd,
133
+ executeLanguageModelCallInTelemetryContext:
134
+ dispatcher.executeLanguageModelCall,
135
+ });
136
+ } catch (error) {
137
+ await dispatcher.onError?.({ callId, error });
138
+ throw error;
139
+ }
140
+
141
+ if (result.aborted) {
142
+ await dispatcher.onAbort?.({ callId, steps: [] } as never);
143
+ return result;
144
+ }
145
+
146
+ if ('terminalError' in result) {
147
+ await dispatcher.onError?.({ callId, error: result.terminalError });
148
+ return result;
149
+ }
150
+
151
+ const step = await buildModelStepResult(
152
+ result.raw,
153
+ result.toolCalls,
154
+ result.finish,
155
+ result.providerExecutedToolResults,
156
+ {
157
+ callId,
158
+ tools,
159
+ stepNumber: telemetry.stepNumber,
160
+ runtimeContext: {},
161
+ toolsContext: {},
162
+ },
163
+ );
164
+
165
+ await dispatcher.onStepEnd?.(step as never);
166
+ // Keep the step's prototype so getters such as `text` stay available.
167
+ await dispatcher.onEnd?.(
168
+ Object.assign(Object.create(Object.getPrototypeOf(step)), step, {
169
+ finalStep: step,
170
+ steps: [step],
171
+ usage: step.usage,
172
+ totalUsage: step.usage,
173
+ }),
174
+ );
175
+
176
+ return result;
177
+ }
package/src/model-call.ts CHANGED
@@ -15,7 +15,11 @@ import type {
15
15
  ToolChoice,
16
16
  ToolSet,
17
17
  } from 'ai';
18
- import type { ProviderOptions, StreamTextTransform } from './workflow-agent.js';
18
+ import type {
19
+ DownloadFunction,
20
+ ProviderOptions,
21
+ StreamTextTransform,
22
+ } from './workflow-agent.js';
19
23
 
20
24
  export type ModelCallStreamPart<TTools extends ToolSet = ToolSet> =
21
25
  | Experimental_LanguageModelStreamPart<TTools>
@@ -62,6 +66,7 @@ export interface ModelCallOptions {
62
66
  toolChoice?: ToolChoice<ToolSet>;
63
67
  includeRawChunks?: boolean;
64
68
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
69
+ download?: DownloadFunction;
65
70
  include?: {
66
71
  requestBody?: boolean;
67
72
  responseBody?: boolean;
@@ -71,6 +76,21 @@ export interface ModelCallOptions {
71
76
  experimental_transform?:
72
77
  | StreamTextTransform<ToolSet>
73
78
  | Array<StreamTextTransform<ToolSet>>;
79
+ /**
80
+ * Set when the model step dispatches telemetry to globally registered
81
+ * integrations, which are not available in the workflow body.
82
+ */
83
+ telemetry?: ModelCallTelemetry;
84
+ }
85
+
86
+ /**
87
+ * Serializable telemetry settings for a durable model call.
88
+ */
89
+ export interface ModelCallTelemetry {
90
+ functionId?: string;
91
+ recordInputs?: boolean;
92
+ recordOutputs?: boolean;
93
+ stepNumber: number;
74
94
  }
75
95
 
76
96
  /**
@@ -19,7 +19,7 @@ import {
19
19
  type Tool,
20
20
  } from '@ai-sdk/provider-utils';
21
21
  import { dynamicTool, tool, type ToolSet } from 'ai';
22
- import Ajv from 'ajv';
22
+ import Ajv, { type ErrorObject } from 'ajv';
23
23
  import Ajv2020 from 'ajv/dist/2020.js';
24
24
 
25
25
  /**
@@ -133,6 +133,29 @@ function resolveToolDescription<TOOLS extends ToolSet>({
133
133
  });
134
134
  }
135
135
 
136
+ function formatAjvError(error: ErrorObject): string {
137
+ const path = `data${error.instancePath}`;
138
+
139
+ switch (error.keyword) {
140
+ case 'enum':
141
+ return `${path} must be one of ${error.params.allowedValues
142
+ .map((value: unknown) => JSON.stringify(value))
143
+ .join(', ')}`;
144
+ case 'additionalProperties':
145
+ return `${path} has unexpected property ${JSON.stringify(
146
+ error.params.additionalProperty,
147
+ )}`;
148
+ case 'const':
149
+ return `${path} must equal ${JSON.stringify(error.params.allowedValue)}`;
150
+ case 'required':
151
+ return `${path} is missing required property ${JSON.stringify(
152
+ error.params.missingProperty,
153
+ )}`;
154
+ default:
155
+ return `${path} ${error.message ?? 'is invalid'}`;
156
+ }
157
+ }
158
+
136
159
  /**
137
160
  * Reconstructs tool objects from serializable tool definitions inside a step.
138
161
  *
@@ -144,6 +167,7 @@ export function resolveSerializableTools(
144
167
  tools: Record<string, SerializableToolDef>,
145
168
  ): ToolSet {
146
169
  const ajvOptions = {
170
+ allErrors: true,
147
171
  strict: false,
148
172
  validateFormats: false,
149
173
  } as const;
@@ -168,7 +192,7 @@ export function resolveSerializableTools(
168
192
  }
169
193
  return {
170
194
  success: false,
171
- error: new Error(schemaAjv.errorsText(validateFn.errors)),
195
+ error: new Error(validateFn.errors!.map(formatAjvError).join(', ')),
172
196
  };
173
197
  },
174
198
  });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Integration test workflows for WorkflowAgent telemetry.
3
+ */
4
+ import { getWritable } from 'workflow';
5
+ import { mockTextModel } from '../providers/mock.js';
6
+ import { WorkflowAgent } from '../workflow-agent.js';
7
+
8
+ async function getGlobalTelemetryIntegrationCount(): Promise<number | null> {
9
+ 'use step';
10
+ return globalThis.AI_SDK_TELEMETRY_INTEGRATIONS?.length ?? null;
11
+ }
12
+
13
+ export async function agentStreamWithGlobalTelemetry(prompt: string) {
14
+ 'use workflow';
15
+ const agent = new WorkflowAgent({
16
+ model: mockTextModel(`Echo: ${prompt}`),
17
+ telemetry: { functionId: 'telemetry-test' },
18
+ });
19
+ const result = await agent.stream({
20
+ messages: [{ role: 'user', content: prompt }],
21
+ writable: getWritable(),
22
+ });
23
+
24
+ return {
25
+ text: result.steps.at(-1)?.text,
26
+ integrationsInWorkflow:
27
+ globalThis.AI_SDK_TELEMETRY_INTEGRATIONS?.length ?? null,
28
+ integrationsInStep: await getGlobalTelemetryIntegrationCount(),
29
+ };
30
+ }
31
+
32
+ export async function agentGenerateWithGlobalTelemetry(prompt: string) {
33
+ 'use workflow';
34
+ const agent = new WorkflowAgent({
35
+ model: mockTextModel(`Echo: ${prompt}`),
36
+ telemetry: { functionId: 'telemetry-test' },
37
+ });
38
+ const result = await agent.generate({
39
+ messages: [{ role: 'user', content: prompt }],
40
+ });
41
+ return { text: result.text };
42
+ }
@@ -578,6 +578,7 @@ export type WorkflowAgentOptions<
578
578
 
579
579
  /**
580
580
  * Default custom download function to use for URLs.
581
+ * Mark the function with `'use step'` when used in a workflow.
581
582
  *
582
583
  * Per-stream `experimental_download` values passed to `stream()` override this default.
583
584
  */
@@ -1074,6 +1075,7 @@ export type WorkflowAgentCallOptions<
1074
1075
  /**
1075
1076
  * Custom download function to use for URLs.
1076
1077
  * By default, files are downloaded if the model does not support the URL for the given media type.
1078
+ * Mark the function with `'use step'` when used in a workflow.
1077
1079
  */
1078
1080
  experimental_download?: DownloadFunction;
1079
1081
 
@@ -1834,7 +1836,8 @@ export class WorkflowAgent<
1834
1836
  ? { prompt: effectivePrompt }
1835
1837
  : { messages: effectiveMessages! }),
1836
1838
  } as Prompt);
1837
- const download = effectiveDownloadFromPrepare;
1839
+ const workflowDownload: DownloadFunction = async requests =>
1840
+ requests.map(() => null);
1838
1841
  const sandbox = options.experimental_sandbox ?? this.experimentalSandbox;
1839
1842
  // Model steps enforce the absolute deadline below. Avoid creating a native
1840
1843
  // timeout signal in the workflow VM, where timer APIs are unavailable.
@@ -1872,7 +1875,8 @@ export class WorkflowAgent<
1872
1875
  effectiveTelemetry,
1873
1876
  telemetryDispatcher,
1874
1877
  prompt,
1875
- download,
1878
+ download: effectiveDownloadFromPrepare,
1879
+ workflowDownload,
1876
1880
  sandbox,
1877
1881
  effectiveAbortSignal,
1878
1882
  timeoutAt,
@@ -1909,6 +1913,7 @@ export class WorkflowAgent<
1909
1913
  telemetryDispatcher,
1910
1914
  prompt,
1911
1915
  download,
1916
+ workflowDownload,
1912
1917
  sandbox,
1913
1918
  effectiveAbortSignal,
1914
1919
  timeoutAt,
@@ -2003,7 +2008,7 @@ export class WorkflowAgent<
2003
2008
  tool,
2004
2009
  errorMode: 'text',
2005
2010
  supportedUrls: {},
2006
- download,
2011
+ download: workflowDownload,
2007
2012
  }),
2008
2013
  });
2009
2014
  continue;
@@ -2061,7 +2066,7 @@ export class WorkflowAgent<
2061
2066
  tool,
2062
2067
  errorMode: 'text',
2063
2068
  supportedUrls: {},
2064
- download,
2069
+ download: workflowDownload,
2065
2070
  }),
2066
2071
  });
2067
2072
  continue;
@@ -2183,7 +2188,7 @@ export class WorkflowAgent<
2183
2188
  const modelPrompt = await convertToLanguageModelPrompt({
2184
2189
  prompt,
2185
2190
  supportedUrls: {},
2186
- download,
2191
+ download: workflowDownload,
2187
2192
  });
2188
2193
 
2189
2194
  // Merge generation settings: constructor defaults < prepareCall < stream options
@@ -2368,7 +2373,7 @@ export class WorkflowAgent<
2368
2373
  messages,
2369
2374
  resolvedContext,
2370
2375
  effectiveAbortSignal,
2371
- download,
2376
+ workflowDownload,
2372
2377
  stepSandbox,
2373
2378
  );
2374
2379
  result =
@@ -2546,6 +2551,7 @@ export class WorkflowAgent<
2546
2551
  repairToolCall: (options.repairToolCall ??
2547
2552
  options.experimental_repairToolCall ??
2548
2553
  this.repairToolCall) as ToolCallRepairFunction<ToolSet> | undefined,
2554
+ download,
2549
2555
  responseFormat: await (options.output ?? this.output)?.responseFormat,
2550
2556
  experimental_transform: options.experimental_transform as
2551
2557
  | StreamTextTransform<ToolSet>
@@ -2735,7 +2741,7 @@ export class WorkflowAgent<
2735
2741
  toolCall,
2736
2742
  capturedProviderToolResults,
2737
2743
  stepTools,
2738
- download,
2744
+ workflowDownload,
2739
2745
  ),
2740
2746
  })),
2741
2747
  );
@@ -2942,7 +2948,7 @@ export class WorkflowAgent<
2942
2948
  toolCall,
2943
2949
  capturedProviderToolResults,
2944
2950
  stepTools,
2945
- download,
2951
+ workflowDownload,
2946
2952
  ),
2947
2953
  })),
2948
2954
  );
@@ -172,6 +172,7 @@ export interface WorkflowChatTransportOptions<UI_MESSAGE extends UIMessage> {
172
172
 
173
173
  /**
174
174
  * Maximum number of consecutive errors allowed during reconnection attempts.
175
+ * Streams that end without receiving any UI message chunks count as errors.
175
176
  * Defaults to 3 if not provided.
176
177
  */
177
178
  maxConsecutiveErrors?: number;
@@ -491,6 +492,7 @@ export class WorkflowChatTransport<
491
492
  useExplicitStartIndex = false;
492
493
 
493
494
  try {
495
+ const streamStartIndex = chunkIndex;
494
496
  const chunkStream = parseJsonEventStream({
495
497
  stream: response.body,
496
498
  schema: uiMessageChunkSchema,
@@ -510,7 +512,10 @@ export class WorkflowChatTransport<
510
512
  gotFinish = true;
511
513
  }
512
514
  }
513
- // Reset consecutive error count only after successful stream parsing
515
+ if (chunkIndex === streamStartIndex) {
516
+ throw new Error('Reconnect stream ended without receiving chunks.');
517
+ }
518
+ // Reset consecutive errors only after successful parsing with progress.
514
519
  consecutiveErrors = 0;
515
520
  } catch (error) {
516
521
  console.error('Error in chat GET reconnectToStream', error);
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflow-chat-transport-Bduf4ZDx.js","names":[],"sources":["../src/normalize-ui-message-stream.ts","../src/workflow-chat-transport.ts"],"sourcesContent":["import type { UIMessageChunk } from 'ai';\n\n/**\n * Tracks, for one part family (text or reasoning), which part ids are open or\n * have ended since the latest step boundary.\n */\ninterface PartFrameState {\n /** A `*-start` was seen and has not yet received its explicit `*-end`. */\n open: Set<string>;\n /** A part that ended since the latest step boundary. */\n ended: Set<string>;\n}\n\nconst newPartFrameState = (): PartFrameState => ({\n open: new Set(),\n ended: new Set(),\n});\n\n/**\n * Repairs the framing for a single `*-start` / `*-delta` / `*-end` chunk\n * against the running framing state, yielding the chunks the consumer should\n * see. Text and reasoning parts share this logic (`startType` differentiates\n * the synthesized start chunk).\n *\n * @yields the chunks (possibly synthesized, possibly none) the consumer should see.\n */\nfunction* repairPart(\n kind: 'start' | 'delta' | 'end',\n id: string,\n chunk: UIMessageChunk,\n state: PartFrameState,\n startType: 'text-start' | 'reasoning-start',\n): Generator<UIMessageChunk> {\n if (kind === 'start') {\n // Drop a duplicate/replayed start for a part that is still open or has\n // already ended since the latest step boundary.\n if (state.open.has(id) || state.ended.has(id)) {\n return;\n }\n state.open.add(id);\n yield chunk;\n return;\n }\n\n // delta / end: drop a re-delivered chunk for an already-ended part.\n if (state.ended.has(id)) {\n return;\n }\n // Synthesize the missing start for an orphaned delta/end.\n if (!state.open.has(id)) {\n state.open.add(id);\n yield { type: startType, id } as UIMessageChunk;\n }\n if (kind === 'end') {\n state.open.delete(id);\n state.ended.add(id);\n }\n yield chunk;\n}\n\n/**\n * Normalizes the part framing of a UI message stream so it is always\n * well-formed for the AI SDK's stream consumer (`processUIMessageStream`,\n * which backs `useChat`/`readUIMessageStream`).\n *\n * ## Why this exists\n *\n * The consumer maintains a map of \"active\" text/reasoning parts keyed by id.\n * A `text-delta`/`text-end` for an id that has no open part is a fatal error\n * (`Received text-delta for missing text part with ID \"0\" ...`) that kills the\n * whole turn. Two properties of the durable streaming model make that error\n * reachable:\n *\n * - A workflow run owns a single shared stream. Multi-step turns can reuse the\n * same part id (commonly `\"0\"`), so a dropped or duplicated `*-start` can\n * orphan the rest of that part's content.\n * - The same stream is read across reconnects, and a stream-producing step can\n * run more than once (retry/redelivery, or the concurrent-worker duplication\n * tracked in vercel/workflow#2331 and #2039). Either can interleave or\n * duplicate chunks on the shared stream — e.g. a `finish-step` landing in the\n * middle of another execution's text part.\n *\n * Since the content is still flowing and only the framing is damaged, repairing\n * the framing here degrades the worst case to \"text begins slightly into the\n * step\" or \"a duplicated tail is dropped\" instead of a dead turn.\n *\n * ## What it does\n *\n * Mirrors the consumer's explicit-end part-lifetime state machine per part type:\n * - keeps open parts active across `finish-step`, while allowing ended ids to\n * be reused by a later step;\n * - synthesizes a missing `*-start` when an orphaned `*-delta`/`*-end` arrives;\n * - drops a re-delivered `*-start`/`*-delta`/`*-end` for a part already\n * open or ended since the latest step boundary (reconnect/replay overlap).\n *\n * A well-formed stream passes through unchanged.\n *\n * ## Scope: text and reasoning only\n *\n * `tool-input-delta` raises the same class of fatal error (`Received\n * tool-input-delta for missing tool call ...`), but tool parts are deliberately\n * left untouched: the consumer does not reset its tool-call map on `finish-step`\n * and tool-call ids are unique, so the step-boundary id-reuse orphaning that\n * makes text/reasoning fragile does not apply to them. If a future duplication\n * mode is found to orphan tool-input parts, extend the same machine to that\n * family rather than special-casing it.\n *\n * @param source the raw UI message chunk stream to normalize.\n * @yields the framing-corrected UI message chunks.\n */\nexport async function* normalizeUIMessageStreamParts(\n source: AsyncIterable<UIMessageChunk>,\n): AsyncGenerator<UIMessageChunk> {\n const text = newPartFrameState();\n const reasoning = newPartFrameState();\n\n for await (const chunk of source) {\n switch (chunk.type) {\n case 'reset-step':\n // A retried model-call step starts a new frame. Forget parts from the\n // invalidated attempt so reused ids are framed normally.\n text.open.clear();\n text.ended.clear();\n reasoning.open.clear();\n reasoning.ended.clear();\n yield chunk;\n break;\n\n case 'finish-step':\n // Open parts are closed only by explicit end chunks. A finish-step can\n // come from another interleaved execution while a part is still open.\n // Ended ids may be reused by the next step.\n text.ended.clear();\n reasoning.ended.clear();\n yield chunk;\n break;\n\n case 'text-start':\n yield* repairPart('start', chunk.id, chunk, text, 'text-start');\n break;\n case 'text-delta':\n yield* repairPart('delta', chunk.id, chunk, text, 'text-start');\n break;\n case 'text-end':\n yield* repairPart('end', chunk.id, chunk, text, 'text-start');\n break;\n\n case 'reasoning-start':\n yield* repairPart(\n 'start',\n chunk.id,\n chunk,\n reasoning,\n 'reasoning-start',\n );\n break;\n case 'reasoning-delta':\n yield* repairPart(\n 'delta',\n chunk.id,\n chunk,\n reasoning,\n 'reasoning-start',\n );\n break;\n case 'reasoning-end':\n yield* repairPart('end', chunk.id, chunk, reasoning, 'reasoning-start');\n break;\n\n default:\n yield chunk;\n }\n }\n}\n","import {\n parseJsonEventStream,\n uiMessageChunkSchema,\n type ChatRequestOptions,\n type ChatTransport,\n type PrepareReconnectToStreamRequest,\n type PrepareSendMessagesRequest,\n type UIMessage,\n type UIMessageChunk,\n} from 'ai';\nimport {\n convertAsyncIteratorToReadableStream,\n getErrorMessage,\n} from '@ai-sdk/provider-utils';\nimport { createAsyncIterableStream } from 'ai/internal';\nimport { normalizeUIMessageStreamParts } from './normalize-ui-message-stream.js';\n\n/**\n * Tracks `*-start` chunks the client has accepted so we can drop deltas/ends\n * that refer to a part whose start was emitted before the resume cursor.\n *\n * AI SDK's UI stream processor throws on `text-delta`/`reasoning-delta`/\n * `tool-input-delta` (and the matching `*-end`) when the start chunk for that\n * id was never observed, and on tool output/approval chunks when no tool part\n * exists for the call id. A negative `startIndex` on a flat chunk stream can\n * easily land mid-part, so without this guard the client crashes on resume.\n *\n * A tool part is established by `tool-input-start` OR by a self-contained\n * `tool-input-available`/`tool-input-error` chunk (the AI SDK creates the\n * part from those directly), so all three mark the call id as seen.\n *\n * This is a best-effort safety net — it preserves only the parts that the\n * resumed window includes a `*-start` for. Server-side rewinding to a step\n * boundary is the proper fix when you want the full message preserved.\n */\ntype OrphanFilter = {\n shouldDrop: (chunk: UIMessageChunk) => boolean;\n};\n\nfunction createOrphanFilter(): OrphanFilter {\n const seenStartedIds = new Set<string>();\n const seenStartedToolCallIds = new Set<string>();\n let warnedOnce = false;\n\n function warnOnce(orphanKind: string, orphanRef: string) {\n if (warnedOnce) return;\n warnedOnce = true;\n console.warn(\n '[WorkflowChatTransport] Dropping orphan UI chunk ' +\n `(${orphanKind} for id \"${orphanRef}\") on resume — ` +\n 'the resume position landed mid-part. The dropped chunk(s) ' +\n \"reference a part whose start chunk wasn't in the resumed \" +\n 'window. To preserve the full message, configure your ' +\n 'stream endpoint to rewind to a step boundary before ' +\n 'returning the readable. See: ' +\n 'https://workflow.dev/docs/ai/resumable-streams#mid-part-resumes',\n );\n }\n\n function shouldDrop(chunk: UIMessageChunk): boolean {\n switch (chunk.type) {\n case 'reset-step':\n seenStartedIds.clear();\n seenStartedToolCallIds.clear();\n return false;\n case 'text-start':\n case 'reasoning-start':\n seenStartedIds.add(chunk.id);\n return false;\n case 'tool-input-start':\n // `tool-input-available` / `tool-input-error` are self-contained: the\n // AI SDK creates the tool part from them directly (non-streamed tool\n // calls are emitted as a bare `tool-input-available`), so they must\n // never be dropped. They also carry the full input, so they recover a\n // tool call whose `tool-input-start` fell outside the resumed window.\n case 'tool-input-available':\n case 'tool-input-error':\n seenStartedToolCallIds.add(chunk.toolCallId);\n return false;\n case 'text-delta':\n case 'text-end':\n case 'reasoning-delta':\n case 'reasoning-end':\n if (seenStartedIds.has(chunk.id)) return false;\n warnOnce(chunk.type, chunk.id);\n return true;\n case 'tool-input-delta':\n case 'tool-approval-request':\n case 'tool-output-available':\n case 'tool-output-error':\n case 'tool-output-denied':\n if (seenStartedToolCallIds.has(chunk.toolCallId)) return false;\n warnOnce(chunk.type, chunk.toolCallId);\n return true;\n default:\n return false;\n }\n }\n\n return { shouldDrop };\n}\n\nexport interface SendMessagesOptions<UI_MESSAGE extends UIMessage> {\n trigger: 'submit-message' | 'regenerate-message';\n chatId: string;\n messageId?: string;\n messages: UI_MESSAGE[];\n abortSignal?: AbortSignal;\n}\n\nexport interface ReconnectToStreamOptions {\n chatId: string;\n abortSignal?: AbortSignal;\n /**\n * Override the `startIndex` for this reconnection.\n * Negative values read from the end when the server's durable stream and\n * tail-index header use the same UIMessageChunk index space.\n * When omitted, falls back to the constructor's `initialStartIndex`.\n */\n startIndex?: number;\n}\n\ntype OnChatSendMessage<UI_MESSAGE extends UIMessage> = (\n response: Response,\n options: SendMessagesOptions<UI_MESSAGE>,\n) => void | Promise<void>;\n\ntype OnChatEnd = ({\n chatId,\n chunkIndex,\n}: {\n chatId: string;\n chunkIndex: number;\n}) => void | Promise<void>;\n\n/**\n * Configuration options for the WorkflowChatTransport.\n *\n * @template UI_MESSAGE - The type of UI messages being sent and received,\n * must extend the UIMessage interface from the AI SDK.\n */\nexport interface WorkflowChatTransportOptions<UI_MESSAGE extends UIMessage> {\n /**\n * API endpoint for chat requests\n * Defaults to /api/chat if not provided\n */\n api?: string;\n\n /**\n * Custom fetch implementation to use for HTTP requests.\n * Defaults to the global fetch function if not provided.\n */\n fetch?: typeof fetch;\n\n /**\n * Callback invoked after successfully sending messages to the chat endpoint.\n * Useful for tracking chat history and inspecting response headers.\n *\n * @param response - The HTTP response object from the chat endpoint\n * @param options - The original options passed to sendMessages\n */\n onChatSendMessage?: OnChatSendMessage<UI_MESSAGE>;\n\n /**\n * Callback invoked when a chat stream ends (receives a \"finish\" chunk).\n * Useful for cleanup operations or state updates.\n *\n * @param chatId - The ID of the chat that ended\n * @param chunkIndex - The total number of chunks received\n */\n onChatEnd?: OnChatEnd;\n\n /**\n * Maximum number of consecutive errors allowed during reconnection attempts.\n * Defaults to 3 if not provided.\n */\n maxConsecutiveErrors?: number;\n\n /**\n * Default `startIndex` to use when reconnecting to a stream without a known\n * chunk position (i.e. the initial reconnection, not a retry).\n * Negative values read from the end of a durable UIMessageChunk stream (e.g.\n * `-10` fetches the last 10 chunks), which is useful for resuming a chat UI\n * after a page refresh without replaying the full conversation. Raw\n * ModelCallStreamPart streams do not support negative UI chunk indexes.\n *\n * Can be overridden per-call via `ReconnectToStreamOptions.startIndex`.\n *\n * Defaults to `0` (replay from the beginning).\n */\n initialStartIndex?: number;\n\n /**\n * Function to prepare the request for sending messages.\n * Allows customizing the API endpoint, headers, credentials, and body.\n */\n prepareSendMessagesRequest?: PrepareSendMessagesRequest<UI_MESSAGE>;\n\n /**\n * Function to prepare the request for reconnecting to a stream.\n * Allows customizing the API endpoint, headers, and credentials.\n */\n prepareReconnectToStreamRequest?: PrepareReconnectToStreamRequest;\n}\n\n/**\n * A transport implementation for managing chat workflows with support for\n * streaming responses and automatic reconnection to interrupted streams.\n *\n * This class implements the ChatTransport interface from the AI SDK and provides\n * reliable message streaming with automatic recovery from network interruptions\n * or function timeouts.\n *\n * @template UI_MESSAGE - The type of UI messages being sent and received,\n * must extend the UIMessage interface from the AI SDK.\n *\n * @implements {ChatTransport<UI_MESSAGE>}\n */\nexport class WorkflowChatTransport<\n UI_MESSAGE extends UIMessage,\n> implements ChatTransport<UI_MESSAGE> {\n private readonly api: string;\n private readonly fetch: typeof fetch;\n private readonly onChatSendMessage?: OnChatSendMessage<UI_MESSAGE>;\n private readonly onChatEnd?: OnChatEnd;\n private readonly maxConsecutiveErrors: number;\n private readonly initialStartIndex: number;\n private readonly prepareSendMessagesRequest?: PrepareSendMessagesRequest<UI_MESSAGE>;\n private readonly prepareReconnectToStreamRequest?: PrepareReconnectToStreamRequest;\n\n /**\n * Creates a new WorkflowChatTransport instance.\n *\n * @param options - Configuration options for the transport\n * @param options.api - API endpoint for chat requests (defaults to '/api/chat')\n * @param options.fetch - Custom fetch implementation (defaults to global fetch)\n * @param options.onChatSendMessage - Callback after sending messages\n * @param options.onChatEnd - Callback when chat stream ends\n * @param options.maxConsecutiveErrors - Maximum consecutive errors for reconnection\n * @param options.prepareSendMessagesRequest - Function to prepare send messages request\n * @param options.prepareReconnectToStreamRequest - Function to prepare reconnect request\n */\n constructor(options: WorkflowChatTransportOptions<UI_MESSAGE> = {}) {\n this.api = options.api ?? '/api/chat';\n this.fetch = options.fetch ?? fetch.bind(globalThis);\n this.onChatSendMessage = options.onChatSendMessage;\n this.onChatEnd = options.onChatEnd;\n this.maxConsecutiveErrors = options.maxConsecutiveErrors ?? 3;\n this.initialStartIndex = options.initialStartIndex ?? 0;\n this.prepareSendMessagesRequest = options.prepareSendMessagesRequest;\n this.prepareReconnectToStreamRequest =\n options.prepareReconnectToStreamRequest;\n }\n\n /**\n * Sends messages to the chat endpoint and returns a stream of response chunks.\n *\n * This method handles the entire chat lifecycle including:\n * - Sending messages to the /api/chat endpoint\n * - Streaming response chunks\n * - Automatic reconnection if the stream is interrupted\n *\n * @param options - Options for sending messages\n * @param options.trigger - The type of message submission ('submit-message' or 'regenerate-message')\n * @param options.chatId - Unique identifier for this chat session\n * @param options.messageId - Optional message ID for tracking specific messages\n * @param options.messages - Array of UI messages to send\n * @param options.abortSignal - Optional AbortSignal to cancel the request\n *\n * @returns A ReadableStream of UIMessageChunk objects containing the response\n * @throws Error if the fetch request fails or returns a non-OK status\n */\n async sendMessages(\n options: SendMessagesOptions<UI_MESSAGE> & ChatRequestOptions,\n ): Promise<ReadableStream<UIMessageChunk>> {\n return convertAsyncIteratorToReadableStream(\n normalizeUIMessageStreamParts(this.sendMessagesIterator(options)),\n );\n }\n\n private async *sendMessagesIterator(\n options: SendMessagesOptions<UI_MESSAGE> & ChatRequestOptions,\n ): AsyncGenerator<UIMessageChunk> {\n const { chatId, messages, abortSignal, trigger, messageId } = options;\n\n // We keep track of if the \"finish\" chunk is received to determine\n // if we need to reconnect, and keep track of the chunk index to resume from.\n let gotFinish = false;\n let chunkIndex = 0;\n\n // Prepare the request using the configurator if provided\n const requestConfig = this.prepareSendMessagesRequest\n ? await this.prepareSendMessagesRequest({\n id: chatId,\n messages,\n requestMetadata: options.metadata,\n body: options.body,\n credentials: undefined,\n headers: options.headers,\n api: this.api,\n trigger,\n messageId,\n })\n : undefined;\n\n const url = requestConfig?.api ?? this.api;\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify(\n requestConfig?.body ?? { messages, ...options.body },\n ),\n headers: requestConfig?.headers,\n credentials: requestConfig?.credentials,\n signal: abortSignal,\n });\n\n if (!response.ok || !response.body) {\n throw new Error(\n `Failed to fetch chat: ${response.status} ${await response.text()}`,\n );\n }\n\n const workflowRunId = response.headers.get('x-workflow-run-id');\n if (!workflowRunId) {\n throw new Error(\n 'Workflow run ID not found in \"x-workflow-run-id\" response header',\n );\n }\n\n // Notify the caller that the chat POST request was sent.\n // This is useful for tracking the chat history on the client\n // side and allows for inspecting response headers.\n await this.onChatSendMessage?.(response, options);\n\n // Flush the initial stream until the end or an error occurs\n try {\n const chunkStream = parseJsonEventStream({\n stream: response.body,\n schema: uiMessageChunkSchema,\n });\n for await (const chunk of createAsyncIterableStream(chunkStream)) {\n if (!chunk.success) {\n throw chunk.error;\n }\n\n chunkIndex++;\n\n yield chunk.value;\n\n if (chunk.value.type === 'finish') {\n gotFinish = true;\n }\n }\n } catch (error) {\n console.error('Error in chat POST stream', error);\n }\n\n if (gotFinish) {\n await this.onFinish(gotFinish, { chatId, chunkIndex });\n } else {\n // If the initial POST request did not include the \"finish\" chunk,\n // we need to reconnect to the stream. This could indicate that a\n // network error occurred or the Vercel Function timed out.\n yield* this.reconnectToStreamIterator(options, workflowRunId, chunkIndex);\n }\n }\n\n /**\n * Reconnects to an existing chat stream that was previously interrupted.\n *\n * This method is useful for resuming a chat session after network issues,\n * page refreshes, or Vercel Function timeouts.\n *\n * @param options - Options for reconnecting to the stream\n * @param options.chatId - The chat ID to reconnect to\n *\n * @returns A ReadableStream of UIMessageChunk objects\n * @throws Error if the reconnection request fails or returns a non-OK status\n */\n async reconnectToStream(\n options: ReconnectToStreamOptions & ChatRequestOptions,\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n const reconnectIterator = normalizeUIMessageStreamParts(\n this.reconnectToStreamIterator(options),\n );\n return convertAsyncIteratorToReadableStream(reconnectIterator);\n }\n\n private async *reconnectToStreamIterator(\n options: ReconnectToStreamOptions & ChatRequestOptions,\n workflowRunId?: string,\n initialChunkIndex = 0,\n ): AsyncGenerator<UIMessageChunk> {\n let chunkIndex = initialChunkIndex;\n\n // When called from the public reconnectToStream (initialChunkIndex === 0),\n // honour the caller's startIndex (or the constructor default) for the\n // first request. This enables negative values so the client can read only\n // the tail of the stream (e.g. the last 10 chunks) instead of replaying\n // everything. After the first request, fall back to the running chunkIndex\n // so that retries resume from the correct position.\n const explicitStartIndex = options.startIndex ?? this.initialStartIndex;\n let useExplicitStartIndex =\n initialChunkIndex === 0 && explicitStartIndex !== 0;\n\n const defaultApi = `${this.api}/${encodeURIComponent(workflowRunId ?? options.chatId)}/stream`;\n\n // Prepare the request using the configurator if provided\n const requestConfig = this.prepareReconnectToStreamRequest\n ? await this.prepareReconnectToStreamRequest({\n id: options.chatId,\n requestMetadata: options.metadata,\n body: undefined,\n credentials: undefined,\n headers: undefined,\n api: defaultApi,\n })\n : undefined;\n\n const baseUrl = requestConfig?.api ?? defaultApi;\n\n let gotFinish = false;\n let consecutiveErrors = 0;\n // When a negative startIndex is used but the tail-index header is absent,\n // retries fall back to startIndex 0 (replay everything) instead of using\n // the incremental chunkIndex which would be wrong.\n let replayFromStart = false;\n\n // When resuming with a negative startIndex, the resolved chunk can land in\n // the middle of a `*-start` / `*-delta` / `*-end` sequence, which crashes\n // the AI SDK UI stream processor. The orphan filter drops chunks whose\n // start chunk was emitted before the resume window. Only activated for\n // negative resumes — non-negative startIndex is the caller's explicit\n // choice and we trust them. See: https://github.com/vercel/workflow/issues/1835\n const orphanFilter =\n useExplicitStartIndex && explicitStartIndex < 0\n ? createOrphanFilter()\n : null;\n\n while (!gotFinish) {\n const startIndex = useExplicitStartIndex\n ? explicitStartIndex\n : replayFromStart\n ? 0\n : chunkIndex;\n\n const url = `${baseUrl}?startIndex=${startIndex}`;\n const response = await this.fetch(url, {\n headers: requestConfig?.headers,\n credentials: requestConfig?.credentials,\n signal: options.abortSignal,\n });\n\n if (!response.ok || !response.body) {\n throw new Error(\n `Failed to fetch chat: ${response.status} ${await response.text()}`,\n );\n }\n\n // When using a negative startIndex, the server resolves it to an\n // absolute position. The reconnection endpoint should return the tail\n // index so we can compute the resolved position for subsequent retries.\n if (useExplicitStartIndex && explicitStartIndex > 0) {\n // Positive startIndex: the first request starts at this absolute\n // position, so set chunkIndex to match so subsequent retries\n // resume from (explicitStartIndex + chunks received).\n chunkIndex = explicitStartIndex;\n } else if (useExplicitStartIndex && explicitStartIndex < 0) {\n const tailIndexHeader = response.headers.get(\n 'x-workflow-stream-tail-index',\n );\n const tailIndex =\n tailIndexHeader !== null ? parseInt(tailIndexHeader, 10) : NaN;\n\n if (!Number.isNaN(tailIndex)) {\n // Resolve: e.g. tailIndex=499, startIndex=-20 → 500 + (-20) = 480\n chunkIndex = Math.max(0, tailIndex + 1 + explicitStartIndex);\n } else {\n // Header missing or unparseable — fall back to replaying from the\n // beginning so retries don't resume from a wrong position.\n console.warn(\n '[WorkflowChatTransport] Negative initialStartIndex is configured ' +\n `(${explicitStartIndex}) but the reconnection endpoint did not ` +\n 'return a valid \"x-workflow-stream-tail-index\" header. Retries ' +\n 'will replay the stream from the beginning. See: ' +\n 'https://workflow.dev/docs/ai/resumable-streams#resuming-from-the-end-of-the-stream',\n );\n replayFromStart = true;\n }\n }\n useExplicitStartIndex = false;\n\n try {\n const chunkStream = parseJsonEventStream({\n stream: response.body,\n schema: uiMessageChunkSchema,\n });\n for await (const chunk of createAsyncIterableStream(chunkStream)) {\n if (!chunk.success) {\n throw chunk.error;\n }\n\n chunkIndex++;\n\n if (orphanFilter?.shouldDrop(chunk.value)) continue;\n\n yield chunk.value;\n\n if (chunk.value.type === 'finish') {\n gotFinish = true;\n }\n }\n // Reset consecutive error count only after successful stream parsing\n consecutiveErrors = 0;\n } catch (error) {\n console.error('Error in chat GET reconnectToStream', error);\n consecutiveErrors++;\n\n if (consecutiveErrors >= this.maxConsecutiveErrors) {\n throw new Error(\n `Failed to reconnect after ${this.maxConsecutiveErrors} consecutive errors. Last error: ${getErrorMessage(error)}`,\n );\n }\n }\n }\n\n await this.onFinish(gotFinish, { chatId: options.chatId, chunkIndex });\n }\n\n private async onFinish(\n gotFinish: boolean,\n { chatId, chunkIndex }: { chatId: string; chunkIndex: number },\n ) {\n if (gotFinish) {\n await this.onChatEnd?.({ chatId, chunkIndex });\n } else {\n throw new Error('No finish chunk received');\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflow-chat-transport-DwOgX7dK.d.ts","names":[],"sources":["../src/workflow-chat-transport.ts"],"mappings":";;UAsGiB,oBAAoB,mBAAmB;EACtD;EACA;EACA;EACA,UAAU;EACV,cAAc;;UAGC;EACf;EACA,cAAc;;;;;;;EAOd;;KAGG,kBAAkB,mBAAmB,cACxC,UAAU,UACV,SAAS,oBAAoB,uBACnB;KAEP,eACH,QACA;EAEA;EACA;aACW;;;;;;;UAQI,6BAA6B,mBAAmB;;;;;EAK/D;;;;;EAMA,eAAe;;;;;;;;EASf,oBAAoB,kBAAkB;;;;;;;;EAStC,YAAY;;;;;EAMZ;;;;;;;;;;;;;EAcA;;;;;EAMA,6BAA6B,2BAA2B;;;;;EAMxD,kCAAkC;;;;;;;;;;;;;;;cAgBvB,sBACX,mBAAmB,sBACR,cAAc;mBACR;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;;;;;;;;;;;;EAcL,YAAA,UAAS,6BAA6B;;;;;;;;;;;;;;;;;;;EA8B5C,aACJ,SAAS,oBAAoB,cAAc,qBAC1C,QAAQ,eAAe;UAMX;;;;;;;;;;;;;EAmGT,kBACJ,SAAS,2BAA2B,qBACnC,QAAQ,eAAe;UAOX;UA6ID"}