@yolk-sdk/vercel-workflows-runtime 0.0.1-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/workflow-loop.d.mts +104 -0
- package/dist/workflow-loop.d.mts.map +1 -0
- package/dist/workflow-loop.mjs +101 -0
- package/dist/workflow-loop.mjs.map +1 -0
- package/dist/workflow.d.mts +2 -0
- package/dist/workflow.mjs +2 -0
- package/package.json +67 -0
- package/src/index.ts +1 -0
- package/src/workflow-loop.ts +252 -0
- package/src/workflow.ts +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yolk SDK contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @yolk-sdk/vercel-workflows-runtime
|
|
2
|
+
|
|
3
|
+
Vercel Workflow-specific contracts for durable agent model/tool step loops.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @yolk-sdk/vercel-workflows-runtime@canary @yolk-sdk/agent@canary effect workflow
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Canary APIs are unstable. Keep all `@yolk-sdk/*` packages on the same version.
|
|
12
|
+
|
|
13
|
+
## Subpaths
|
|
14
|
+
|
|
15
|
+
| Subpath | Purpose |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `@yolk-sdk/vercel-workflows-runtime` | Empty root export; reserved package entry |
|
|
18
|
+
| `@yolk-sdk/vercel-workflows-runtime/workflow` | Workflow-safe agent loop orchestration APIs |
|
|
19
|
+
|
|
20
|
+
## Imports
|
|
21
|
+
|
|
22
|
+
The root export is intentionally empty. Import Workflow APIs from `./workflow`:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import {
|
|
26
|
+
noWorkflowStepRetry,
|
|
27
|
+
runVercelAgentWorkflow
|
|
28
|
+
} from '@yolk-sdk/vercel-workflows-runtime/workflow'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Runtime model
|
|
32
|
+
|
|
33
|
+
`runVercelAgentWorkflow` coordinates host-provided callbacks:
|
|
34
|
+
|
|
35
|
+
- model step: produce model events/tool calls
|
|
36
|
+
- tool batch step: execute requested tools
|
|
37
|
+
- awaiting-input state: carry pending HITL hook data between steps
|
|
38
|
+
- close step: flush/close output stream
|
|
39
|
+
- terminal status: completed, step failure, close failure, or max turns exceeded
|
|
40
|
+
|
|
41
|
+
Continuation state stays plain serializable data for Workflow persistence.
|
|
42
|
+
|
|
43
|
+
## Retry policy
|
|
44
|
+
|
|
45
|
+
Default retry policy is `noWorkflowStepRetry` (`maxAttempts: 1`). Retries are opt-in because streamed retries can duplicate chunks unless host/client de-dupe is ready.
|
|
46
|
+
|
|
47
|
+
## Host responsibilities
|
|
48
|
+
|
|
49
|
+
- Own Next/Vercel routes, auth, providers, tools, persistence, and telemetry.
|
|
50
|
+
- Encode/decode app transcript/session state into serializable Workflow inputs.
|
|
51
|
+
- Decide cancellation/resume/conflict UX.
|
|
52
|
+
- Own hook tokens and response validation for HITL resume.
|
|
53
|
+
- Test directive behavior with `@workflow/vitest` when changing package-owned Workflow files.
|
|
54
|
+
|
|
55
|
+
## Boundaries
|
|
56
|
+
|
|
57
|
+
- No Next routes, server actions, auth, app tools, DB, UI, or provider SDKs.
|
|
58
|
+
- Effect may run inside host/package step callbacks; workflow orchestration keeps plain data contracts.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
//#region src/workflow-loop.d.ts
|
|
2
|
+
type VercelAgentWorkflowInput = {
|
|
3
|
+
readonly request: unknown;
|
|
4
|
+
readonly context: unknown;
|
|
5
|
+
};
|
|
6
|
+
type SerializableWorkflowState = {
|
|
7
|
+
readonly request: unknown;
|
|
8
|
+
readonly messages?: ReadonlyArray<unknown>;
|
|
9
|
+
readonly createdMessages: ReadonlyArray<unknown>;
|
|
10
|
+
readonly usage?: unknown;
|
|
11
|
+
readonly turn: number;
|
|
12
|
+
readonly eventSequence?: number;
|
|
13
|
+
};
|
|
14
|
+
type VercelAgentWorkflowModelStepInput = {
|
|
15
|
+
readonly context: unknown;
|
|
16
|
+
readonly state: SerializableWorkflowState;
|
|
17
|
+
};
|
|
18
|
+
type VercelAgentWorkflowModelStepResult = {
|
|
19
|
+
readonly done: boolean;
|
|
20
|
+
readonly messages: ReadonlyArray<unknown>;
|
|
21
|
+
readonly createdMessages: ReadonlyArray<unknown>;
|
|
22
|
+
readonly toolCalls: ReadonlyArray<unknown>;
|
|
23
|
+
readonly usage: unknown;
|
|
24
|
+
readonly turn: number;
|
|
25
|
+
readonly eventSequence?: number;
|
|
26
|
+
};
|
|
27
|
+
type VercelAgentWorkflowToolBatchStepInput = {
|
|
28
|
+
readonly context: unknown;
|
|
29
|
+
readonly request: unknown;
|
|
30
|
+
readonly calls: ReadonlyArray<unknown>;
|
|
31
|
+
readonly createdMessages: ReadonlyArray<unknown>;
|
|
32
|
+
readonly hitlResponses?: ReadonlyArray<unknown>;
|
|
33
|
+
readonly usage?: unknown;
|
|
34
|
+
readonly turn?: number;
|
|
35
|
+
readonly eventSequence?: number;
|
|
36
|
+
};
|
|
37
|
+
type VercelAgentWorkflowAwaitingInput = {
|
|
38
|
+
readonly hookToken: string;
|
|
39
|
+
readonly requests: ReadonlyArray<unknown>;
|
|
40
|
+
readonly messages: ReadonlyArray<unknown>;
|
|
41
|
+
readonly usage: unknown;
|
|
42
|
+
readonly turns: number;
|
|
43
|
+
readonly eventSequence?: number;
|
|
44
|
+
};
|
|
45
|
+
type VercelAgentWorkflowToolBatchStepResult = {
|
|
46
|
+
readonly messages: ReadonlyArray<unknown>;
|
|
47
|
+
readonly createdMessages: ReadonlyArray<unknown>;
|
|
48
|
+
readonly awaitingInput?: VercelAgentWorkflowAwaitingInput;
|
|
49
|
+
readonly eventSequence?: number;
|
|
50
|
+
};
|
|
51
|
+
type WorkflowStepResult<A> = {
|
|
52
|
+
readonly _tag: 'Success';
|
|
53
|
+
readonly value: A;
|
|
54
|
+
} | {
|
|
55
|
+
readonly _tag: 'Failure';
|
|
56
|
+
readonly error: unknown;
|
|
57
|
+
};
|
|
58
|
+
type VercelAgentWorkflowStepRetryPolicy = {
|
|
59
|
+
readonly maxAttempts: number;
|
|
60
|
+
};
|
|
61
|
+
type VercelAgentWorkflowRunResult = {
|
|
62
|
+
readonly _tag: 'Completed';
|
|
63
|
+
readonly turns: number;
|
|
64
|
+
readonly state: SerializableWorkflowState;
|
|
65
|
+
} | {
|
|
66
|
+
readonly _tag: 'ModelStepFailed';
|
|
67
|
+
readonly turn: number;
|
|
68
|
+
readonly error: unknown;
|
|
69
|
+
readonly state: SerializableWorkflowState;
|
|
70
|
+
} | {
|
|
71
|
+
readonly _tag: 'ToolBatchStepFailed';
|
|
72
|
+
readonly turn: number;
|
|
73
|
+
readonly error: unknown;
|
|
74
|
+
readonly state: SerializableWorkflowState;
|
|
75
|
+
} | {
|
|
76
|
+
readonly _tag: 'CloseStreamFailed';
|
|
77
|
+
readonly turns: number;
|
|
78
|
+
readonly error: unknown;
|
|
79
|
+
readonly state: SerializableWorkflowState;
|
|
80
|
+
} | {
|
|
81
|
+
readonly _tag: 'MaxTurnsExceeded';
|
|
82
|
+
readonly maxTurns: number;
|
|
83
|
+
readonly error: Error;
|
|
84
|
+
readonly state: SerializableWorkflowState;
|
|
85
|
+
};
|
|
86
|
+
type VercelAgentWorkflowLoopConfig = {
|
|
87
|
+
readonly input: VercelAgentWorkflowInput;
|
|
88
|
+
readonly maxTurns?: number;
|
|
89
|
+
readonly runModelStep: (input: VercelAgentWorkflowModelStepInput) => Promise<VercelAgentWorkflowModelStepResult>;
|
|
90
|
+
readonly runToolBatchStep: (input: VercelAgentWorkflowToolBatchStepInput) => Promise<VercelAgentWorkflowToolBatchStepResult>;
|
|
91
|
+
readonly closeStream: () => Promise<void>;
|
|
92
|
+
readonly writeError: (error: unknown) => Promise<void>;
|
|
93
|
+
readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
94
|
+
readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
95
|
+
readonly closeStreamRetry?: VercelAgentWorkflowStepRetryPolicy;
|
|
96
|
+
};
|
|
97
|
+
declare const defaultMaxWorkflowTurns = 500;
|
|
98
|
+
declare const noWorkflowStepRetry: VercelAgentWorkflowStepRetryPolicy;
|
|
99
|
+
declare const settleWorkflowStep: <A>(promise: Promise<A>) => Promise<WorkflowStepResult<A>>;
|
|
100
|
+
declare function retryWorkflowStep<A>(runStep: () => Promise<A>, policy?: VercelAgentWorkflowStepRetryPolicy): Promise<A>;
|
|
101
|
+
declare function runVercelAgentWorkflow(config: VercelAgentWorkflowLoopConfig): Promise<VercelAgentWorkflowRunResult>;
|
|
102
|
+
//#endregion
|
|
103
|
+
export { SerializableWorkflowState, VercelAgentWorkflowAwaitingInput, VercelAgentWorkflowInput, VercelAgentWorkflowLoopConfig, VercelAgentWorkflowModelStepInput, VercelAgentWorkflowModelStepResult, VercelAgentWorkflowRunResult, VercelAgentWorkflowStepRetryPolicy, VercelAgentWorkflowToolBatchStepInput, VercelAgentWorkflowToolBatchStepResult, WorkflowStepResult, defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep };
|
|
104
|
+
//# sourceMappingURL=workflow-loop.d.mts.map
|
|
@@ -0,0 +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,iBAejE,iBAAA,GAAA,CACpB,OAAA,QAAe,OAAA,CAAQ,CAAA,GACvB,MAAA,GAAS,kCAAA,GACR,OAAA,CAAQ,CAAA;AAAA,iBAiBW,sBAAA,CACpB,MAAA,EAAQ,6BAAA,GACP,OAAA,CAAQ,4BAAA"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//#region src/workflow-loop.ts
|
|
2
|
+
const defaultMaxWorkflowTurns = 500;
|
|
3
|
+
const noWorkflowStepRetry = { maxAttempts: 1 };
|
|
4
|
+
const settleWorkflowStep = (promise) => promise.then((value) => ({
|
|
5
|
+
_tag: "Success",
|
|
6
|
+
value
|
|
7
|
+
}), (error) => ({
|
|
8
|
+
_tag: "Failure",
|
|
9
|
+
error
|
|
10
|
+
}));
|
|
11
|
+
const workflowMaxTurnsError = (maxTurns) => /* @__PURE__ */ new Error(`Vercel agent workflow exceeded max turns: ${maxTurns}`);
|
|
12
|
+
const writeErrorSafely = (writeError, error) => writeError(error).catch(() => void 0);
|
|
13
|
+
const maxRetryAttempts = (policy) => Math.max(1, Math.floor(policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts));
|
|
14
|
+
async function retryWorkflowStep(runStep, policy) {
|
|
15
|
+
const maxAttempts = maxRetryAttempts(policy);
|
|
16
|
+
let attempt = 1;
|
|
17
|
+
for (;;) try {
|
|
18
|
+
return await runStep();
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (attempt >= maxAttempts) throw error;
|
|
21
|
+
attempt += 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function runVercelAgentWorkflow(config) {
|
|
25
|
+
let state = {
|
|
26
|
+
request: config.input.request,
|
|
27
|
+
createdMessages: [],
|
|
28
|
+
turn: 1,
|
|
29
|
+
eventSequence: 0
|
|
30
|
+
};
|
|
31
|
+
const maxTurns = config.maxTurns ?? 500;
|
|
32
|
+
for (let step = 0; step < maxTurns; step++) {
|
|
33
|
+
const modelResult = await settleWorkflowStep(retryWorkflowStep(() => config.runModelStep({
|
|
34
|
+
context: config.input.context,
|
|
35
|
+
state
|
|
36
|
+
}), config.modelStepRetry));
|
|
37
|
+
if (modelResult._tag === "Failure") {
|
|
38
|
+
await writeErrorSafely(config.writeError, modelResult.error);
|
|
39
|
+
return {
|
|
40
|
+
_tag: "ModelStepFailed",
|
|
41
|
+
turn: state.turn,
|
|
42
|
+
error: modelResult.error,
|
|
43
|
+
state
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (modelResult.value.done) {
|
|
47
|
+
const closeResult = await settleWorkflowStep(retryWorkflowStep(config.closeStream, config.closeStreamRetry));
|
|
48
|
+
if (closeResult._tag === "Failure") {
|
|
49
|
+
await writeErrorSafely(config.writeError, closeResult.error);
|
|
50
|
+
return {
|
|
51
|
+
_tag: "CloseStreamFailed",
|
|
52
|
+
turns: modelResult.value.turn,
|
|
53
|
+
error: closeResult.error,
|
|
54
|
+
state
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
_tag: "Completed",
|
|
59
|
+
turns: modelResult.value.turn,
|
|
60
|
+
state
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const toolsResult = await settleWorkflowStep(retryWorkflowStep(() => config.runToolBatchStep({
|
|
64
|
+
context: config.input.context,
|
|
65
|
+
request: config.input.request,
|
|
66
|
+
calls: modelResult.value.toolCalls,
|
|
67
|
+
createdMessages: modelResult.value.createdMessages,
|
|
68
|
+
turn: modelResult.value.turn,
|
|
69
|
+
eventSequence: modelResult.value.eventSequence ?? state.eventSequence
|
|
70
|
+
}), config.toolBatchStepRetry));
|
|
71
|
+
if (toolsResult._tag === "Failure") {
|
|
72
|
+
await writeErrorSafely(config.writeError, toolsResult.error);
|
|
73
|
+
return {
|
|
74
|
+
_tag: "ToolBatchStepFailed",
|
|
75
|
+
turn: modelResult.value.turn,
|
|
76
|
+
error: toolsResult.error,
|
|
77
|
+
state
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
state = {
|
|
81
|
+
request: config.input.request,
|
|
82
|
+
messages: [...modelResult.value.messages, ...toolsResult.value.messages],
|
|
83
|
+
createdMessages: toolsResult.value.createdMessages,
|
|
84
|
+
usage: modelResult.value.usage,
|
|
85
|
+
turn: modelResult.value.turn + 1,
|
|
86
|
+
eventSequence: toolsResult.value.eventSequence ?? modelResult.value.eventSequence ?? state.eventSequence
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const error = workflowMaxTurnsError(maxTurns);
|
|
90
|
+
await writeErrorSafely(config.writeError, error);
|
|
91
|
+
return {
|
|
92
|
+
_tag: "MaxTurnsExceeded",
|
|
93
|
+
maxTurns,
|
|
94
|
+
error,
|
|
95
|
+
state
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep };
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=workflow-loop.mjs.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { SerializableWorkflowState, VercelAgentWorkflowAwaitingInput, VercelAgentWorkflowInput, VercelAgentWorkflowLoopConfig, VercelAgentWorkflowModelStepInput, VercelAgentWorkflowModelStepResult, VercelAgentWorkflowRunResult, VercelAgentWorkflowStepRetryPolicy, VercelAgentWorkflowToolBatchStepInput, VercelAgentWorkflowToolBatchStepResult, WorkflowStepResult, defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep } from "./workflow-loop.mjs";
|
|
2
|
+
export { type SerializableWorkflowState, type VercelAgentWorkflowAwaitingInput, type VercelAgentWorkflowInput, type VercelAgentWorkflowLoopConfig, type VercelAgentWorkflowModelStepInput, type VercelAgentWorkflowModelStepResult, type VercelAgentWorkflowRunResult, type VercelAgentWorkflowStepRetryPolicy, type VercelAgentWorkflowToolBatchStepInput, type VercelAgentWorkflowToolBatchStepResult, type WorkflowStepResult, defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep };
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yolk-sdk/vercel-workflows-runtime",
|
|
3
|
+
"version": "0.0.1-canary.0",
|
|
4
|
+
"description": "Vercel Workflow orchestration primitives for Yolk agent loops.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/magoz/yolk-sdk.git",
|
|
11
|
+
"directory": "packages/vercel-workflows-runtime"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/magoz/yolk-sdk/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/magoz/yolk-sdk#readme",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"vercel",
|
|
19
|
+
"workflow",
|
|
20
|
+
"agents",
|
|
21
|
+
"runtime",
|
|
22
|
+
"effect"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
"./package.json": "./package.json",
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"import": "./dist/index.mjs",
|
|
32
|
+
"default": "./dist/index.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./workflow": {
|
|
35
|
+
"types": "./dist/workflow.d.mts",
|
|
36
|
+
"import": "./dist/workflow.mjs",
|
|
37
|
+
"default": "./dist/workflow.mjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"src/**/*.ts",
|
|
42
|
+
"!src/**/*.test.ts",
|
|
43
|
+
"!src/**/*.test.tsx",
|
|
44
|
+
"dist/**/*",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public",
|
|
49
|
+
"provenance": true
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"effect": "4.0.0-beta.65",
|
|
53
|
+
"workflow": "^4.2.4"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@effect/vitest": "4.0.0-beta.65",
|
|
57
|
+
"@workflow/vitest": "^4.0.5",
|
|
58
|
+
"vitest": "^4.0.17"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsdown",
|
|
62
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
63
|
+
"test": "vitest run --passWithNoTests",
|
|
64
|
+
"test:workflow": "vitest run --config vitest.workflow.config.ts",
|
|
65
|
+
"test:run": "vitest run --passWithNoTests"
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
export type VercelAgentWorkflowInput = {
|
|
2
|
+
readonly request: unknown
|
|
3
|
+
readonly context: unknown
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type SerializableWorkflowState = {
|
|
7
|
+
readonly request: unknown
|
|
8
|
+
readonly messages?: ReadonlyArray<unknown>
|
|
9
|
+
readonly createdMessages: ReadonlyArray<unknown>
|
|
10
|
+
readonly usage?: unknown
|
|
11
|
+
readonly turn: number
|
|
12
|
+
readonly eventSequence?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type VercelAgentWorkflowModelStepInput = {
|
|
16
|
+
readonly context: unknown
|
|
17
|
+
readonly state: SerializableWorkflowState
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type VercelAgentWorkflowModelStepResult = {
|
|
21
|
+
readonly done: boolean
|
|
22
|
+
readonly messages: ReadonlyArray<unknown>
|
|
23
|
+
readonly createdMessages: ReadonlyArray<unknown>
|
|
24
|
+
readonly toolCalls: ReadonlyArray<unknown>
|
|
25
|
+
readonly usage: unknown
|
|
26
|
+
readonly turn: number
|
|
27
|
+
readonly eventSequence?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type VercelAgentWorkflowToolBatchStepInput = {
|
|
31
|
+
readonly context: unknown
|
|
32
|
+
readonly request: unknown
|
|
33
|
+
readonly calls: ReadonlyArray<unknown>
|
|
34
|
+
readonly createdMessages: ReadonlyArray<unknown>
|
|
35
|
+
readonly hitlResponses?: ReadonlyArray<unknown>
|
|
36
|
+
readonly usage?: unknown
|
|
37
|
+
readonly turn?: number
|
|
38
|
+
readonly eventSequence?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type VercelAgentWorkflowAwaitingInput = {
|
|
42
|
+
readonly hookToken: string
|
|
43
|
+
readonly requests: ReadonlyArray<unknown>
|
|
44
|
+
readonly messages: ReadonlyArray<unknown>
|
|
45
|
+
readonly usage: unknown
|
|
46
|
+
readonly turns: number
|
|
47
|
+
readonly eventSequence?: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type VercelAgentWorkflowToolBatchStepResult = {
|
|
51
|
+
readonly messages: ReadonlyArray<unknown>
|
|
52
|
+
readonly createdMessages: ReadonlyArray<unknown>
|
|
53
|
+
readonly awaitingInput?: VercelAgentWorkflowAwaitingInput
|
|
54
|
+
readonly eventSequence?: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type WorkflowStepResult<A> =
|
|
58
|
+
| {
|
|
59
|
+
readonly _tag: 'Success'
|
|
60
|
+
readonly value: A
|
|
61
|
+
}
|
|
62
|
+
| {
|
|
63
|
+
readonly _tag: 'Failure'
|
|
64
|
+
readonly error: unknown
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type VercelAgentWorkflowStepRetryPolicy = {
|
|
68
|
+
readonly maxAttempts: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type VercelAgentWorkflowRunResult =
|
|
72
|
+
| {
|
|
73
|
+
readonly _tag: 'Completed'
|
|
74
|
+
readonly turns: number
|
|
75
|
+
readonly state: SerializableWorkflowState
|
|
76
|
+
}
|
|
77
|
+
| {
|
|
78
|
+
readonly _tag: 'ModelStepFailed'
|
|
79
|
+
readonly turn: number
|
|
80
|
+
readonly error: unknown
|
|
81
|
+
readonly state: SerializableWorkflowState
|
|
82
|
+
}
|
|
83
|
+
| {
|
|
84
|
+
readonly _tag: 'ToolBatchStepFailed'
|
|
85
|
+
readonly turn: number
|
|
86
|
+
readonly error: unknown
|
|
87
|
+
readonly state: SerializableWorkflowState
|
|
88
|
+
}
|
|
89
|
+
| {
|
|
90
|
+
readonly _tag: 'CloseStreamFailed'
|
|
91
|
+
readonly turns: number
|
|
92
|
+
readonly error: unknown
|
|
93
|
+
readonly state: SerializableWorkflowState
|
|
94
|
+
}
|
|
95
|
+
| {
|
|
96
|
+
readonly _tag: 'MaxTurnsExceeded'
|
|
97
|
+
readonly maxTurns: number
|
|
98
|
+
readonly error: Error
|
|
99
|
+
readonly state: SerializableWorkflowState
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type VercelAgentWorkflowLoopConfig = {
|
|
103
|
+
readonly input: VercelAgentWorkflowInput
|
|
104
|
+
readonly maxTurns?: number
|
|
105
|
+
readonly runModelStep: (
|
|
106
|
+
input: VercelAgentWorkflowModelStepInput
|
|
107
|
+
) => Promise<VercelAgentWorkflowModelStepResult>
|
|
108
|
+
readonly runToolBatchStep: (
|
|
109
|
+
input: VercelAgentWorkflowToolBatchStepInput
|
|
110
|
+
) => Promise<VercelAgentWorkflowToolBatchStepResult>
|
|
111
|
+
readonly closeStream: () => Promise<void>
|
|
112
|
+
readonly writeError: (error: unknown) => Promise<void>
|
|
113
|
+
readonly modelStepRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
114
|
+
readonly toolBatchStepRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
115
|
+
readonly closeStreamRetry?: VercelAgentWorkflowStepRetryPolicy
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const defaultMaxWorkflowTurns = 500
|
|
119
|
+
export const noWorkflowStepRetry: VercelAgentWorkflowStepRetryPolicy = { maxAttempts: 1 }
|
|
120
|
+
|
|
121
|
+
export const settleWorkflowStep = <A>(promise: Promise<A>): Promise<WorkflowStepResult<A>> =>
|
|
122
|
+
promise.then(
|
|
123
|
+
value => ({ _tag: 'Success', value }),
|
|
124
|
+
error => ({ _tag: 'Failure', error })
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
const workflowMaxTurnsError = (maxTurns: number) =>
|
|
128
|
+
new Error(`Vercel agent workflow exceeded max turns: ${maxTurns}`)
|
|
129
|
+
|
|
130
|
+
const writeErrorSafely = (writeError: (error: unknown) => Promise<void>, error: unknown) =>
|
|
131
|
+
writeError(error).catch(() => undefined)
|
|
132
|
+
|
|
133
|
+
const maxRetryAttempts = (policy: VercelAgentWorkflowStepRetryPolicy | undefined) =>
|
|
134
|
+
Math.max(1, Math.floor(policy?.maxAttempts ?? noWorkflowStepRetry.maxAttempts))
|
|
135
|
+
|
|
136
|
+
export async function retryWorkflowStep<A>(
|
|
137
|
+
runStep: () => Promise<A>,
|
|
138
|
+
policy?: VercelAgentWorkflowStepRetryPolicy
|
|
139
|
+
): Promise<A> {
|
|
140
|
+
const maxAttempts = maxRetryAttempts(policy)
|
|
141
|
+
let attempt = 1
|
|
142
|
+
|
|
143
|
+
for (;;) {
|
|
144
|
+
try {
|
|
145
|
+
return await runStep()
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (attempt >= maxAttempts) {
|
|
148
|
+
throw error
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
attempt += 1
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function runVercelAgentWorkflow(
|
|
157
|
+
config: VercelAgentWorkflowLoopConfig
|
|
158
|
+
): Promise<VercelAgentWorkflowRunResult> {
|
|
159
|
+
let state: SerializableWorkflowState = {
|
|
160
|
+
request: config.input.request,
|
|
161
|
+
createdMessages: [],
|
|
162
|
+
turn: 1,
|
|
163
|
+
eventSequence: 0
|
|
164
|
+
}
|
|
165
|
+
const maxTurns = config.maxTurns ?? defaultMaxWorkflowTurns
|
|
166
|
+
|
|
167
|
+
for (let step = 0; step < maxTurns; step++) {
|
|
168
|
+
const modelResult = await settleWorkflowStep(
|
|
169
|
+
retryWorkflowStep(
|
|
170
|
+
() => config.runModelStep({ context: config.input.context, state }),
|
|
171
|
+
config.modelStepRetry
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if (modelResult._tag === 'Failure') {
|
|
176
|
+
await writeErrorSafely(config.writeError, modelResult.error)
|
|
177
|
+
return {
|
|
178
|
+
_tag: 'ModelStepFailed',
|
|
179
|
+
turn: state.turn,
|
|
180
|
+
error: modelResult.error,
|
|
181
|
+
state
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (modelResult.value.done) {
|
|
186
|
+
const closeResult = await settleWorkflowStep(
|
|
187
|
+
retryWorkflowStep(config.closeStream, config.closeStreamRetry)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if (closeResult._tag === 'Failure') {
|
|
191
|
+
await writeErrorSafely(config.writeError, closeResult.error)
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
_tag: 'CloseStreamFailed',
|
|
195
|
+
turns: modelResult.value.turn,
|
|
196
|
+
error: closeResult.error,
|
|
197
|
+
state
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
_tag: 'Completed',
|
|
203
|
+
turns: modelResult.value.turn,
|
|
204
|
+
state
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const toolsResult = await settleWorkflowStep(
|
|
209
|
+
retryWorkflowStep(
|
|
210
|
+
() =>
|
|
211
|
+
config.runToolBatchStep({
|
|
212
|
+
context: config.input.context,
|
|
213
|
+
request: config.input.request,
|
|
214
|
+
calls: modelResult.value.toolCalls,
|
|
215
|
+
createdMessages: modelResult.value.createdMessages,
|
|
216
|
+
turn: modelResult.value.turn,
|
|
217
|
+
eventSequence: modelResult.value.eventSequence ?? state.eventSequence
|
|
218
|
+
}),
|
|
219
|
+
config.toolBatchStepRetry
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if (toolsResult._tag === 'Failure') {
|
|
224
|
+
await writeErrorSafely(config.writeError, toolsResult.error)
|
|
225
|
+
return {
|
|
226
|
+
_tag: 'ToolBatchStepFailed',
|
|
227
|
+
turn: modelResult.value.turn,
|
|
228
|
+
error: toolsResult.error,
|
|
229
|
+
state
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
state = {
|
|
234
|
+
request: config.input.request,
|
|
235
|
+
messages: [...modelResult.value.messages, ...toolsResult.value.messages],
|
|
236
|
+
createdMessages: toolsResult.value.createdMessages,
|
|
237
|
+
usage: modelResult.value.usage,
|
|
238
|
+
turn: modelResult.value.turn + 1,
|
|
239
|
+
eventSequence: toolsResult.value.eventSequence ?? modelResult.value.eventSequence ?? state.eventSequence
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const error = workflowMaxTurnsError(maxTurns)
|
|
244
|
+
await writeErrorSafely(config.writeError, error)
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
_tag: 'MaxTurnsExceeded',
|
|
248
|
+
maxTurns,
|
|
249
|
+
error,
|
|
250
|
+
state
|
|
251
|
+
}
|
|
252
|
+
}
|
package/src/workflow.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export {
|
|
2
|
+
defaultMaxWorkflowTurns,
|
|
3
|
+
noWorkflowStepRetry,
|
|
4
|
+
retryWorkflowStep,
|
|
5
|
+
runVercelAgentWorkflow,
|
|
6
|
+
settleWorkflowStep
|
|
7
|
+
} from './workflow-loop.ts'
|
|
8
|
+
export type {
|
|
9
|
+
SerializableWorkflowState,
|
|
10
|
+
VercelAgentWorkflowAwaitingInput,
|
|
11
|
+
VercelAgentWorkflowInput,
|
|
12
|
+
VercelAgentWorkflowLoopConfig,
|
|
13
|
+
VercelAgentWorkflowModelStepInput,
|
|
14
|
+
VercelAgentWorkflowModelStepResult,
|
|
15
|
+
VercelAgentWorkflowRunResult,
|
|
16
|
+
VercelAgentWorkflowStepRetryPolicy,
|
|
17
|
+
VercelAgentWorkflowToolBatchStepInput,
|
|
18
|
+
VercelAgentWorkflowToolBatchStepResult,
|
|
19
|
+
WorkflowStepResult
|
|
20
|
+
} from './workflow-loop.ts'
|