@yolk-sdk/vercel-workflows-runtime 0.0.1-canary.1 → 0.0.1-canary.10
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 -3
- package/dist/workflow-loop.d.mts +8 -0
- package/dist/workflow-loop.d.mts.map +1 -1
- package/dist/workflow-loop.mjs +64 -24
- package/dist/workflow-loop.mjs.map +1 -1
- package/package.json +1 -1
- package/src/workflow-loop.ts +109 -28
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Vercel Workflow-specific contracts for durable agent model/tool step loops.
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pnpm add @yolk-sdk/vercel-workflows-runtime@canary
|
|
8
|
+
pnpm add @yolk-sdk/vercel-workflows-runtime@canary effect workflow
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Canary APIs are unstable. Keep all `@yolk-sdk/*` packages on the same version.
|
|
@@ -34,12 +34,55 @@ import {
|
|
|
34
34
|
|
|
35
35
|
- model step: produce model events/tool calls
|
|
36
36
|
- tool batch step: execute requested tools
|
|
37
|
-
- awaiting-input state: carry pending HITL hook data
|
|
37
|
+
- awaiting-input state: carry pending HITL hook data, wait through `awaitInput`, then rerun the
|
|
38
|
+
same tool batch with accumulated responses
|
|
38
39
|
- close step: flush/close output stream
|
|
39
|
-
- terminal status: completed, step failure, close failure, or max turns exceeded
|
|
40
|
+
- terminal status: completed, step failure, await-input failure, close failure, or max turns exceeded
|
|
40
41
|
|
|
41
42
|
Continuation state stays plain serializable data for Workflow persistence.
|
|
42
43
|
|
|
44
|
+
## Minimal workflow wrapper
|
|
45
|
+
|
|
46
|
+
Host apps keep concrete Workflow directives local and pass them into the package loop:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { createHook } from 'workflow'
|
|
50
|
+
import { runVercelAgentWorkflow } from '@yolk-sdk/vercel-workflows-runtime/workflow'
|
|
51
|
+
|
|
52
|
+
export async function runAgentWorkflow(input: { request: unknown; context: unknown }) {
|
|
53
|
+
'use workflow'
|
|
54
|
+
|
|
55
|
+
return await runVercelAgentWorkflow({
|
|
56
|
+
input,
|
|
57
|
+
runModelStep,
|
|
58
|
+
runToolBatchStep,
|
|
59
|
+
closeStream,
|
|
60
|
+
writeError,
|
|
61
|
+
awaitInput: async awaitingInput => {
|
|
62
|
+
using hook = createHook<unknown>({ token: awaitingInput.hookToken })
|
|
63
|
+
|
|
64
|
+
return await hook
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`runModelStep`, `runToolBatchStep`, `closeStream`, and `writeError` are host-owned
|
|
71
|
+
`'use step'` functions. Keep provider calls, tools, persistence, telemetry, and Effect runtimes in
|
|
72
|
+
those steps, not in the `'use workflow'` orchestration body.
|
|
73
|
+
|
|
74
|
+
## Awaiting input
|
|
75
|
+
|
|
76
|
+
When a tool batch needs HITL input, return `awaitingInput` from `runToolBatchStep`. The package
|
|
77
|
+
loop will:
|
|
78
|
+
|
|
79
|
+
1. pass that payload to `awaitInput`
|
|
80
|
+
2. wait for the hook/webhook response
|
|
81
|
+
3. rerun the same tool batch with `hitlResponses: [...previous, response]`
|
|
82
|
+
|
|
83
|
+
The host owns `hookToken` construction, auth, resume routes, and response decoding. Use stable
|
|
84
|
+
tokens that the resume side can reconstruct or store.
|
|
85
|
+
|
|
43
86
|
## Retry policy
|
|
44
87
|
|
|
45
88
|
Default retry policy is `noWorkflowStepRetry` (`maxAttempts: 1`). Retries are opt-in because streamed retries can duplicate chunks unless host/client de-dupe is ready.
|
package/dist/workflow-loop.d.mts
CHANGED
|
@@ -72,6 +72,12 @@ type VercelAgentWorkflowRunResult = {
|
|
|
72
72
|
readonly turn: number;
|
|
73
73
|
readonly error: unknown;
|
|
74
74
|
readonly state: SerializableWorkflowState;
|
|
75
|
+
} | {
|
|
76
|
+
readonly _tag: 'AwaitInputFailed';
|
|
77
|
+
readonly turn: number;
|
|
78
|
+
readonly error: unknown;
|
|
79
|
+
readonly awaitingInput: VercelAgentWorkflowAwaitingInput;
|
|
80
|
+
readonly state: SerializableWorkflowState;
|
|
75
81
|
} | {
|
|
76
82
|
readonly _tag: 'CloseStreamFailed';
|
|
77
83
|
readonly turns: number;
|
|
@@ -90,8 +96,10 @@ type VercelAgentWorkflowLoopConfig = {
|
|
|
90
96
|
readonly runToolBatchStep: (input: VercelAgentWorkflowToolBatchStepInput) => Promise<VercelAgentWorkflowToolBatchStepResult>;
|
|
91
97
|
readonly closeStream: () => Promise<void>;
|
|
92
98
|
readonly writeError: (error: unknown) => Promise<void>;
|
|
99
|
+
readonly awaitInput?: (input: VercelAgentWorkflowAwaitingInput) => Promise<unknown>;
|
|
93
100
|
readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
94
101
|
readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
102
|
+
readonly awaitInputRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
95
103
|
readonly closeStreamRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
96
104
|
};
|
|
97
105
|
declare const defaultMaxWorkflowTurns = 500;
|
|
@@ -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,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,SACJ,WAAA,QAAmB,OAAA;EAAA,SACnB,UAAA,GAAa,KAAA,cAAmB,OAAA;EAAA,SAChC,cAAA,GAAiB,kCAAA;EAAA,SACjB,kBAAA,GAAqB,kCAAA;EAAA,SACrB,gBAAA,GAAmB,kCAAA;AAAA;AAAA,cAGjB,uBAAA;AAAA,cACA,mBAAA,EAAqB,kCAAuD;AAAA,cAE5E,kBAAA,MAAyB,OAAA,EAAS,OAAA,CAAQ,CAAA,MAAK,OAAA,CAAQ,kBAAA,CAAmB,CAAA;AAAA,
|
|
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
|
@@ -10,6 +10,7 @@ const settleWorkflowStep = (promise) => promise.then((value) => ({
|
|
|
10
10
|
}));
|
|
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
|
+
const missingAwaitInputHandlerError = () => /* @__PURE__ */ new Error("Vercel agent workflow awaiting input requested but no awaitInput handler is configured");
|
|
13
14
|
const maxRetryAttempts = (policy) => Math.max(1, Math.floor(policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts));
|
|
14
15
|
async function retryWorkflowStep(runStep, policy) {
|
|
15
16
|
const maxAttempts = maxRetryAttempts(policy);
|
|
@@ -22,20 +23,21 @@ async function retryWorkflowStep(runStep, policy) {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
async function runVercelAgentWorkflow(config) {
|
|
26
|
+
const { input, maxTurns: configuredMaxTurns, runModelStep, runToolBatchStep, closeStream, writeError, awaitInput, modelStepRetry, toolBatchStepRetry, awaitInputRetry, closeStreamRetry } = config;
|
|
25
27
|
let state = {
|
|
26
|
-
request:
|
|
28
|
+
request: input.request,
|
|
27
29
|
createdMessages: [],
|
|
28
30
|
turn: 1,
|
|
29
31
|
eventSequence: 0
|
|
30
32
|
};
|
|
31
|
-
const maxTurns =
|
|
33
|
+
const maxTurns = configuredMaxTurns ?? 500;
|
|
32
34
|
for (let step = 0; step < maxTurns; step++) {
|
|
33
|
-
const modelResult = await settleWorkflowStep(retryWorkflowStep(() =>
|
|
34
|
-
context:
|
|
35
|
+
const modelResult = await settleWorkflowStep(retryWorkflowStep(() => runModelStep({
|
|
36
|
+
context: input.context,
|
|
35
37
|
state
|
|
36
|
-
}),
|
|
38
|
+
}), modelStepRetry));
|
|
37
39
|
if (modelResult._tag === "Failure") {
|
|
38
|
-
await writeErrorSafely(
|
|
40
|
+
await writeErrorSafely(writeError, modelResult.error);
|
|
39
41
|
return {
|
|
40
42
|
_tag: "ModelStepFailed",
|
|
41
43
|
turn: state.turn,
|
|
@@ -44,9 +46,9 @@ async function runVercelAgentWorkflow(config) {
|
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
48
|
if (modelResult.value.done) {
|
|
47
|
-
const closeResult = await settleWorkflowStep(retryWorkflowStep(
|
|
49
|
+
const closeResult = await settleWorkflowStep(retryWorkflowStep(closeStream, closeStreamRetry));
|
|
48
50
|
if (closeResult._tag === "Failure") {
|
|
49
|
-
await writeErrorSafely(
|
|
51
|
+
await writeErrorSafely(writeError, closeResult.error);
|
|
50
52
|
return {
|
|
51
53
|
_tag: "CloseStreamFailed",
|
|
52
54
|
turns: modelResult.value.turn,
|
|
@@ -60,34 +62,72 @@ async function runVercelAgentWorkflow(config) {
|
|
|
60
62
|
state
|
|
61
63
|
};
|
|
62
64
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
let completedToolsResult;
|
|
66
|
+
let toolHitlResponses = [];
|
|
67
|
+
let toolEventSequence = modelResult.value.eventSequence ?? state.eventSequence;
|
|
68
|
+
for (;;) {
|
|
69
|
+
const toolsResult = await settleWorkflowStep(retryWorkflowStep(() => runToolBatchStep({
|
|
70
|
+
context: input.context,
|
|
71
|
+
request: input.request,
|
|
72
|
+
calls: modelResult.value.toolCalls,
|
|
73
|
+
createdMessages: modelResult.value.createdMessages,
|
|
74
|
+
hitlResponses: toolHitlResponses,
|
|
75
|
+
usage: modelResult.value.usage,
|
|
76
|
+
turn: modelResult.value.turn,
|
|
77
|
+
eventSequence: toolEventSequence
|
|
78
|
+
}), toolBatchStepRetry));
|
|
79
|
+
if (toolsResult._tag === "Failure") {
|
|
80
|
+
await writeErrorSafely(writeError, toolsResult.error);
|
|
81
|
+
return {
|
|
82
|
+
_tag: "ToolBatchStepFailed",
|
|
83
|
+
turn: modelResult.value.turn,
|
|
84
|
+
error: toolsResult.error,
|
|
85
|
+
state
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (toolsResult.value.awaitingInput === void 0) {
|
|
89
|
+
completedToolsResult = toolsResult.value;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
const awaitingInput = toolsResult.value.awaitingInput;
|
|
93
|
+
toolEventSequence = awaitingInput.eventSequence ?? toolsResult.value.eventSequence ?? toolEventSequence;
|
|
94
|
+
const hitlResponse = await settleWorkflowStep(retryWorkflowStep(() => {
|
|
95
|
+
if (awaitInput === void 0) return Promise.reject(missingAwaitInputHandlerError());
|
|
96
|
+
return awaitInput(awaitingInput);
|
|
97
|
+
}, awaitInputRetry));
|
|
98
|
+
if (hitlResponse._tag === "Failure") {
|
|
99
|
+
await writeErrorSafely(writeError, hitlResponse.error);
|
|
100
|
+
return {
|
|
101
|
+
_tag: "AwaitInputFailed",
|
|
102
|
+
turn: modelResult.value.turn,
|
|
103
|
+
error: hitlResponse.error,
|
|
104
|
+
awaitingInput,
|
|
105
|
+
state
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
toolHitlResponses = [...toolHitlResponses, hitlResponse.value];
|
|
109
|
+
}
|
|
110
|
+
if (completedToolsResult === void 0) {
|
|
111
|
+
const error = /* @__PURE__ */ new Error("Vercel agent workflow tool batch did not complete");
|
|
112
|
+
await writeErrorSafely(writeError, error);
|
|
73
113
|
return {
|
|
74
114
|
_tag: "ToolBatchStepFailed",
|
|
75
115
|
turn: modelResult.value.turn,
|
|
76
|
-
error
|
|
116
|
+
error,
|
|
77
117
|
state
|
|
78
118
|
};
|
|
79
119
|
}
|
|
80
120
|
state = {
|
|
81
|
-
request:
|
|
82
|
-
messages: [...modelResult.value.messages, ...
|
|
83
|
-
createdMessages:
|
|
121
|
+
request: input.request,
|
|
122
|
+
messages: [...modelResult.value.messages, ...completedToolsResult.messages],
|
|
123
|
+
createdMessages: completedToolsResult.createdMessages,
|
|
84
124
|
usage: modelResult.value.usage,
|
|
85
125
|
turn: modelResult.value.turn + 1,
|
|
86
|
-
eventSequence:
|
|
126
|
+
eventSequence: completedToolsResult.eventSequence ?? toolEventSequence
|
|
87
127
|
};
|
|
88
128
|
}
|
|
89
129
|
const error = workflowMaxTurnsError(maxTurns);
|
|
90
|
-
await writeErrorSafely(
|
|
130
|
+
await writeErrorSafely(writeError, error);
|
|
91
131
|
return {
|
|
92
132
|
_tag: "MaxTurnsExceeded",
|
|
93
133
|
maxTurns,
|
|
@@ -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: '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 modelStepRetry?: VercelAgentWorkflowStepRetryPolicy\n readonly toolBatchStepRetry?: 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 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 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 let state: SerializableWorkflowState = {\n request: config.input.request,\n createdMessages: [],\n turn: 1,\n eventSequence: 0\n }\n const maxTurns = config.maxTurns ?? defaultMaxWorkflowTurns\n\n for (let step = 0; step < maxTurns; step++) {\n const modelResult = await settleWorkflowStep(\n retryWorkflowStep(\n () => config.runModelStep({ context: config.input.context, state }),\n config.modelStepRetry\n )\n )\n\n if (modelResult._tag === 'Failure') {\n await writeErrorSafely(config.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(\n retryWorkflowStep(config.closeStream, config.closeStreamRetry)\n )\n\n if (closeResult._tag === 'Failure') {\n await writeErrorSafely(config.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 const toolsResult = await settleWorkflowStep(\n retryWorkflowStep(\n () =>\n config.runToolBatchStep({\n context: config.input.context,\n request: config.input.request,\n calls: modelResult.value.toolCalls,\n createdMessages: modelResult.value.createdMessages,\n turn: modelResult.value.turn,\n eventSequence: modelResult.value.eventSequence ?? state.eventSequence\n }),\n config.toolBatchStepRetry\n )\n )\n\n if (toolsResult._tag === 'Failure') {\n await writeErrorSafely(config.writeError, toolsResult.error)\n return {\n _tag: 'ToolBatchStepFailed',\n turn: modelResult.value.turn,\n error: toolsResult.error,\n state\n }\n }\n\n state = {\n request: config.input.request,\n messages: [...modelResult.value.messages, ...toolsResult.value.messages],\n createdMessages: toolsResult.value.createdMessages,\n usage: modelResult.value.usage,\n turn: modelResult.value.turn + 1,\n eventSequence: toolsResult.value.eventSequence ?? modelResult.value.eventSequence ?? state.eventSequence\n }\n }\n\n const error = workflowMaxTurnsError(maxTurns)\n await writeErrorSafely(config.writeError, error)\n\n return {\n _tag: 'MaxTurnsExceeded',\n maxTurns,\n error,\n state\n }\n}\n"],"mappings":";AAqHA,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,oBAAoB,WACxB,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,eAAe,oBAAoB,WAAW,CAAC;AAEhF,eAAsB,kBACpB,SACA,QACY;CACZ,MAAM,cAAc,iBAAiB,MAAM;CAC3C,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,IAAI,QAAmC;EACrC,SAAS,OAAO,MAAM;EACtB,iBAAiB,CAAC;EAClB,MAAM;EACN,eAAe;CACjB;CACA,MAAM,WAAW,OAAO,YAAA;CAExB,KAAK,IAAI,OAAO,GAAG,OAAO,UAAU,QAAQ;EAC1C,MAAM,cAAc,MAAM,mBACxB,wBACQ,OAAO,aAAa;GAAE,SAAS,OAAO,MAAM;GAAS;EAAM,CAAC,GAClE,OAAO,cACT,CACF;EAEA,IAAI,YAAY,SAAS,WAAW;GAClC,MAAM,iBAAiB,OAAO,YAAY,YAAY,KAAK;GAC3D,OAAO;IACL,MAAM;IACN,MAAM,MAAM;IACZ,OAAO,YAAY;IACnB;GACF;EACF;EAEA,IAAI,YAAY,MAAM,MAAM;GAC1B,MAAM,cAAc,MAAM,mBACxB,kBAAkB,OAAO,aAAa,OAAO,gBAAgB,CAC/D;GAEA,IAAI,YAAY,SAAS,WAAW;IAClC,MAAM,iBAAiB,OAAO,YAAY,YAAY,KAAK;IAE3D,OAAO;KACL,MAAM;KACN,OAAO,YAAY,MAAM;KACzB,OAAO,YAAY;KACnB;IACF;GACF;GAEA,OAAO;IACL,MAAM;IACN,OAAO,YAAY,MAAM;IACzB;GACF;EACF;EAEA,MAAM,cAAc,MAAM,mBACxB,wBAEI,OAAO,iBAAiB;GACtB,SAAS,OAAO,MAAM;GACtB,SAAS,OAAO,MAAM;GACtB,OAAO,YAAY,MAAM;GACzB,iBAAiB,YAAY,MAAM;GACnC,MAAM,YAAY,MAAM;GACxB,eAAe,YAAY,MAAM,iBAAiB,MAAM;EAC1D,CAAC,GACH,OAAO,kBACT,CACF;EAEA,IAAI,YAAY,SAAS,WAAW;GAClC,MAAM,iBAAiB,OAAO,YAAY,YAAY,KAAK;GAC3D,OAAO;IACL,MAAM;IACN,MAAM,YAAY,MAAM;IACxB,OAAO,YAAY;IACnB;GACF;EACF;EAEA,QAAQ;GACN,SAAS,OAAO,MAAM;GACtB,UAAU,CAAC,GAAG,YAAY,MAAM,UAAU,GAAG,YAAY,MAAM,QAAQ;GACvE,iBAAiB,YAAY,MAAM;GACnC,OAAO,YAAY,MAAM;GACzB,MAAM,YAAY,MAAM,OAAO;GAC/B,eAAe,YAAY,MAAM,iBAAiB,YAAY,MAAM,iBAAiB,MAAM;EAC7F;CACF;CAEA,MAAM,QAAQ,sBAAsB,QAAQ;CAC5C,MAAM,iBAAiB,OAAO,YAAY,KAAK;CAE/C,OAAO;EACL,MAAM;EACN;EACA;EACA;CACF;AACF"}
|
|
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('Vercel agent workflow awaiting input requested but no awaitInput handler is configured')\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 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(\n () => runModelStep({ context: input.context, state }),\n modelStepRetry\n )\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(\n retryWorkflowStep(closeStream, closeStreamRetry)\n )\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 = awaitingInput.eventSequence ?? toolsResult.value.eventSequence ?? toolEventSequence\n\n const hitlResponse = await settleWorkflowStep(\n retryWorkflowStep(\n () => {\n if (awaitInput === undefined) {\n return Promise.reject(missingAwaitInputHandlerError())\n }\n\n return awaitInput(awaitingInput)\n },\n awaitInputRetry\n )\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
|
@@ -86,6 +86,13 @@ export type VercelAgentWorkflowRunResult =
|
|
|
86
86
|
readonly error: unknown
|
|
87
87
|
readonly state: SerializableWorkflowState
|
|
88
88
|
}
|
|
89
|
+
| {
|
|
90
|
+
readonly _tag: 'AwaitInputFailed'
|
|
91
|
+
readonly turn: number
|
|
92
|
+
readonly error: unknown
|
|
93
|
+
readonly awaitingInput: VercelAgentWorkflowAwaitingInput
|
|
94
|
+
readonly state: SerializableWorkflowState
|
|
95
|
+
}
|
|
89
96
|
| {
|
|
90
97
|
readonly _tag: 'CloseStreamFailed'
|
|
91
98
|
readonly turns: number
|
|
@@ -110,8 +117,10 @@ export type VercelAgentWorkflowLoopConfig = {
|
|
|
110
117
|
) => Promise<VercelAgentWorkflowToolBatchStepResult>
|
|
111
118
|
readonly closeStream: () => Promise<void>
|
|
112
119
|
readonly writeError: (error: unknown) => Promise<void>
|
|
120
|
+
readonly awaitInput?: (input: VercelAgentWorkflowAwaitingInput) => Promise<unknown>
|
|
113
121
|
readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
114
122
|
readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
123
|
+
readonly awaitInputRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
115
124
|
readonly closeStreamRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
116
125
|
}
|
|
117
126
|
|
|
@@ -130,6 +139,9 @@ const workflowMaxTurnsError = (maxTurns: number) =>
|
|
|
130
139
|
const writeErrorSafely = (writeError: (error: unknown) => Promise<void>, error: unknown) =>
|
|
131
140
|
writeError(error).catch(() => undefined)
|
|
132
141
|
|
|
142
|
+
const missingAwaitInputHandlerError = () =>
|
|
143
|
+
new Error('Vercel agent workflow awaiting input requested but no awaitInput handler is configured')
|
|
144
|
+
|
|
133
145
|
const maxRetryAttempts = (policy: VercelAgentWorkflowStepRetryPolicy | undefined) =>
|
|
134
146
|
Math.max(1, Math.floor(policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts))
|
|
135
147
|
|
|
@@ -156,24 +168,37 @@ export async function retryWorkflowStep<A>(
|
|
|
156
168
|
export async function runVercelAgentWorkflow(
|
|
157
169
|
config: VercelAgentWorkflowLoopConfig
|
|
158
170
|
): Promise<VercelAgentWorkflowRunResult> {
|
|
171
|
+
const {
|
|
172
|
+
input,
|
|
173
|
+
maxTurns: configuredMaxTurns,
|
|
174
|
+
runModelStep,
|
|
175
|
+
runToolBatchStep,
|
|
176
|
+
closeStream,
|
|
177
|
+
writeError,
|
|
178
|
+
awaitInput,
|
|
179
|
+
modelStepRetry,
|
|
180
|
+
toolBatchStepRetry,
|
|
181
|
+
awaitInputRetry,
|
|
182
|
+
closeStreamRetry
|
|
183
|
+
} = config
|
|
159
184
|
let state: SerializableWorkflowState = {
|
|
160
|
-
request:
|
|
185
|
+
request: input.request,
|
|
161
186
|
createdMessages: [],
|
|
162
187
|
turn: 1,
|
|
163
188
|
eventSequence: 0
|
|
164
189
|
}
|
|
165
|
-
const maxTurns =
|
|
190
|
+
const maxTurns = configuredMaxTurns ?? defaultMaxWorkflowTurns
|
|
166
191
|
|
|
167
192
|
for (let step = 0; step < maxTurns; step++) {
|
|
168
193
|
const modelResult = await settleWorkflowStep(
|
|
169
194
|
retryWorkflowStep(
|
|
170
|
-
() =>
|
|
171
|
-
|
|
195
|
+
() => runModelStep({ context: input.context, state }),
|
|
196
|
+
modelStepRetry
|
|
172
197
|
)
|
|
173
198
|
)
|
|
174
199
|
|
|
175
200
|
if (modelResult._tag === 'Failure') {
|
|
176
|
-
await writeErrorSafely(
|
|
201
|
+
await writeErrorSafely(writeError, modelResult.error)
|
|
177
202
|
return {
|
|
178
203
|
_tag: 'ModelStepFailed',
|
|
179
204
|
turn: state.turn,
|
|
@@ -184,11 +209,11 @@ export async function runVercelAgentWorkflow(
|
|
|
184
209
|
|
|
185
210
|
if (modelResult.value.done) {
|
|
186
211
|
const closeResult = await settleWorkflowStep(
|
|
187
|
-
retryWorkflowStep(
|
|
212
|
+
retryWorkflowStep(closeStream, closeStreamRetry)
|
|
188
213
|
)
|
|
189
214
|
|
|
190
215
|
if (closeResult._tag === 'Failure') {
|
|
191
|
-
await writeErrorSafely(
|
|
216
|
+
await writeErrorSafely(writeError, closeResult.error)
|
|
192
217
|
|
|
193
218
|
return {
|
|
194
219
|
_tag: 'CloseStreamFailed',
|
|
@@ -205,43 +230,99 @@ export async function runVercelAgentWorkflow(
|
|
|
205
230
|
}
|
|
206
231
|
}
|
|
207
232
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
233
|
+
let completedToolsResult: VercelAgentWorkflowToolBatchStepResult | undefined
|
|
234
|
+
let toolHitlResponses: ReadonlyArray<unknown> = []
|
|
235
|
+
let toolEventSequence = modelResult.value.eventSequence ?? state.eventSequence
|
|
236
|
+
|
|
237
|
+
for (;;) {
|
|
238
|
+
const toolsResult = await settleWorkflowStep(
|
|
239
|
+
retryWorkflowStep(
|
|
240
|
+
() =>
|
|
241
|
+
runToolBatchStep({
|
|
242
|
+
context: input.context,
|
|
243
|
+
request: input.request,
|
|
244
|
+
calls: modelResult.value.toolCalls,
|
|
245
|
+
createdMessages: modelResult.value.createdMessages,
|
|
246
|
+
hitlResponses: toolHitlResponses,
|
|
247
|
+
usage: modelResult.value.usage,
|
|
248
|
+
turn: modelResult.value.turn,
|
|
249
|
+
eventSequence: toolEventSequence
|
|
250
|
+
}),
|
|
251
|
+
toolBatchStepRetry
|
|
252
|
+
)
|
|
220
253
|
)
|
|
221
|
-
)
|
|
222
254
|
|
|
223
|
-
|
|
224
|
-
|
|
255
|
+
if (toolsResult._tag === 'Failure') {
|
|
256
|
+
await writeErrorSafely(writeError, toolsResult.error)
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
_tag: 'ToolBatchStepFailed',
|
|
260
|
+
turn: modelResult.value.turn,
|
|
261
|
+
error: toolsResult.error,
|
|
262
|
+
state
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (toolsResult.value.awaitingInput === undefined) {
|
|
267
|
+
completedToolsResult = toolsResult.value
|
|
268
|
+
break
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const awaitingInput = toolsResult.value.awaitingInput
|
|
272
|
+
toolEventSequence = awaitingInput.eventSequence ?? toolsResult.value.eventSequence ?? toolEventSequence
|
|
273
|
+
|
|
274
|
+
const hitlResponse = await settleWorkflowStep(
|
|
275
|
+
retryWorkflowStep(
|
|
276
|
+
() => {
|
|
277
|
+
if (awaitInput === undefined) {
|
|
278
|
+
return Promise.reject(missingAwaitInputHandlerError())
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return awaitInput(awaitingInput)
|
|
282
|
+
},
|
|
283
|
+
awaitInputRetry
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if (hitlResponse._tag === 'Failure') {
|
|
288
|
+
await writeErrorSafely(writeError, hitlResponse.error)
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
_tag: 'AwaitInputFailed',
|
|
292
|
+
turn: modelResult.value.turn,
|
|
293
|
+
error: hitlResponse.error,
|
|
294
|
+
awaitingInput,
|
|
295
|
+
state
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
toolHitlResponses = [...toolHitlResponses, hitlResponse.value]
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (completedToolsResult === undefined) {
|
|
303
|
+
const error = new Error('Vercel agent workflow tool batch did not complete')
|
|
304
|
+
await writeErrorSafely(writeError, error)
|
|
305
|
+
|
|
225
306
|
return {
|
|
226
307
|
_tag: 'ToolBatchStepFailed',
|
|
227
308
|
turn: modelResult.value.turn,
|
|
228
|
-
error
|
|
309
|
+
error,
|
|
229
310
|
state
|
|
230
311
|
}
|
|
231
312
|
}
|
|
232
313
|
|
|
233
314
|
state = {
|
|
234
|
-
request:
|
|
235
|
-
messages: [...modelResult.value.messages, ...
|
|
236
|
-
createdMessages:
|
|
315
|
+
request: input.request,
|
|
316
|
+
messages: [...modelResult.value.messages, ...completedToolsResult.messages],
|
|
317
|
+
createdMessages: completedToolsResult.createdMessages,
|
|
237
318
|
usage: modelResult.value.usage,
|
|
238
319
|
turn: modelResult.value.turn + 1,
|
|
239
|
-
eventSequence:
|
|
320
|
+
eventSequence: completedToolsResult.eventSequence ?? toolEventSequence
|
|
240
321
|
}
|
|
241
322
|
}
|
|
242
323
|
|
|
243
324
|
const error = workflowMaxTurnsError(maxTurns)
|
|
244
|
-
await writeErrorSafely(
|
|
325
|
+
await writeErrorSafely(writeError, error)
|
|
245
326
|
|
|
246
327
|
return {
|
|
247
328
|
_tag: 'MaxTurnsExceeded',
|