@yolk-sdk/vercel-workflows 0.1.0-canary.48 → 0.1.0-canary.49
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 +46 -28
- package/dist/workflow-loop.d.mts +7 -2
- package/dist/workflow-loop.d.mts.map +1 -1
- package/dist/workflow-loop.mjs +4 -1
- package/dist/workflow-loop.mjs.map +1 -1
- package/package.json +1 -1
- package/src/workflow-loop.ts +12 -2
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ queues, hooks, or stream storage.
|
|
|
60
60
|
- tool batch result: return one ordered tool-result message per host call, including failed `isError` results
|
|
61
61
|
- awaiting-input state: carry pending HITL hook data, wait through `awaitInput`, then rerun the
|
|
62
62
|
same tool batch with accumulated responses
|
|
63
|
-
- close step: flush/close output stream
|
|
63
|
+
- close step: flush/close output stream after a successful final model step
|
|
64
64
|
- terminal status: completed, step failure, await-input failure, close failure, or max turns exceeded
|
|
65
65
|
|
|
66
66
|
Continuation state stays plain serializable data for Workflow persistence.
|
|
@@ -71,30 +71,23 @@ Host apps keep concrete Workflow directives local and pass them into the package
|
|
|
71
71
|
|
|
72
72
|
```ts
|
|
73
73
|
import { createHook } from 'workflow'
|
|
74
|
-
import { Effect } from 'effect'
|
|
75
74
|
import { runVercelAgentWorkflow } from '@yolk-sdk/vercel-workflows'
|
|
76
75
|
|
|
77
|
-
const workflowStep = <A>(body: () => Promise<A>) =>
|
|
78
|
-
Effect.tryPromise({ try: body, catch: error => error })
|
|
79
|
-
|
|
80
76
|
export async function runAgentWorkflow(input: { request: unknown; context: unknown }) {
|
|
81
77
|
'use workflow'
|
|
82
78
|
|
|
83
|
-
return await
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
)
|
|
79
|
+
return await runVercelAgentWorkflow({
|
|
80
|
+
input,
|
|
81
|
+
runModelStep,
|
|
82
|
+
runToolBatchStep,
|
|
83
|
+
closeStream,
|
|
84
|
+
writeError,
|
|
85
|
+
awaitInput: async awaitingInput => {
|
|
86
|
+
using hook = createHook<unknown>({ token: awaitingInput.hookToken })
|
|
87
|
+
|
|
88
|
+
return await hook
|
|
89
|
+
}
|
|
90
|
+
})
|
|
98
91
|
}
|
|
99
92
|
```
|
|
100
93
|
|
|
@@ -118,6 +111,8 @@ tokens that the resume side can reconstruct or store.
|
|
|
118
111
|
|
|
119
112
|
Default retry policy is `noWorkflowStepRetry` (`maxAttempts: 1`). Retries are opt-in because
|
|
120
113
|
streamed retries can duplicate chunks unless host/client de-dupe is ready.
|
|
114
|
+
Finite `maxAttempts` values are floored and clamped to at least one; non-finite values normalize to
|
|
115
|
+
one. Retries have no built-in backoff.
|
|
121
116
|
|
|
122
117
|
## Durable stream events
|
|
123
118
|
|
|
@@ -129,7 +124,9 @@ Use `writeDurableAgentEvent` to assign deterministic ids and write NDJSON:
|
|
|
129
124
|
|
|
130
125
|
```ts
|
|
131
126
|
const state = makeDurableAgentEventSequencerState(eventSequence)
|
|
132
|
-
const sequenced =
|
|
127
|
+
const sequenced =
|
|
128
|
+
yield *
|
|
129
|
+
writeDurableAgentEvent({
|
|
133
130
|
writer,
|
|
134
131
|
event,
|
|
135
132
|
streamId: `workflow:${runId}`,
|
|
@@ -149,12 +146,23 @@ active-run locking, stale-run guards, and cancellation behavior.
|
|
|
149
146
|
|
|
150
147
|
## Terminal barriers
|
|
151
148
|
|
|
152
|
-
For durable host streams, terminal events are commit barriers. Emit live/progress events
|
|
153
|
-
commit canonical host state, then write the
|
|
154
|
-
|
|
149
|
+
For durable host streams, protocol terminal events are commit barriers. Emit live/progress events
|
|
150
|
+
first, commit canonical host state, then write the event.
|
|
151
|
+
|
|
152
|
+
“Terminal” in `isTerminalAgentEvent` means a logical client-consumption boundary, not necessarily a
|
|
153
|
+
final Workflow state. Final callback ownership is:
|
|
154
|
+
|
|
155
|
+
```txt
|
|
156
|
+
AgentEnd: durable completion commit -> AgentEnd -> closeStream
|
|
157
|
+
AgentError: failure commit/mapping -> AgentError -> writeError-owned close
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`AgentAwaitingInput` is terminal for the current client consumption window but resumable for the
|
|
161
|
+
Workflow run. Persist pending HITL state and write it without finalizing the durable stream solely
|
|
162
|
+
because `isTerminalAgentEvent` returns true:
|
|
155
163
|
|
|
156
164
|
```txt
|
|
157
|
-
|
|
165
|
+
persist pending HITL -> AgentAwaitingInput -> release writer lock (do not close) -> await hook -> continue
|
|
158
166
|
```
|
|
159
167
|
|
|
160
168
|
This lets clients stop consuming at a protocol-terminal event and immediately revalidate or reconnect
|
|
@@ -165,7 +173,9 @@ Use `commitThenWriteTerminalEvent` when a step must commit host state before wri
|
|
|
165
173
|
event:
|
|
166
174
|
|
|
167
175
|
```ts
|
|
168
|
-
const result =
|
|
176
|
+
const result =
|
|
177
|
+
yield *
|
|
178
|
+
commitThenWriteTerminalEvent({
|
|
169
179
|
terminal,
|
|
170
180
|
commit: persistState,
|
|
171
181
|
write: event => writeDurableAgentEvent({ writer, event, streamId, turn, state }),
|
|
@@ -175,8 +185,14 @@ const result = yield* commitThenWriteTerminalEvent({
|
|
|
175
185
|
```
|
|
176
186
|
|
|
177
187
|
The helper never writes the success terminal before `commit` succeeds. If `commit` fails, it writes
|
|
178
|
-
the host-provided terminal error event instead.
|
|
179
|
-
|
|
188
|
+
the host-provided terminal error event instead. It can protect either a final event or an
|
|
189
|
+
`AgentAwaitingInput` persistence barrier; it never decides closure from `_tag` and never closes
|
|
190
|
+
writers.
|
|
191
|
+
|
|
192
|
+
`runVercelAgentWorkflow` calls `closeStream` only after `runModelStep` returns `done: true`. On model,
|
|
193
|
+
tool, await-input, or max-turn failure it calls `writeError` best-effort and returns without calling
|
|
194
|
+
`closeStream`; `writeError` must write a safe run-final error and attempt to close that failure path.
|
|
195
|
+
Never close on resumable `AgentAwaitingInput`.
|
|
180
196
|
|
|
181
197
|
## Host responsibilities
|
|
182
198
|
|
|
@@ -188,6 +204,8 @@ surrounding workflow loop or stream owner after terminal handling settles.
|
|
|
188
204
|
- Emit replay-safe event ids for durable streams; scope stream ids per independent run and de-dupe
|
|
189
205
|
by `eventId` client-side.
|
|
190
206
|
- Write durable terminal events only after host persistence has settled.
|
|
207
|
+
- Implement `closeStream` for successful final closure and `writeError` for safe final error write
|
|
208
|
+
plus failure closure. Release, but do not close, the writer after `AgentAwaitingInput`.
|
|
191
209
|
- Test directive behavior with `@workflow/vitest` when changing package-owned Workflow files.
|
|
192
210
|
|
|
193
211
|
## Boundaries
|
package/dist/workflow-loop.d.mts
CHANGED
|
@@ -93,9 +93,14 @@ type VercelAgentWorkflowLoopConfig = {
|
|
|
93
93
|
readonly input: VercelAgentWorkflowInput;
|
|
94
94
|
readonly maxTurns?: number;
|
|
95
95
|
readonly runModelStep: (input: VercelAgentWorkflowModelStepInput) => Promise<VercelAgentWorkflowModelStepResult>;
|
|
96
|
-
readonly runToolBatchStep: (input: VercelAgentWorkflowToolBatchStepInput) => Promise<VercelAgentWorkflowToolBatchStepResult>;
|
|
96
|
+
readonly runToolBatchStep: (input: VercelAgentWorkflowToolBatchStepInput) => Promise<VercelAgentWorkflowToolBatchStepResult>; /** Called only after a model step returns `done: true`. */
|
|
97
97
|
readonly closeStream: () => Promise<void>;
|
|
98
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Best-effort final failure handler. It must write a safe final error and
|
|
100
|
+
* close the failure stream; rejection is suppressed in favor of the
|
|
101
|
+
* original structured run result.
|
|
102
|
+
*/
|
|
103
|
+
readonly writeError: (error: unknown) => Promise<void>; /** A successful await suspends/resumes without invoking either finalizer. */
|
|
99
104
|
readonly awaitInput?: (input: VercelAgentWorkflowAwaitingInput) => Promise<unknown>;
|
|
100
105
|
readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
101
106
|
readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-loop.d.mts","names":[],"sources":["../src/workflow-loop.ts"],"mappings":";KAAY,wBAAA;EAAA,SACD,OAAA;EAAA,SACA,OAAO;AAAA;AAAA,KAGN,yBAAA;EAAA,SACD,OAAA;EAAA,SACA,QAAA,GAAW,aAAA;EAAA,SACX,eAAA,EAAiB,aAAa;EAAA,SAC9B,KAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;AAAA;AAAA,KAGC,iCAAA;EAAA,SACD,OAAA;EAAA,SACA,KAAA,EAAO,yBAAyB;AAAA;AAAA,KAG/B,kCAAA;EAAA,SACD,IAAA;EAAA,SACA,QAAA,EAAU,aAAA;EAAA,SACV,eAAA,EAAiB,aAAA;EAAA,SACjB,SAAA,EAAW,aAAA;EAAA,SACX,KAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;AAAA;AAAA,KAGC,qCAAA;EAAA,SACD,OAAA;EAAA,SACA,OAAA;EAAA,SACA,KAAA,EAAO,aAAA;EAAA,SACP,eAAA,EAAiB,aAAA;EAAA,SACjB,aAAA,GAAgB,aAAA;EAAA,SAChB,KAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;AAAA;AAAA,KAGC,gCAAA;EAAA,SACD,SAAA;EAAA,SACA,QAAA,EAAU,aAAA;EAAA,SACV,QAAA,EAAU,aAAa;EAAA,SACvB,KAAA;EAAA,SACA,KAAA;EAAA,SACA,aAAA;AAAA;AAAA,KAGC,sCAAA;EAAA,SACD,QAAA,EAAU,aAAA;EAAA,SACV,eAAA,EAAiB,aAAA;EAAA,SACjB,aAAA,GAAgB,gCAAA;EAAA,SAChB,aAAA;AAAA;AAAA,KAGC,kBAAA;EAAA,SAEG,IAAA;EAAA,SACA,KAAA,EAAO,CAAC;AAAA;EAAA,SAGR,IAAA;EAAA,SACA,KAAA;AAAA;AAAA,KAGH,kCAAA;EAAA,SACD,WAAW;AAAA;AAAA,KAGV,4BAAA;EAAA,SAEG,IAAA;EAAA,SACA,KAAA;EAAA,SACA,KAAA,EAAO,yBAAA;AAAA;EAAA,SAGP,IAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;EAAA,SACA,KAAA,EAAO,yBAAA;AAAA;EAAA,SAGP,IAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;EAAA,SACA,KAAA,EAAO,yBAAA;AAAA;EAAA,SAGP,IAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;EAAA,SACA,aAAA,EAAe,gCAAA;EAAA,SACf,KAAA,EAAO,yBAAA;AAAA;EAAA,SAGP,IAAA;EAAA,SACA,KAAA;EAAA,SACA,KAAA;EAAA,SACA,KAAA,EAAO,yBAAA;AAAA;EAAA,SAGP,IAAA;EAAA,SACA,QAAA;EAAA,SACA,KAAA,EAAO,KAAA;EAAA,SACP,KAAA,EAAO,yBAAA;AAAA;AAAA,KAGV,6BAAA;EAAA,SACD,KAAA,EAAO,wBAAA;EAAA,SACP,QAAA;EAAA,SACA,YAAA,GACP,KAAA,EAAO,iCAAA,KACJ,OAAA,CAAQ,kCAAA;EAAA,SACJ,gBAAA,GACP,KAAA,EAAO,qCAAA,KACJ,OAAA,CAAQ,sCAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"workflow-loop.d.mts","names":[],"sources":["../src/workflow-loop.ts"],"mappings":"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"}
|
package/dist/workflow-loop.mjs
CHANGED
|
@@ -11,7 +11,10 @@ const settleWorkflowStep = (promise) => promise.then((value) => ({
|
|
|
11
11
|
const workflowMaxTurnsError = (maxTurns) => /* @__PURE__ */ new Error(`Vercel agent workflow exceeded max turns: ${maxTurns}`);
|
|
12
12
|
const writeErrorSafely = (writeError, error) => writeError(error).catch(() => void 0);
|
|
13
13
|
const missingAwaitInputHandlerError = () => /* @__PURE__ */ new Error("Vercel agent workflow awaiting input requested but no awaitInput handler is configured");
|
|
14
|
-
const maxRetryAttempts = (policy) =>
|
|
14
|
+
const maxRetryAttempts = (policy) => {
|
|
15
|
+
const configured = policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts;
|
|
16
|
+
return Number.isFinite(configured) ? Math.max(1, Math.floor(configured)) : 1;
|
|
17
|
+
};
|
|
15
18
|
async function retryWorkflowStep(runStep, policy) {
|
|
16
19
|
const maxAttempts = maxRetryAttempts(policy);
|
|
17
20
|
let attempt = 1;
|
|
@@ -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 readonly closeStream: () => Promise<void>\n readonly writeError: (error: unknown) => Promise<void>\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 Math.max(1, Math.floor(policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts))\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 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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"}
|
package/package.json
CHANGED
package/src/workflow-loop.ts
CHANGED
|
@@ -115,8 +115,15 @@ export type VercelAgentWorkflowLoopConfig = {
|
|
|
115
115
|
readonly runToolBatchStep: (
|
|
116
116
|
input: VercelAgentWorkflowToolBatchStepInput
|
|
117
117
|
) => Promise<VercelAgentWorkflowToolBatchStepResult>
|
|
118
|
+
/** Called only after a model step returns `done: true`. */
|
|
118
119
|
readonly closeStream: () => Promise<void>
|
|
120
|
+
/**
|
|
121
|
+
* Best-effort final failure handler. It must write a safe final error and
|
|
122
|
+
* close the failure stream; rejection is suppressed in favor of the
|
|
123
|
+
* original structured run result.
|
|
124
|
+
*/
|
|
119
125
|
readonly writeError: (error: unknown) => Promise<void>
|
|
126
|
+
/** A successful await suspends/resumes without invoking either finalizer. */
|
|
120
127
|
readonly awaitInput?: (input: VercelAgentWorkflowAwaitingInput) => Promise<unknown>
|
|
121
128
|
readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
122
129
|
readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
@@ -144,8 +151,11 @@ const missingAwaitInputHandlerError = () =>
|
|
|
144
151
|
'Vercel agent workflow awaiting input requested but no awaitInput handler is configured'
|
|
145
152
|
)
|
|
146
153
|
|
|
147
|
-
const maxRetryAttempts = (policy: VercelAgentWorkflowStepRetryPolicy | undefined) =>
|
|
148
|
-
|
|
154
|
+
const maxRetryAttempts = (policy: VercelAgentWorkflowStepRetryPolicy | undefined) => {
|
|
155
|
+
const configured = policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts
|
|
156
|
+
|
|
157
|
+
return Number.isFinite(configured) ? Math.max(1, Math.floor(configured)) : 1
|
|
158
|
+
}
|
|
149
159
|
|
|
150
160
|
export async function retryWorkflowStep<A>(
|
|
151
161
|
runStep: () => Promise<A>,
|