@yolk-sdk/vercel-workflows 0.1.0-canary.56 → 0.1.0-canary.58

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.
package/README.md CHANGED
@@ -9,6 +9,7 @@ pnpm add @yolk-sdk/vercel-workflows@canary effect workflow
9
9
  ```
10
10
 
11
11
  Canary APIs are unstable. Keep all `@yolk-sdk/*` packages on the same version.
12
+ Requires Node.js 22+ and a server runtime supported by Vercel Workflow.
12
13
 
13
14
  ## Subpaths
14
15
 
@@ -34,7 +35,8 @@ import {
34
35
 
35
36
  The `./workflow` subpath is also exported for explicit imports.
36
37
 
37
- Use `./effect` at host boundaries that start, replay, resume, or cancel Workflow runs:
38
+ Use `./effect` at host boundaries that start, replay, resume, or cancel Workflow runs. This is a
39
+ host-wiring sketch; `runAgentWorkflow`, `request`, and `context` are host-owned values:
38
40
 
39
41
  ```ts
40
42
  import { Effect } from 'effect'
@@ -58,6 +60,8 @@ queues, hooks, or stream storage.
58
60
  - model step: produce model events/tool calls
59
61
  - tool batch step: execute requested tools
60
62
  - tool batch result: return one ordered tool-result message per host call, including failed `isError` results
63
+ - partial tool-step failure: return wire-safe `failure`, cumulative `usage`, and `eventSequence` when
64
+ completed sibling tools must remain observable
61
65
  - awaiting-input state: carry pending HITL hook data, wait through `awaitInput`, then rerun the
62
66
  same tool batch with accumulated responses
63
67
  - close step: flush/close output stream after a successful final model step
@@ -67,7 +71,8 @@ Continuation state stays plain serializable data for Workflow persistence.
67
71
 
68
72
  ## Minimal workflow wrapper
69
73
 
70
- Host apps keep concrete Workflow directives local and pass them into the package loop:
74
+ Host apps keep concrete Workflow directives local and pass them into the package loop. The callback
75
+ names below are host-owned `'use step'` functions:
71
76
 
72
77
  ```ts
73
78
  import { createHook } from 'workflow'
@@ -114,25 +119,29 @@ streamed retries can duplicate chunks unless host/client de-dupe is ready.
114
119
  Finite `maxAttempts` values are floored and clamped to at least one; non-finite values normalize to
115
120
  one. Retries have no built-in backoff.
116
121
 
122
+ `maxTurns` follows the same positive-integer boundary: finite values are floored and clamped to at
123
+ least one. Omitted or non-finite values use `defaultMaxWorkflowTurns`.
124
+
117
125
  ## Durable stream events
118
126
 
119
127
  Workflow streams can replay old chunks after retries or reconnects. Durable streams should emit an
120
128
  `eventId` on every JSON-serializable event, typically `AgentEvent`, including errors. Clients should
121
129
  track seen ids.
122
130
 
123
- Use `writeDurableAgentEvent` to assign deterministic ids and write NDJSON:
131
+ Use `writeDurableAgentEvent` to assign deterministic ids and write NDJSON. The following is an
132
+ `Effect.gen` fragment; `writer`, `event`, `runId`, `turn`, and `eventSequence` are host-owned:
124
133
 
125
134
  ```ts
126
135
  const state = makeDurableAgentEventSequencerState(eventSequence)
127
136
  const sequenced =
128
137
  yield *
129
138
  writeDurableAgentEvent({
130
- writer,
131
- event,
132
- streamId: `workflow:${runId}`,
133
- turn,
134
- state
135
- })
139
+ writer,
140
+ event,
141
+ streamId: `workflow:${runId}`,
142
+ turn,
143
+ state
144
+ })
136
145
 
137
146
  eventSequence = sequenced.nextEventSequence
138
147
  ```
@@ -170,18 +179,19 @@ from durable state. Hosts that emit terminal events before persistence should no
170
179
  `streamAgentEventStreamUntilTerminal()` as a durable-settled signal.
171
180
 
172
181
  Use `commitThenWriteTerminalEvent` when a step must commit host state before writing its terminal
173
- event:
182
+ event. This is an `Effect.gen` fragment whose terminal, persistence, writer, and stream values come
183
+ from the host:
174
184
 
175
185
  ```ts
176
186
  const result =
177
187
  yield *
178
188
  commitThenWriteTerminalEvent({
179
- terminal,
180
- commit: persistState,
181
- write: event => writeDurableAgentEvent({ writer, event, streamId, turn, state }),
182
- writeCommitError: error =>
183
- writeDurableAgentEvent({ writer, event: commitErrorEvent(error), streamId, turn, state })
184
- })
189
+ terminal,
190
+ commit: persistState,
191
+ write: event => writeDurableAgentEvent({ writer, event, streamId, turn, state }),
192
+ writeCommitError: error =>
193
+ writeDurableAgentEvent({ writer, event: commitErrorEvent(error), streamId, turn, state })
194
+ })
185
195
  ```
186
196
 
187
197
  The helper never writes the success terminal before `commit` succeeds. If `commit` fails, it writes
@@ -44,9 +44,15 @@ type VercelAgentWorkflowAwaitingInput = {
44
44
  };
45
45
  type VercelAgentWorkflowToolBatchStepResult = {
46
46
  readonly messages: ReadonlyArray<unknown>;
47
- readonly createdMessages: ReadonlyArray<unknown>;
47
+ readonly createdMessages: ReadonlyArray<unknown>; /** Updated cumulative usage when tools run nested model work such as subagents. */
48
+ readonly usage?: unknown;
48
49
  readonly awaitingInput?: VercelAgentWorkflowAwaitingInput;
49
50
  readonly eventSequence?: number;
51
+ /**
52
+ * A plain wire-safe failure captured after partial progress. Returning it instead of rejecting
53
+ * lets orchestration retain usage and event sequencing from completed sibling tools.
54
+ */
55
+ readonly failure?: unknown;
50
56
  };
