@yolk-sdk/vercel-workflows-runtime 0.0.1-canary.5 → 0.0.1-canary.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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 between steps
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.
@@ -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,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"}
1
+ {"version":3,"file":"workflow-loop.d.mts","names":[],"sources":["../src/workflow-loop.ts"],"mappings":"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"}
@@ -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: config.input.request,
28
+ request: input.request,
27
29
  createdMessages: [],
28
30
  turn: 1,
29
31
  eventSequence: 0
30
32
  };
31
- const maxTurns = config.maxTurns ?? 500;
33
+ const maxTurns = configuredMaxTurns ?? 500;
32
34
  for (let step = 0; step < maxTurns; step++) {
33
- const modelResult = await settleWorkflowStep(retryWorkflowStep(() => config.runModelStep({
34
- context: config.input.context,
35
+ const modelResult = await settleWorkflowStep(retryWorkflowStep(() => runModelStep({
36
+ context: input.context,
35
37
  state
36
- }), config.modelStepRetry));
38
+ }), modelStepRetry));
37
39
  if (modelResult._tag === "Failure") {
38
- await writeErrorSafely(config.writeError, modelResult.error);
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(config.closeStream, config.closeStreamRetry));
49
+ const closeResult = await settleWorkflowStep(retryWorkflowStep(closeStream, closeStreamRetry));
48
50
  if (closeResult._tag === "Failure") {
49
- await writeErrorSafely(config.writeError, closeResult.error);
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
- 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);
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: toolsResult.error,
116
+ error,
77
117
  state
78
118
  };
79
119
  }
80
120
  state = {
81
- request: config.input.request,
82
- messages: [...modelResult.value.messages, ...toolsResult.value.messages],
83
- createdMessages: toolsResult.value.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: toolsResult.value.eventSequence ?? modelResult.value.eventSequence ?? state.eventSequence
126
+ eventSequence: completedToolsResult.eventSequence ?? toolEventSequence
87
127
  };
88
128
  }
89
129
  const error = workflowMaxTurnsError(maxTurns);
90
- await writeErrorSafely(config.writeError, error);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yolk-sdk/vercel-workflows-runtime",
3
- "version": "0.0.1-canary.5",
3
+ "version": "0.0.1-canary.7",
4
4
  "description": "Vercel Workflow orchestration primitives for Yolk agent loops.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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: config.input.request,
185
+ request: input.request,
161
186
  createdMessages: [],
162
187
  turn: 1,
163
188
  eventSequence: 0
164
189
  }
165
- const maxTurns = config.maxTurns ?? defaultMaxWorkflowTurns
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
- () => config.runModelStep({ context: config.input.context, state }),
171
- config.modelStepRetry
195
+ () => runModelStep({ context: input.context, state }),
196
+ modelStepRetry
172
197
  )
173
198
  )
174
199
 
175
200
  if (modelResult._tag === 'Failure') {
176
- await writeErrorSafely(config.writeError, modelResult.error)
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(config.closeStream, config.closeStreamRetry)
212
+ retryWorkflowStep(closeStream, closeStreamRetry)
188
213
  )
189
214
 
190
215
  if (closeResult._tag === 'Failure') {
191
- await writeErrorSafely(config.writeError, closeResult.error)
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
- 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
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
- if (toolsResult._tag === 'Failure') {
224
- await writeErrorSafely(config.writeError, toolsResult.error)
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: toolsResult.error,
309
+ error,
229
310
  state
230
311
  }
231
312
  }
232
313
 
233
314
  state = {
234
- request: config.input.request,
235
- messages: [...modelResult.value.messages, ...toolsResult.value.messages],
236
- createdMessages: toolsResult.value.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: toolsResult.value.eventSequence ?? modelResult.value.eventSequence ?? state.eventSequence
320
+ eventSequence: completedToolsResult.eventSequence ?? toolEventSequence
240
321
  }
241
322
  }
242
323
 
243
324
  const error = workflowMaxTurnsError(maxTurns)
244
- await writeErrorSafely(config.writeError, error)
325
+ await writeErrorSafely(writeError, error)
245
326
 
246
327
  return {
247
328
  _tag: 'MaxTurnsExceeded',