51
57
  type WorkflowStepResult<A> = {
52
58
  readonly _tag: 'Success';
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-loop.d.mts","names":[],"sources":["../src/workflow-loop.ts"],"mappings":"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"}
1
+ {"version":3,"file":"workflow-loop.d.mts","names":[],"sources":["../src/workflow-loop.ts"],"mappings":"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"}
@@ -15,6 +15,10 @@ const maxRetryAttempts = (policy) => {
15
15
  const configured = policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts;
16
16
  return Number.isFinite(configured) ? Math.max(1, Math.floor(configured)) : 1;
17
17
  };
18
+ const maxWorkflowTurns = (configured) => {
19
+ if (configured === void 0 || !Number.isFinite(configured)) return 500;
20
+ return Math.max(1, Math.floor(configured));
21
+ };
18
22
  async function retryWorkflowStep(runStep, policy) {
19
23
  const maxAttempts = maxRetryAttempts(policy);
20
24
  let attempt = 1;
@@ -33,7 +37,7 @@ async function runVercelAgentWorkflow(config) {
33
37
  turn: 1,
34
38
  eventSequence: 0
35
39
  };
36
- const maxTurns = configuredMaxTurns ?? 500;
40
+ const maxTurns = maxWorkflowTurns(configuredMaxTurns);
37
41
  for (let step = 0; step < maxTurns; step++) {
38
42
  const modelResult = await settleWorkflowStep(retryWorkflowStep(() => runModelStep({
39
43
  context: input.context,
@@ -49,6 +53,14 @@ async function runVercelAgentWorkflow(config) {
49
53
  };
50
54
  }
51
55
  if (modelResult.value.done) {
56
+ const terminalState = {
57
+ request: input.request,
58
+ messages: modelResult.value.messages,
59
+ createdMessages: modelResult.value.createdMessages,
60
+ usage: modelResult.value.usage,
61
+ turn: modelResult.value.turn,
62
+ eventSequence: modelResult.value.eventSequence ?? state.eventSequence
63
+ };
52
64
  const closeResult = await settleWorkflowStep(retryWorkflowStep(closeStream, closeStreamRetry));
53
65
  if (closeResult._tag === "Failure") {
54
66
  await writeErrorSafely(writeError, closeResult.error);
@@ -56,18 +68,27 @@ async function runVercelAgentWorkflow(config) {
56
68
  _tag: "CloseStreamFailed",
57
69
  turns: modelResult.value.turn,
58
70
  error: closeResult.error,
59
- state
71
+ state: terminalState
60
72
  };
61
73
  }
62
74
  return {
63
75
  _tag: "Completed",
64
76
  turns: modelResult.value.turn,
65
- state
77
+ state: terminalState
66
78
  };
67
79
  }
68
80
  let completedToolsResult;
69
81
  let toolHitlResponses = [];
82
+ let cumulativeUsage = modelResult.value.usage;
70
83
  let toolEventSequence = modelResult.value.eventSequence ?? state.eventSequence;
84
+ const currentTurnState = (toolResult) => ({
85
+ request: input.request,
86
+ messages: toolResult === void 0 ? modelResult.value.messages : [...modelResult.value.messages, ...toolResult.messages],
87
+ createdMessages: toolResult?.createdMessages ?? modelResult.value.createdMessages,
88
+ usage: cumulativeUsage,
89
+ turn: modelResult.value.turn,
90
+ eventSequence: toolEventSequence
91
+ });
71
92
  for (;;) {
72
93
  const toolsResult = await settleWorkflowStep(retryWorkflowStep(() => runToolBatchStep({
73
94
  context: input.context,
@@ -75,7 +96,7 @@ async function runVercelAgentWorkflow(config) {
75
96
  calls: modelResult.value.toolCalls,
76
97
  createdMessages: modelResult.value.createdMessages,
77
98
  hitlResponses: toolHitlResponses,
78
- usage: modelResult.value.usage,
99
+ usage: cumulativeUsage,
79
100
  turn: modelResult.value.turn,
80
101
  eventSequence: toolEventSequence
81
102
  }), toolBatchStepRetry));
@@ -85,7 +106,18 @@ async function runVercelAgentWorkflow(config) {
85
106
  _tag: "ToolBatchStepFailed",
86
107
  turn: modelResult.value.turn,
87
108
  error: toolsResult.error,
88
- state
109
+ state: currentTurnState()
110
+ };
111
+ }
112
+ cumulativeUsage = toolsResult.value.usage ?? cumulativeUsage;
113
+ toolEventSequence = toolsResult.value.eventSequence ?? toolEventSequence;
114
+ if (toolsResult.value.failure !== void 0) {
115
+ await writeErrorSafely(writeError, toolsResult.value.failure);
116
+ return {
117
+ _tag: "ToolBatchStepFailed",
118
+ turn: modelResult.value.turn,
119
+ error: toolsResult.value.failure,
120
+ state: currentTurnState(toolsResult.value)
89
121
  };
90
122
  }
91
123
  if (toolsResult.value.awaitingInput === void 0) {
@@ -93,7 +125,8 @@ async function runVercelAgentWorkflow(config) {
93
125
  break;
94
126
  }
95
127
  const awaitingInput = toolsResult.value.awaitingInput;
96
- toolEventSequence = awaitingInput.eventSequence ?? toolsResult.value.eventSequence ?? toolEventSequence;
128
+ cumulativeUsage = toolsResult.value.usage ?? awaitingInput.usage ?? cumulativeUsage;
129
+ toolEventSequence = awaitingInput.eventSequence ?? toolEventSequence;
97
130
  const hitlResponse = await settleWorkflowStep(retryWorkflowStep(() => {
98
131
  if (awaitInput === void 0) return Promise.reject(missingAwaitInputHandlerError());
99
132
  return awaitInput(awaitingInput);
@@ -105,7 +138,7 @@ async function runVercelAgentWorkflow(config) {
105
138
  turn: modelResult.value.turn,
106
139
  error: hitlResponse.error,
107
140
  awaitingInput,
108
- state
141
+ state: currentTurnState(toolsResult.value)
109
142
  };
110
143
  }
111
144
  toolHitlResponses = [...toolHitlResponses, hitlResponse.value];
@@ -124,7 +157,7 @@ async function runVercelAgentWorkflow(config) {
124
157
  request: input.request,
125
158
  messages: [...modelResult.value.messages, ...completedToolsResult.messages],
126
159
  createdMessages: completedToolsResult.createdMessages,
127
- usage: modelResult.value.usage,
160
+ usage: completedToolsResult.usage ?? cumulativeUsage,
128
161
  turn: modelResult.value.turn + 1,
129
162
  eventSequence: completedToolsResult.eventSequence ?? toolEventSequence
130
163
  };
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-loop.mjs","names":[],"sources":["../src/workflow-loop.ts"],"sourcesContent":["export type VercelAgentWorkflowInput = {\n readonly request: unknown\n readonly context: unknown\n}\n\nexport type SerializableWorkflowState = {\n readonly request: unknown\n readonly messages?: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n readonly usage?: unknown\n readonly turn: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowModelStepInput = {\n readonly context: unknown\n readonly state: SerializableWorkflowState\n}\n\nexport type VercelAgentWorkflowModelStepResult = {\n readonly done: boolean\n readonly messages: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n readonly toolCalls: ReadonlyArray<unknown>\n readonly usage: unknown\n readonly turn: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowToolBatchStepInput = {\n readonly context: unknown\n readonly request: unknown\n readonly calls: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n readonly hitlResponses?: ReadonlyArray<unknown>\n readonly usage?: unknown\n readonly turn?: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowAwaitingInput = {\n readonly hookToken: string\n readonly requests: ReadonlyArray<unknown>\n readonly messages: ReadonlyArray<unknown>\n readonly usage: unknown\n readonly turns: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowToolBatchStepResult = {\n readonly messages: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n readonly awaitingInput?: VercelAgentWorkflowAwaitingInput\n readonly eventSequence?: number\n}\n\nexport type WorkflowStepResult<A> =\n | {\n readonly _tag: 'Success'\n readonly value: A\n }\n | {\n readonly _tag: 'Failure'\n readonly error: unknown\n }\n\nexport type VercelAgentWorkflowStepRetryPolicy = {\n readonly maxAttempts: number\n}\n\nexport type VercelAgentWorkflowRunResult =\n | {\n readonly _tag: 'Completed'\n readonly turns: number\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'ModelStepFailed'\n readonly turn: number\n readonly error: unknown\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'ToolBatchStepFailed'\n readonly turn: number\n readonly error: unknown\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'AwaitInputFailed'\n readonly turn: number\n readonly error: unknown\n readonly awaitingInput: VercelAgentWorkflowAwaitingInput\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'CloseStreamFailed'\n readonly turns: number\n readonly error: unknown\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'MaxTurnsExceeded'\n readonly maxTurns: number\n readonly error: Error\n readonly state: SerializableWorkflowState\n }\n\nexport type VercelAgentWorkflowLoopConfig = {\n readonly input: VercelAgentWorkflowInput\n readonly maxTurns?: number\n readonly runModelStep: (\n input: VercelAgentWorkflowModelStepInput\n ) => Promise<VercelAgentWorkflowModelStepResult>\n readonly runToolBatchStep: (\n input: VercelAgentWorkflowToolBatchStepInput\n ) => Promise<VercelAgentWorkflowToolBatchStepResult>\n /** Called only after a model step returns `done: true`. */\n readonly closeStream: () => Promise<void>\n /**\n * Best-effort final failure handler. It must write a safe final error and\n * close the failure stream; rejection is suppressed in favor of the\n * original structured run result.\n */\n readonly writeError: (error: unknown) => Promise<void>\n /** A successful await suspends/resumes without invoking either finalizer. */\n readonly awaitInput?: (input: VercelAgentWorkflowAwaitingInput) => Promise<unknown>\n readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy\n readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy\n readonly awaitInputRetry?: VercelAgentWorkflowStepRetryPolicy\n readonly closeStreamRetry?: VercelAgentWorkflowStepRetryPolicy\n}\n\nexport const defaultMaxWorkflowTurns = 500\nexport const noWorkflowStepRetry: VercelAgentWorkflowStepRetryPolicy = { maxAttempts: 1 }\n\nexport const settleWorkflowStep = <A>(promise: Promise<A>): Promise<WorkflowStepResult<A>> =>\n promise.then(\n value => ({ _tag: 'Success', value }),\n error => ({ _tag: 'Failure', error })\n )\n\nconst workflowMaxTurnsError = (maxTurns: number) =>\n new Error(`Vercel agent workflow exceeded max turns: ${maxTurns}`)\n\nconst writeErrorSafely = (writeError: (error: unknown) => Promise<void>, error: unknown) =>\n writeError(error).catch(() => undefined)\n\nconst missingAwaitInputHandlerError = () =>\n new Error(\n 'Vercel agent workflow awaiting input requested but no awaitInput handler is configured'\n )\n\nconst maxRetryAttempts = (policy: VercelAgentWorkflowStepRetryPolicy | undefined) => {\n const configured = policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts\n\n return Number.isFinite(configured) ? Math.max(1, Math.floor(configured)) : 1\n}\n\nexport async function retryWorkflowStep<A>(\n runStep: () => Promise<A>,\n policy?: VercelAgentWorkflowStepRetryPolicy\n): Promise<A> {\n const maxAttempts = maxRetryAttempts(policy)\n\n let attempt = 1\n\n for (;;) {\n try {\n return await runStep()\n } catch (error) {\n if (attempt >= maxAttempts) {\n throw error\n }\n\n attempt += 1\n }\n }\n}\n\nexport async function runVercelAgentWorkflow(\n config: VercelAgentWorkflowLoopConfig\n): Promise<VercelAgentWorkflowRunResult> {\n const {\n input,\n maxTurns: configuredMaxTurns,\n runModelStep,\n runToolBatchStep,\n closeStream,\n writeError,\n awaitInput,\n modelStepRetry,\n toolBatchStepRetry,\n awaitInputRetry,\n closeStreamRetry\n } = config\n let state: SerializableWorkflowState = {\n request: input.request,\n createdMessages: [],\n turn: 1,\n eventSequence: 0\n }\n const maxTurns = configuredMaxTurns ?? defaultMaxWorkflowTurns\n\n for (let step = 0; step < maxTurns; step++) {\n const modelResult = await settleWorkflowStep(\n retryWorkflowStep(() => runModelStep({ context: input.context, state }), modelStepRetry)\n )\n\n if (modelResult._tag === 'Failure') {\n await writeErrorSafely(writeError, modelResult.error)\n return {\n _tag: 'ModelStepFailed',\n turn: state.turn,\n error: modelResult.error,\n state\n }\n }\n\n if (modelResult.value.done) {\n const closeResult = await settleWorkflowStep(retryWorkflowStep(closeStream, closeStreamRetry))\n\n if (closeResult._tag === 'Failure') {\n await writeErrorSafely(writeError, closeResult.error)\n\n return {\n _tag: 'CloseStreamFailed',\n turns: modelResult.value.turn,\n error: closeResult.error,\n state\n }\n }\n\n return {\n _tag: 'Completed',\n turns: modelResult.value.turn,\n state\n }\n }\n\n let completedToolsResult: VercelAgentWorkflowToolBatchStepResult | undefined\n let toolHitlResponses: ReadonlyArray<unknown> = []\n let toolEventSequence = modelResult.value.eventSequence ?? state.eventSequence\n\n for (;;) {\n const toolsResult = await settleWorkflowStep(\n retryWorkflowStep(\n () =>\n runToolBatchStep({\n context: input.context,\n request: input.request,\n calls: modelResult.value.toolCalls,\n createdMessages: modelResult.value.createdMessages,\n hitlResponses: toolHitlResponses,\n usage: modelResult.value.usage,\n turn: modelResult.value.turn,\n eventSequence: toolEventSequence\n }),\n toolBatchStepRetry\n )\n )\n\n if (toolsResult._tag === 'Failure') {\n await writeErrorSafely(writeError, toolsResult.error)\n\n return {\n _tag: 'ToolBatchStepFailed',\n turn: modelResult.value.turn,\n error: toolsResult.error,\n state\n }\n }\n\n if (toolsResult.value.awaitingInput === undefined) {\n completedToolsResult = toolsResult.value\n break\n }\n\n const awaitingInput = toolsResult.value.awaitingInput\n toolEventSequence =\n awaitingInput.eventSequence ?? toolsResult.value.eventSequence ?? toolEventSequence\n\n const hitlResponse = await settleWorkflowStep(\n retryWorkflowStep(() => {\n if (awaitInput === undefined) {\n return Promise.reject(missingAwaitInputHandlerError())\n }\n\n return awaitInput(awaitingInput)\n }, awaitInputRetry)\n )\n\n if (hitlResponse._tag === 'Failure') {\n await writeErrorSafely(writeError, hitlResponse.error)\n\n return {\n _tag: 'AwaitInputFailed',\n turn: modelResult.value.turn,\n error: hitlResponse.error,\n awaitingInput,\n state\n }\n }\n\n toolHitlResponses = [...toolHitlResponses, hitlResponse.value]\n }\n\n if (completedToolsResult === undefined) {\n const error = new Error('Vercel agent workflow tool batch did not complete')\n await writeErrorSafely(writeError, error)\n\n return {\n _tag: 'ToolBatchStepFailed',\n turn: modelResult.value.turn,\n error,\n state\n }\n }\n\n state = {\n request: input.request,\n messages: [...modelResult.value.messages, ...completedToolsResult.messages],\n createdMessages: completedToolsResult.createdMessages,\n usage: modelResult.value.usage,\n turn: modelResult.value.turn + 1,\n eventSequence: completedToolsResult.eventSequence ?? toolEventSequence\n }\n }\n\n const error = workflowMaxTurnsError(maxTurns)\n await writeErrorSafely(writeError, error)\n\n return {\n _tag: 'MaxTurnsExceeded',\n maxTurns,\n error,\n state\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workflow-loop.mjs","names":[],"sources":["../src/workflow-loop.ts"],"sourcesContent":["export type VercelAgentWorkflowInput = {\n readonly request: unknown\n readonly context: unknown\n}\n\nexport type SerializableWorkflowState = {\n readonly request: unknown\n readonly messages?: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n readonly usage?: unknown\n readonly turn: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowModelStepInput = {\n readonly context: unknown\n readonly state: SerializableWorkflowState\n}\n\nexport type VercelAgentWorkflowModelStepResult = {\n readonly done: boolean\n readonly messages: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n readonly toolCalls: ReadonlyArray<unknown>\n readonly usage: unknown\n readonly turn: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowToolBatchStepInput = {\n readonly context: unknown\n readonly request: unknown\n readonly calls: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n readonly hitlResponses?: ReadonlyArray<unknown>\n readonly usage?: unknown\n readonly turn?: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowAwaitingInput = {\n readonly hookToken: string\n readonly requests: ReadonlyArray<unknown>\n readonly messages: ReadonlyArray<unknown>\n readonly usage: unknown\n readonly turns: number\n readonly eventSequence?: number\n}\n\nexport type VercelAgentWorkflowToolBatchStepResult = {\n readonly messages: ReadonlyArray<unknown>\n readonly createdMessages: ReadonlyArray<unknown>\n /** Updated cumulative usage when tools run nested model work such as subagents. */\n readonly usage?: unknown\n readonly awaitingInput?: VercelAgentWorkflowAwaitingInput\n readonly eventSequence?: number\n /**\n * A plain wire-safe failure captured after partial progress. Returning it instead of rejecting\n * lets orchestration retain usage and event sequencing from completed sibling tools.\n */\n readonly failure?: unknown\n}\n\nexport type WorkflowStepResult<A> =\n | {\n readonly _tag: 'Success'\n readonly value: A\n }\n | {\n readonly _tag: 'Failure'\n readonly error: unknown\n }\n\nexport type VercelAgentWorkflowStepRetryPolicy = {\n readonly maxAttempts: number\n}\n\nexport type VercelAgentWorkflowRunResult =\n | {\n readonly _tag: 'Completed'\n readonly turns: number\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'ModelStepFailed'\n readonly turn: number\n readonly error: unknown\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'ToolBatchStepFailed'\n readonly turn: number\n readonly error: unknown\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'AwaitInputFailed'\n readonly turn: number\n readonly error: unknown\n readonly awaitingInput: VercelAgentWorkflowAwaitingInput\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'CloseStreamFailed'\n readonly turns: number\n readonly error: unknown\n readonly state: SerializableWorkflowState\n }\n | {\n readonly _tag: 'MaxTurnsExceeded'\n readonly maxTurns: number\n readonly error: Error\n readonly state: SerializableWorkflowState\n }\n\nexport type VercelAgentWorkflowLoopConfig = {\n readonly input: VercelAgentWorkflowInput\n readonly maxTurns?: number\n readonly runModelStep: (\n input: VercelAgentWorkflowModelStepInput\n ) => Promise<VercelAgentWorkflowModelStepResult>\n readonly runToolBatchStep: (\n input: VercelAgentWorkflowToolBatchStepInput\n ) => Promise<VercelAgentWorkflowToolBatchStepResult>\n /** Called only after a model step returns `done: true`. */\n readonly closeStream: () => Promise<void>\n /**\n * Best-effort final failure handler. It must write a safe final error and\n * close the failure stream; rejection is suppressed in favor of the\n * original structured run result.\n */\n readonly writeError: (error: unknown) => Promise<void>\n /** A successful await suspends/resumes without invoking either finalizer. */\n readonly awaitInput?: (input: VercelAgentWorkflowAwaitingInput) => Promise<unknown>\n readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy\n readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy\n readonly awaitInputRetry?: VercelAgentWorkflowStepRetryPolicy\n readonly closeStreamRetry?: VercelAgentWorkflowStepRetryPolicy\n}\n\nexport const defaultMaxWorkflowTurns = 500\nexport const noWorkflowStepRetry: VercelAgentWorkflowStepRetryPolicy = { maxAttempts: 1 }\n\nexport const settleWorkflowStep = <A>(promise: Promise<A>): Promise<WorkflowStepResult<A>> =>\n promise.then(\n value => ({ _tag: 'Success', value }),\n error => ({ _tag: 'Failure', error })\n )\n\nconst workflowMaxTurnsError = (maxTurns: number) =>\n new Error(`Vercel agent workflow exceeded max turns: ${maxTurns}`)\n\nconst writeErrorSafely = (writeError: (error: unknown) => Promise<void>, error: unknown) =>\n writeError(error).catch(() => undefined)\n\nconst missingAwaitInputHandlerError = () =>\n new Error(\n 'Vercel agent workflow awaiting input requested but no awaitInput handler is configured'\n )\n\nconst maxRetryAttempts = (policy: VercelAgentWorkflowStepRetryPolicy | undefined) => {\n const configured = policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts\n\n return Number.isFinite(configured) ? Math.max(1, Math.floor(configured)) : 1\n}\n\nconst maxWorkflowTurns = (configured: number | undefined) => {\n if (configured === undefined || !Number.isFinite(configured)) {\n return defaultMaxWorkflowTurns\n }\n\n return Math.max(1, Math.floor(configured))\n}\n\nexport async function retryWorkflowStep<A>(\n runStep: () => Promise<A>,\n policy?: VercelAgentWorkflowStepRetryPolicy\n): Promise<A> {\n const maxAttempts = maxRetryAttempts(policy)\n\n let attempt = 1\n\n for (;;) {\n try {\n return await runStep()\n } catch (error) {\n if (attempt >= maxAttempts) {\n throw error\n }\n\n attempt += 1\n }\n }\n}\n\nexport async function runVercelAgentWorkflow(\n config: VercelAgentWorkflowLoopConfig\n): Promise<VercelAgentWorkflowRunResult> {\n const {\n input,\n maxTurns: configuredMaxTurns,\n runModelStep,\n runToolBatchStep,\n closeStream,\n writeError,\n awaitInput,\n modelStepRetry,\n toolBatchStepRetry,\n awaitInputRetry,\n closeStreamRetry\n } = config\n let state: SerializableWorkflowState = {\n request: input.request,\n createdMessages: [],\n turn: 1,\n eventSequence: 0\n }\n const maxTurns = maxWorkflowTurns(configuredMaxTurns)\n\n for (let step = 0; step < maxTurns; step++) {\n const modelResult = await settleWorkflowStep(\n retryWorkflowStep(() => runModelStep({ context: input.context, state }), modelStepRetry)\n )\n\n if (modelResult._tag === 'Failure') {\n await writeErrorSafely(writeError, modelResult.error)\n return {\n _tag: 'ModelStepFailed',\n turn: state.turn,\n error: modelResult.error,\n state\n }\n }\n\n if (modelResult.value.done) {\n const terminalState: SerializableWorkflowState = {\n request: input.request,\n messages: modelResult.value.messages,\n createdMessages: modelResult.value.createdMessages,\n usage: modelResult.value.usage,\n turn: modelResult.value.turn,\n eventSequence: modelResult.value.eventSequence ?? state.eventSequence\n }\n const closeResult = await settleWorkflowStep(retryWorkflowStep(closeStream, closeStreamRetry))\n\n if (closeResult._tag === 'Failure') {\n await writeErrorSafely(writeError, closeResult.error)\n\n return {\n _tag: 'CloseStreamFailed',\n turns: modelResult.value.turn,\n error: closeResult.error,\n state: terminalState\n }\n }\n\n return {\n _tag: 'Completed',\n turns: modelResult.value.turn,\n state: terminalState\n }\n }\n\n let completedToolsResult: VercelAgentWorkflowToolBatchStepResult | undefined\n let toolHitlResponses: ReadonlyArray<unknown> = []\n let cumulativeUsage = modelResult.value.usage\n let toolEventSequence = modelResult.value.eventSequence ?? state.eventSequence\n const currentTurnState = (\n toolResult?: VercelAgentWorkflowToolBatchStepResult\n ): SerializableWorkflowState => ({\n request: input.request,\n messages:\n toolResult === undefined\n ? modelResult.value.messages\n : [...modelResult.value.messages, ...toolResult.messages],\n createdMessages: toolResult?.createdMessages ?? modelResult.value.createdMessages,\n usage: cumulativeUsage,\n turn: modelResult.value.turn,\n eventSequence: toolEventSequence\n })\n\n for (;;) {\n const toolsResult = await settleWorkflowStep(\n retryWorkflowStep(\n () =>\n runToolBatchStep({\n context: input.context,\n request: input.request,\n calls: modelResult.value.toolCalls,\n createdMessages: modelResult.value.createdMessages,\n hitlResponses: toolHitlResponses,\n usage: cumulativeUsage,\n turn: modelResult.value.turn,\n eventSequence: toolEventSequence\n }),\n toolBatchStepRetry\n )\n )\n\n if (toolsResult._tag === 'Failure') {\n await writeErrorSafely(writeError, toolsResult.error)\n\n return {\n _tag: 'ToolBatchStepFailed',\n turn: modelResult.value.turn,\n error: toolsResult.error,\n state: currentTurnState()\n }\n }\n\n cumulativeUsage = toolsResult.value.usage ?? cumulativeUsage\n toolEventSequence = toolsResult.value.eventSequence ?? toolEventSequence\n\n if (toolsResult.value.failure !== undefined) {\n await writeErrorSafely(writeError, toolsResult.value.failure)\n\n return {\n _tag: 'ToolBatchStepFailed',\n turn: modelResult.value.turn,\n error: toolsResult.value.failure,\n state: currentTurnState(toolsResult.value)\n }\n }\n\n if (toolsResult.value.awaitingInput === undefined) {\n completedToolsResult = toolsResult.value\n break\n }\n\n const awaitingInput = toolsResult.value.awaitingInput\n cumulativeUsage = toolsResult.value.usage ?? awaitingInput.usage ?? cumulativeUsage\n toolEventSequence = awaitingInput.eventSequence ?? toolEventSequence\n\n const hitlResponse = await settleWorkflowStep(\n retryWorkflowStep(() => {\n if (awaitInput === undefined) {\n return Promise.reject(missingAwaitInputHandlerError())\n }\n\n return awaitInput(awaitingInput)\n }, awaitInputRetry)\n )\n\n if (hitlResponse._tag === 'Failure') {\n await writeErrorSafely(writeError, hitlResponse.error)\n\n return {\n _tag: 'AwaitInputFailed',\n turn: modelResult.value.turn,\n error: hitlResponse.error,\n awaitingInput,\n state: currentTurnState(toolsResult.value)\n }\n }\n\n toolHitlResponses = [...toolHitlResponses, hitlResponse.value]\n }\n\n if (completedToolsResult === undefined) {\n const error = new Error('Vercel agent workflow tool batch did not complete')\n await writeErrorSafely(writeError, error)\n\n return {\n _tag: 'ToolBatchStepFailed',\n turn: modelResult.value.turn,\n error,\n state\n }\n }\n\n state = {\n request: input.request,\n messages: [...modelResult.value.messages, ...completedToolsResult.messages],\n createdMessages: completedToolsResult.createdMessages,\n usage: completedToolsResult.usage ?? cumulativeUsage,\n turn: modelResult.value.turn + 1,\n eventSequence: completedToolsResult.eventSequence ?? toolEventSequence\n }\n }\n\n const error = workflowMaxTurnsError(maxTurns)\n await writeErrorSafely(writeError, error)\n\n return {\n _tag: 'MaxTurnsExceeded',\n maxTurns,\n error,\n state\n }\n}\n"],"mappings":";AA4IA,MAAa,0BAA0B;AACvC,MAAa,sBAA0D,EAAE,aAAa,EAAE;AAExF,MAAa,sBAAyB,YACpC,QAAQ,MACN,WAAU;CAAE,MAAM;CAAW;AAAM,KACnC,WAAU;CAAE,MAAM;CAAW;AAAM,EACrC;AAEF,MAAM,yBAAyB,6BAC7B,IAAI,MAAM,6CAA6C,UAAU;AAEnE,MAAM,oBAAoB,YAA+C,UACvE,WAAW,KAAK,EAAE,YAAY,KAAA,CAAS;AAEzC,MAAM,sDACJ,IAAI,MACF,wFACF;AAEF,MAAM,oBAAoB,WAA2D;CACnF,MAAM,aAAa,QAAQ,eAAe,oBAAoB;CAE9D,OAAO,OAAO,SAAS,UAAU,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,CAAC,IAAI;AAC7E;AAEA,MAAM,oBAAoB,eAAmC;CAC3D,IAAI,eAAe,KAAA,KAAa,CAAC,OAAO,SAAS,UAAU,GACzD,OAAA;CAGF,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,CAAC;AAC3C;AAEA,eAAsB,kBACpB,SACA,QACY;CACZ,MAAM,cAAc,iBAAiB,MAAM;CAE3C,IAAI,UAAU;CAEd,SACE,IAAI;EACF,OAAO,MAAM,QAAQ;CACvB,SAAS,OAAO;EACd,IAAI,WAAW,aACb,MAAM;EAGR,WAAW;CACb;AAEJ;AAEA,eAAsB,uBACpB,QACuC;CACvC,MAAM,EACJ,OACA,UAAU,oBACV,cACA,kBACA,aACA,YACA,YACA,gBACA,oBACA,iBACA,qBACE;CACJ,IAAI,QAAmC;EACrC,SAAS,MAAM;EACf,iBAAiB,CAAC;EAClB,MAAM;EACN,eAAe;CACjB;CACA,MAAM,WAAW,iBAAiB,kBAAkB;CAEpD,KAAK,IAAI,OAAO,GAAG,OAAO,UAAU,QAAQ;EAC1C,MAAM,cAAc,MAAM,mBACxB,wBAAwB,aAAa;GAAE,SAAS,MAAM;GAAS;EAAM,CAAC,GAAG,cAAc,CACzF;EAEA,IAAI,YAAY,SAAS,WAAW;GAClC,MAAM,iBAAiB,YAAY,YAAY,KAAK;GACpD,OAAO;IACL,MAAM;IACN,MAAM,MAAM;IACZ,OAAO,YAAY;IACnB;GACF;EACF;EAEA,IAAI,YAAY,MAAM,MAAM;GAC1B,MAAM,gBAA2C;IAC/C,SAAS,MAAM;IACf,UAAU,YAAY,MAAM;IAC5B,iBAAiB,YAAY,MAAM;IACnC,OAAO,YAAY,MAAM;IACzB,MAAM,YAAY,MAAM;IACxB,eAAe,YAAY,MAAM,iBAAiB,MAAM;GAC1D;GACA,MAAM,cAAc,MAAM,mBAAmB,kBAAkB,aAAa,gBAAgB,CAAC;GAE7F,IAAI,YAAY,SAAS,WAAW;IAClC,MAAM,iBAAiB,YAAY,YAAY,KAAK;IAEpD,OAAO;KACL,MAAM;KACN,OAAO,YAAY,MAAM;KACzB,OAAO,YAAY;KACnB,OAAO;IACT;GACF;GAEA,OAAO;IACL,MAAM;IACN,OAAO,YAAY,MAAM;IACzB,OAAO;GACT;EACF;EAEA,IAAI;EACJ,IAAI,oBAA4C,CAAC;EACjD,IAAI,kBAAkB,YAAY,MAAM;EACxC,IAAI,oBAAoB,YAAY,MAAM,iBAAiB,MAAM;EACjE,MAAM,oBACJ,gBAC+B;GAC/B,SAAS,MAAM;GACf,UACE,eAAe,KAAA,IACX,YAAY,MAAM,WAClB,CAAC,GAAG,YAAY,MAAM,UAAU,GAAG,WAAW,QAAQ;GAC5D,iBAAiB,YAAY,mBAAmB,YAAY,MAAM;GAClE,OAAO;GACP,MAAM,YAAY,MAAM;GACxB,eAAe;EACjB;EAEA,SAAS;GACP,MAAM,cAAc,MAAM,mBACxB,wBAEI,iBAAiB;IACf,SAAS,MAAM;IACf,SAAS,MAAM;IACf,OAAO,YAAY,MAAM;IACzB,iBAAiB,YAAY,MAAM;IACnC,eAAe;IACf,OAAO;IACP,MAAM,YAAY,MAAM;IACxB,eAAe;GACjB,CAAC,GACH,kBACF,CACF;GAEA,IAAI,YAAY,SAAS,WAAW;IAClC,MAAM,iBAAiB,YAAY,YAAY,KAAK;IAEpD,OAAO;KACL,MAAM;KACN,MAAM,YAAY,MAAM;KACxB,OAAO,YAAY;KACnB,OAAO,iBAAiB;IAC1B;GACF;GAEA,kBAAkB,YAAY,MAAM,SAAS;GAC7C,oBAAoB,YAAY,MAAM,iBAAiB;GAEvD,IAAI,YAAY,MAAM,YAAY,KAAA,GAAW;IAC3C,MAAM,iBAAiB,YAAY,YAAY,MAAM,OAAO;IAE5D,OAAO;KACL,MAAM;KACN,MAAM,YAAY,MAAM;KACxB,OAAO,YAAY,MAAM;KACzB,OAAO,iBAAiB,YAAY,KAAK;IAC3C;GACF;GAEA,IAAI,YAAY,MAAM,kBAAkB,KAAA,GAAW;IACjD,uBAAuB,YAAY;IACnC;GACF;GAEA,MAAM,gBAAgB,YAAY,MAAM;GACxC,kBAAkB,YAAY,MAAM,SAAS,cAAc,SAAS;GACpE,oBAAoB,cAAc,iBAAiB;GAEnD,MAAM,eAAe,MAAM,mBACzB,wBAAwB;IACtB,IAAI,eAAe,KAAA,GACjB,OAAO,QAAQ,OAAO,8BAA8B,CAAC;IAGvD,OAAO,WAAW,aAAa;GACjC,GAAG,eAAe,CACpB;GAEA,IAAI,aAAa,SAAS,WAAW;IACnC,MAAM,iBAAiB,YAAY,aAAa,KAAK;IAErD,OAAO;KACL,MAAM;KACN,MAAM,YAAY,MAAM;KACxB,OAAO,aAAa;KACpB;KACA,OAAO,iBAAiB,YAAY,KAAK;IAC3C;GACF;GAEA,oBAAoB,CAAC,GAAG,mBAAmB,aAAa,KAAK;EAC/D;EAEA,IAAI,yBAAyB,KAAA,GAAW;GACtC,MAAM,wBAAQ,IAAI,MAAM,mDAAmD;GAC3E,MAAM,iBAAiB,YAAY,KAAK;GAExC,OAAO;IACL,MAAM;IACN,MAAM,YAAY,MAAM;IACxB;IACA;GACF;EACF;EAEA,QAAQ;GACN,SAAS,MAAM;GACf,UAAU,CAAC,GAAG,YAAY,MAAM,UAAU,GAAG,qBAAqB,QAAQ;GAC1E,iBAAiB,qBAAqB;GACtC,OAAO,qBAAqB,SAAS;GACrC,MAAM,YAAY,MAAM,OAAO;GAC/B,eAAe,qBAAqB,iBAAiB;EACvD;CACF;CAEA,MAAM,QAAQ,sBAAsB,QAAQ;CAC5C,MAAM,iBAAiB,YAAY,KAAK;CAExC,OAAO;EACL,MAAM;EACN;EACA;EACA;CACF;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yolk-sdk/vercel-workflows",
3
- "version": "0.1.0-canary.56",
3
+ "version": "0.1.0-canary.58",
4
4
  "description": "Vercel Workflow orchestration primitives for Yolk agent loops.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,8 +50,15 @@ export type VercelAgentWorkflowAwaitingInput = {
50
50
  export type VercelAgentWorkflowToolBatchStepResult = {
51
51
  readonly messages: ReadonlyArray<unknown>
52
52
  readonly createdMessages: ReadonlyArray<unknown>
53
+ /** Updated cumulative usage when tools run nested model work such as subagents. */
54
+ readonly usage?: unknown
53
55
  readonly awaitingInput?: VercelAgentWorkflowAwaitingInput
54
56
  readonly eventSequence?: number
57
+ /**
58
+ * A plain wire-safe failure captured after partial progress. Returning it instead of rejecting
59
+ * lets orchestration retain usage and event sequencing from completed sibling tools.
60
+ */
61
+ readonly failure?: unknown
55
62
  }
56
63
 
57
64
  export type WorkflowStepResult<A> =
@@ -157,6 +164,14 @@ const maxRetryAttempts = (policy: VercelAgentWorkflowStepRetryPolicy | undefined
157
164
  return Number.isFinite(configured) ? Math.max(1, Math.floor(configured)) : 1
158
165
  }
159
166
 
167
+ const maxWorkflowTurns = (configured: number | undefined) => {
168
+ if (configured === undefined || !Number.isFinite(configured)) {
169
+ return defaultMaxWorkflowTurns
170
+ }
171
+
172
+ return Math.max(1, Math.floor(configured))
173
+ }
174
+
160
175
  export async function retryWorkflowStep<A>(
161
176
  runStep: () => Promise<A>,
162
177
  policy?: VercelAgentWorkflowStepRetryPolicy
@@ -200,7 +215,7 @@ export async function runVercelAgentWorkflow(
200
215
  turn: 1,
201
216
  eventSequence: 0
202
217
  }
203
- const maxTurns = configuredMaxTurns ?? defaultMaxWorkflowTurns
218
+ const maxTurns = maxWorkflowTurns(configuredMaxTurns)
204
219
 
205
220
  for (let step = 0; step < maxTurns; step++) {
206
221
  const modelResult = await settleWorkflowStep(
@@ -218,6 +233,14 @@ export async function runVercelAgentWorkflow(
218
233
  }
219
234
 
220
235
  if (modelResult.value.done) {
236
+ const terminalState: SerializableWorkflowState = {
237
+ request: input.request,
238
+ messages: modelResult.value.messages,
239
+ createdMessages: modelResult.value.createdMessages,
240
+ usage: modelResult.value.usage,
241
+ turn: modelResult.value.turn,
242
+ eventSequence: modelResult.value.eventSequence ?? state.eventSequence
243
+ }
221
244
  const closeResult = await settleWorkflowStep(retryWorkflowStep(closeStream, closeStreamRetry))
222
245
 
223
246
  if (closeResult._tag === 'Failure') {
@@ -227,20 +250,34 @@ export async function runVercelAgentWorkflow(
227
250
  _tag: 'CloseStreamFailed',
228
251
  turns: modelResult.value.turn,
229
252
  error: closeResult.error,
230
- state
253
+ state: terminalState
231
254
  }
232
255
  }
233
256
 
234
257
  return {
235
258
  _tag: 'Completed',
236
259
  turns: modelResult.value.turn,
237
- state
260
+ state: terminalState
238
261
  }
239
262
  }
240
263
 
241
264
  let completedToolsResult: VercelAgentWorkflowToolBatchStepResult | undefined
242
265
  let toolHitlResponses: ReadonlyArray<unknown> = []
266
+ let cumulativeUsage = modelResult.value.usage
243
267
  let toolEventSequence = modelResult.value.eventSequence ?? state.eventSequence
268
+ const currentTurnState = (
269
+ toolResult?: VercelAgentWorkflowToolBatchStepResult
270
+ ): SerializableWorkflowState => ({
271
+ request: input.request,
272
+ messages:
273
+ toolResult === undefined
274
+ ? modelResult.value.messages
275
+ : [...modelResult.value.messages, ...toolResult.messages],
276
+ createdMessages: toolResult?.createdMessages ?? modelResult.value.createdMessages,
277
+ usage: cumulativeUsage,
278
+ turn: modelResult.value.turn,
279
+ eventSequence: toolEventSequence
280
+ })
244
281
 
245
282
  for (;;) {
246
283
  const toolsResult = await settleWorkflowStep(
@@ -252,7 +289,7 @@ export async function runVercelAgentWorkflow(
252
289
  calls: modelResult.value.toolCalls,
253
290
  createdMessages: modelResult.value.createdMessages,
254
291
  hitlResponses: toolHitlResponses,
255
- usage: modelResult.value.usage,
292
+ usage: cumulativeUsage,
256
293
  turn: modelResult.value.turn,
257
294
  eventSequence: toolEventSequence
258
295
  }),
@@ -267,7 +304,21 @@ export async function runVercelAgentWorkflow(
267
304
  _tag: 'ToolBatchStepFailed',
268
305
  turn: modelResult.value.turn,
269
306
  error: toolsResult.error,
270
- state
307
+ state: currentTurnState()
308
+ }
309
+ }
310
+
311
+ cumulativeUsage = toolsResult.value.usage ?? cumulativeUsage
312
+ toolEventSequence = toolsResult.value.eventSequence ?? toolEventSequence
313
+
314
+ if (toolsResult.value.failure !== undefined) {
315
+ await writeErrorSafely(writeError, toolsResult.value.failure)
316
+
317
+ return {
318
+ _tag: 'ToolBatchStepFailed',
319
+ turn: modelResult.value.turn,
320
+ error: toolsResult.value.failure,
321
+ state: currentTurnState(toolsResult.value)
271
322
  }
272
323
  }
273
324
 
@@ -277,8 +328,8 @@ export async function runVercelAgentWorkflow(
277
328
  }
278
329
 
279
330
  const awaitingInput = toolsResult.value.awaitingInput
280
- toolEventSequence =
281
- awaitingInput.eventSequence ?? toolsResult.value.eventSequence ?? toolEventSequence
331
+ cumulativeUsage = toolsResult.value.usage ?? awaitingInput.usage ?? cumulativeUsage
332
+ toolEventSequence = awaitingInput.eventSequence ?? toolEventSequence
282
333
 
283
334
  const hitlResponse = await settleWorkflowStep(
284
335
  retryWorkflowStep(() => {
@@ -298,7 +349,7 @@ export async function runVercelAgentWorkflow(
298
349
  turn: modelResult.value.turn,
299
350
  error: hitlResponse.error,
300
351
  awaitingInput,
301
- state
352
+ state: currentTurnState(toolsResult.value)
302
353
  }
303
354
  }
304
355
 
@@ -321,7 +372,7 @@ export async function runVercelAgentWorkflow(
321
372
  request: input.request,
322
373
  messages: [...modelResult.value.messages, ...completedToolsResult.messages],
323
374
  createdMessages: completedToolsResult.createdMessages,
324
- usage: modelResult.value.usage,
375
+ usage: completedToolsResult.usage ?? cumulativeUsage,
325
376
  turn: modelResult.value.turn + 1,
326
377
  eventSequence: completedToolsResult.eventSequence ?? toolEventSequence
327
378
  }