@yolk-sdk/vercel-workflows 0.1.0-canary.100
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 +340 -0
- package/dist/effect.d.mts +56 -0
- package/dist/effect.d.mts.map +1 -0
- package/dist/effect.mjs +58 -0
- package/dist/effect.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/testing/index.d.mts +97 -0
- package/dist/testing/index.d.mts.map +1 -0
- package/dist/testing/index.mjs +285 -0
- package/dist/testing/index.mjs.map +1 -0
- package/dist/workflow-children.d.mts +42 -0
- package/dist/workflow-children.d.mts.map +1 -0
- package/dist/workflow-children.mjs +54 -0
- package/dist/workflow-children.mjs.map +1 -0
- package/dist/workflow-events.d.mts +210 -0
- package/dist/workflow-events.d.mts.map +1 -0
- package/dist/workflow-events.mjs +54 -0
- package/dist/workflow-events.mjs.map +1 -0
- package/dist/workflow-loop.d.mts +416 -0
- package/dist/workflow-loop.d.mts.map +1 -0
- package/dist/workflow-loop.mjs +177 -0
- package/dist/workflow-loop.mjs.map +1 -0
- package/dist/workflow.d.mts +4 -0
- package/dist/workflow.mjs +4 -0
- package/package.json +77 -0
- package/src/effect.ts +232 -0
- package/src/index.ts +36 -0
- package/src/testing/index.ts +503 -0
- package/src/workflow-children.ts +81 -0
- package/src/workflow-events.ts +202 -0
- package/src/workflow-loop.ts +404 -0
- package/src/workflow.ts +42 -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,340 @@
|
|
|
1
|
+
# @yolk-sdk/vercel-workflows
|
|
2
|
+
|
|
3
|
+
Vercel Workflow agent-loop contracts, durable stream helpers, and an Effect-native Workflow client/layer.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @yolk-sdk/vercel-workflows@canary effect@4.0.0 workflow@^5.0.0-beta.42
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Canary APIs are unstable. Keep all `@yolk-sdk/*` packages on the same version.
|
|
12
|
+
Use the SDK's matching Effect version (`4.0.0`) in host code.
|
|
13
|
+
Requires Node.js 22+ and a server runtime supported by Vercel Workflow.
|
|
14
|
+
|
|
15
|
+
Requires `workflow@^5.0.0-beta.42`. The 5.x line region-pins Vercel runs so durable streams are
|
|
16
|
+
served from the deployment's own region; the 4.x backend was `iad1`-only, which made deployed
|
|
17
|
+
streaming from other regions arrive in multi-second bursts. Host apps must resolve a single
|
|
18
|
+
`workflow` instance shared with this package.
|
|
19
|
+
|
|
20
|
+
## Subpaths
|
|
21
|
+
|
|
22
|
+
| Subpath | Purpose |
|
|
23
|
+
| ------------------------------------- | ------------------------------------------------------- |
|
|
24
|
+
| `@yolk-sdk/vercel-workflows` | Workflow-safe agent loop orchestration APIs |
|
|
25
|
+
| `@yolk-sdk/vercel-workflows/workflow` | Explicit equivalent subpath |
|
|
26
|
+
| `@yolk-sdk/vercel-workflows/effect` | Effect-native wrapper around public `workflow/api` APIs |
|
|
27
|
+
| `@yolk-sdk/vercel-workflows/testing` | `TestWorkflowWorld` behavioral platform emulator |
|
|
28
|
+
|
|
29
|
+
## Imports
|
|
30
|
+
|
|
31
|
+
Import Workflow APIs from the package root:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import {
|
|
35
|
+
commitThenWriteTerminalEvent,
|
|
36
|
+
makeDurableAgentEventSequencerState,
|
|
37
|
+
noWorkflowStepRetry,
|
|
38
|
+
runVercelAgentWorkflow,
|
|
39
|
+
VercelAgentWorkflowRunResult,
|
|
40
|
+
WorkflowStepResult,
|
|
41
|
+
writeDurableAgentEvent
|
|
42
|
+
} from '@yolk-sdk/vercel-workflows'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The `./workflow` subpath is also exported for explicit imports.
|
|
46
|
+
|
|
47
|
+
Use `./effect` at host boundaries that start, replay, resume, or cancel Workflow runs. This is a
|
|
48
|
+
host-wiring sketch; `runAgentWorkflow`, `request`, and `context` are host-owned values:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { Effect } from 'effect'
|
|
52
|
+
import { VercelWorkflows } from '@yolk-sdk/vercel-workflows/effect'
|
|
53
|
+
|
|
54
|
+
const program = Effect.gen(function* () {
|
|
55
|
+
const workflows = yield* VercelWorkflows
|
|
56
|
+
const run = yield* workflows.start(runAgentWorkflow, [{ request, context }])
|
|
57
|
+
|
|
58
|
+
return yield* run.getReadable<Uint8Array>()
|
|
59
|
+
}).pipe(Effect.provide(VercelWorkflows.layer))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The API wrapper keeps Vercel's SDK underneath. It does not reimplement Vercel backend HTTP,
|
|
63
|
+
queues, hooks, or stream storage.
|
|
64
|
+
|
|
65
|
+
## Test Workflow behavior
|
|
66
|
+
|
|
67
|
+
`@yolk-sdk/vercel-workflows/testing` provides `TestWorkflowWorld`, a behavioral emulator for host
|
|
68
|
+
tests. It covers run lifecycle, append-only streams with close-once conflicts, step retries and
|
|
69
|
+
attempt metadata, hooks/resume, cancellation, and the SDK seam used by
|
|
70
|
+
`VercelWorkflows.layerFromSdk`. Ambient module mocks do not apply retry policy by themselves: wrap
|
|
71
|
+
host step callbacks with `world.step(...)` or call `world.runStep(...)` explicitly when the test
|
|
72
|
+
must exercise platform retry attempts and `getStepMetadata().attempt`.
|
|
73
|
+
|
|
74
|
+
Mock the ambient `workflow` module so production orchestration runs against the active test world:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { vi } from 'vitest'
|
|
78
|
+
import { TestWorkflowWorld } from '@yolk-sdk/vercel-workflows/testing'
|
|
79
|
+
|
|
80
|
+
vi.mock('workflow', async () => {
|
|
81
|
+
const { testWorkflowModule } = await import('@yolk-sdk/vercel-workflows/testing')
|
|
82
|
+
return testWorkflowModule
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const world = new TestWorkflowWorld()
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Tests whose import graph reaches `workflow` must use Vitest's Node environment, not jsdom, because
|
|
89
|
+
Workflow 5 captures Node `URL` intrinsics at import time. Hosts should test their own `'use workflow'`
|
|
90
|
+
and `'use step'` directive files with `@workflow/vitest` in addition to using the emulator.
|
|
91
|
+
|
|
92
|
+
## Runtime model
|
|
93
|
+
|
|
94
|
+
`runVercelAgentWorkflow` coordinates host-provided callbacks:
|
|
95
|
+
|
|
96
|
+
- model step: produce model events/tool calls
|
|
97
|
+
- no-tool continuation: when a model step returns `done: false` with no tool calls, continue
|
|
98
|
+
directly to the next model step without invoking the tool-batch callback
|
|
99
|
+
- tool batch step: execute requested tools
|
|
100
|
+
- tool batch result: return one ordered tool-result message per host call, including failed `isError` results
|
|
101
|
+
- partial tool-step failure: return wire-safe `failure`, cumulative `usage`, and `eventSequence` when
|
|
102
|
+
completed sibling tools must remain observable
|
|
103
|
+
- awaiting-input state: carry pending HITL hook data, wait through `awaitInput`, then rerun the
|
|
104
|
+
same tool batch with accumulated responses
|
|
105
|
+
- close step: flush/close output stream after a successful final model step
|
|
106
|
+
- terminal status: completed, step failure, await-input failure, close failure, or max turns exceeded
|
|
107
|
+
|
|
108
|
+
`WorkflowStepResult` and `VercelAgentWorkflowRunResult` are `Data.taggedEnum` **values** (plain objects,
|
|
109
|
+
`_tag` last), re-exported from the package root and `./workflow`. `settleWorkflowStep` returns
|
|
110
|
+
`WorkflowStepResult.Success({ value })` / `.Failure({ error })`. Inspect run status with constructors
|
|
111
|
+
or `VercelAgentWorkflowRunResult.$is('Completed')` rather than handwritten `{ _tag }` objects.
|
|
112
|
+
|
|
113
|
+
Continuation state stays plain serializable data for Workflow persistence.
|
|
114
|
+
|
|
115
|
+
## Minimal workflow wrapper
|
|
116
|
+
|
|
117
|
+
Host apps keep concrete Workflow directives local and pass them into the package loop. The callback
|
|
118
|
+
names below are host-owned `'use step'` functions:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { createHook } from 'workflow'
|
|
122
|
+
import { runVercelAgentWorkflow } from '@yolk-sdk/vercel-workflows'
|
|
123
|
+
|
|
124
|
+
export async function runAgentWorkflow(input: { request: unknown; context: unknown }) {
|
|
125
|
+
'use workflow'
|
|
126
|
+
|
|
127
|
+
return await runVercelAgentWorkflow({
|
|
128
|
+
input,
|
|
129
|
+
runModelStep,
|
|
130
|
+
runToolBatchStep,
|
|
131
|
+
closeStream,
|
|
132
|
+
writeError,
|
|
133
|
+
awaitInput: async awaitingInput => {
|
|
134
|
+
using hook = createHook<unknown>({ token: awaitingInput.hookToken })
|
|
135
|
+
|
|
136
|
+
return await hook
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`runModelStep`, `closeStream`, and `writeError` are host-owned `'use step'` functions.
|
|
143
|
+
`runToolBatchStep` may instead be Workflow orchestration calling concrete per-call steps. Keep provider calls, tools, persistence, telemetry, and Effect runtimes in
|
|
144
|
+
those steps, not in the `'use workflow'` orchestration body.
|
|
145
|
+
|
|
146
|
+
## Tool steps are at-least-once
|
|
147
|
+
|
|
148
|
+
Vercel's queue delivers steps at least once. A tool-batch step can run again after a crash and,
|
|
149
|
+
in practice, concurrently with a first execution that is still running, even with
|
|
150
|
+
`noWorkflowStepRetry`. Retry policy cannot prevent duplicate writes; a durable tool ledger can.
|
|
151
|
+
This package does not build tool sets or call `runToolBatch` (your step does), so wire the ledger
|
|
152
|
+
where you resolve tools inside the step:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
// Inside the host's tool-batch 'use step' function.
|
|
156
|
+
const resolveStepTools = Effect.gen(function* () {
|
|
157
|
+
return yield* resolveTools(modules, context, {
|
|
158
|
+
ledger: { store: makeRunToolLedger(workflowRunId), deadline: () => stepDeadlineMs }
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`resolveTools` and `ToolLedgerStore` come from `@yolk-sdk/agent/tools` (install
|
|
164
|
+
`@yolk-sdk/agent@canary` in the host; this package does not depend on it); `makeRunToolLedger` is
|
|
165
|
+
your durable store (for example Postgres) scoped to the Workflow run. Ledgered calls (by default every
|
|
166
|
+
non-`read` tool plus the built-in `subagent` tool, including code mode scripts and their nested
|
|
167
|
+
writes) then return their recorded result, wait for the running execution, or report that an
|
|
168
|
+
abandoned call may already have been applied; they never run twice under the same ledger key.
|
|
169
|
+
Add custom delegation tools with `isLedgered`. Input and interaction tools are never ledgered (they
|
|
170
|
+
rely on the host's `InteractionHost` receipts). Read calls still re-run, and after an in-flight
|
|
171
|
+
timeout the model may retry under a new call id. An in-memory store does not protect steps.
|
|
172
|
+
`deadline` bounds only waiting for a duplicate: recording a call's outcome can take up to about
|
|
173
|
+
15 s after the call returns, so leave that time before the function budget ends, and keep store
|
|
174
|
+
operations interruptible (the ledger's timeouts cannot cut uninterruptible store work).
|
|
175
|
+
|
|
176
|
+
## Independent children
|
|
177
|
+
|
|
178
|
+
`orchestrateWorkflowToolBatch` runs a full-batch `preflight` before any `execute` callback,
|
|
179
|
+
limits local concurrency, and returns results in original call order. Both callbacks are host
|
|
180
|
+
seams: perform IO in steps, not in the orchestration helper. The host commits exactly one tool
|
|
181
|
+
result per call. Return a pause value from preflight when any HITL request is pending.
|
|
182
|
+
If an execution rejects, the helper stops dispatching new calls, settles already-active siblings,
|
|
183
|
+
and returns their ordered progress plus `failures`. Hosts must propagate those failures while
|
|
184
|
+
retaining completed results and usage; do not treat a partial batch as successful.
|
|
185
|
+
|
|
186
|
+
Start child workflows with Workflow 5 `start(childWorkflow, args)` **from orchestration**, not
|
|
187
|
+
inside the parent tool step. A child wrapper runs its own model/tool steps and returns a typed
|
|
188
|
+
application outcome; platform `completed` alone does not prove application success. Foreground
|
|
189
|
+
hosts await that child outcome; background hosts return an accepted handle and continue.
|
|
190
|
+
|
|
191
|
+
`awaitWorkflowChild({ read, sleep })` polls short durable read steps with a host-provided Workflow
|
|
192
|
+
`sleep` between reads. Both callbacks receive the same zero-based attempt index, so hosts can
|
|
193
|
+
apply capped backoff and end an observation after a bounded number of reads. At that ceiling,
|
|
194
|
+
`read` should return `{ done: true, value: stillRunningObservation }`; this ends polling, not the
|
|
195
|
+
child lifecycle. Preserve an owned handle for eventual result lookup. Existing zero-argument
|
|
196
|
+
callbacks remain supported and the helper has no implicit budget. Hosts must explicitly bound
|
|
197
|
+
long waits to avoid unbounded durable event accumulation. Do not hold a step open awaiting a long
|
|
198
|
+
`run.returnValue` poll.
|
|
199
|
+
|
|
200
|
+
Hosts own immutable logical reservations, admission/CAS, ownership checks, storage, bounded
|
|
201
|
+
fanout, safe errors, credentials, and cancellation policy. Parent completion/failure must not
|
|
202
|
+
implicitly cancel independent children. Explicit Stop should persist a durable tombstone before
|
|
203
|
+
sweeping admitted child ids, reject late admission, and expose partial sweep failures for retry.
|
|
204
|
+
Give each independent stream and parallel call its own deterministic event-id namespace.
|
|
205
|
+
|
|
206
|
+
## Awaiting input
|
|
207
|
+
|
|
208
|
+
When a tool batch needs HITL input, return `awaitingInput` from `runToolBatchStep`. The package
|
|
209
|
+
loop will:
|
|
210
|
+
|
|
211
|
+
1. pass that payload to `awaitInput`
|
|
212
|
+
2. wait for the hook/webhook response
|
|
213
|
+
3. rerun the same tool batch with the accumulated responses in a fresh `hitlResponses` array
|
|
214
|
+
|
|
215
|
+
The loop owns its accumulator: host mutation of a supplied array cannot affect a later batch.
|
|
216
|
+
Response order and element identity are preserved; response objects are not deep-cloned.
|
|
217
|
+
|
|
218
|
+
The host owns `hookToken` construction, auth, resume routes, and response decoding. Use stable
|
|
219
|
+
tokens that the resume side can reconstruct or store.
|
|
220
|
+
|
|
221
|
+
## Retry policy
|
|
222
|
+
|
|
223
|
+
Default retry policy is `noWorkflowStepRetry` (`maxAttempts: 1`). Retries are opt-in because
|
|
224
|
+
streamed retries can duplicate chunks unless host/client de-dupe is ready.
|
|
225
|
+
Finite `maxAttempts` values are floored and clamped to at least one; non-finite values normalize to
|
|
226
|
+
one. Retries have no built-in backoff.
|
|
227
|
+
|
|
228
|
+
One attempt does not stop queue redelivery: a step can still run again after a crash or
|
|
229
|
+
concurrently. See [Tool steps are at-least-once](#tool-steps-are-at-least-once).
|
|
230
|
+
|
|
231
|
+
`maxTurns` follows the same positive-integer boundary: finite values are floored and clamped to at
|
|
232
|
+
least one. Omitted or non-finite values use `defaultMaxWorkflowTurns`.
|
|
233
|
+
|
|
234
|
+
## Durable stream events
|
|
235
|
+
|
|
236
|
+
Workflow streams can replay old chunks after retries or reconnects. Durable streams should emit an
|
|
237
|
+
`eventId` on every JSON-serializable event, typically `AgentEvent`, including errors. Clients should
|
|
238
|
+
track seen ids.
|
|
239
|
+
|
|
240
|
+
Use `writeDurableAgentEvent` to assign deterministic ids and write NDJSON. The following is an
|
|
241
|
+
`Effect.gen` fragment; `writer`, `event`, `runId`, `turn`, and `eventSequence` are host-owned:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const state = makeDurableAgentEventSequencerState(eventSequence)
|
|
245
|
+
const sequenced =
|
|
246
|
+
yield *
|
|
247
|
+
writeDurableAgentEvent({
|
|
248
|
+
writer,
|
|
249
|
+
event,
|
|
250
|
+
streamId: `workflow:${runId}`,
|
|
251
|
+
turn,
|
|
252
|
+
state
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
eventSequence = sequenced.nextEventSequence
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The id format is `${streamId}:${turn}:${eventSequence}`. Scope `streamId` to one logical durable
|
|
259
|
+
run (for example `workflow:${runId}`), not just one route or feature. Replay-safe clients de-dupe by
|
|
260
|
+
`eventId`; reusing a stream id across independent runs can make a follow-up run's live deltas look
|
|
261
|
+
like old replayed events. Keep `eventSequence` in Workflow state and reset it when starting a fresh
|
|
262
|
+
logical stream. Resume route reads should use `startIndex` when available. Host apps still own
|
|
263
|
+
active-run locking, stale-run guards, and cancellation behavior.
|
|
264
|
+
|
|
265
|
+
## Terminal barriers
|
|
266
|
+
|
|
267
|
+
For durable host streams, protocol terminal events are commit barriers. Emit live/progress events
|
|
268
|
+
first, commit canonical host state, then write the event.
|
|
269
|
+
|
|
270
|
+
“Terminal” in `isTerminalAgentEvent` means a logical client-consumption boundary, not necessarily a
|
|
271
|
+
final Workflow state. Final callback ownership is:
|
|
272
|
+
|
|
273
|
+
```txt
|
|
274
|
+
AgentEnd: durable completion commit -> AgentEnd -> closeStream
|
|
275
|
+
AgentError: failure commit/mapping -> AgentError -> writeError-owned close
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`AgentAwaitingInput` is terminal for the current client consumption window but resumable for the
|
|
279
|
+
Workflow run. Persist pending HITL state and write it without finalizing the durable stream solely
|
|
280
|
+
because `isTerminalAgentEvent` returns true:
|
|
281
|
+
|
|
282
|
+
```txt
|
|
283
|
+
persist pending HITL -> AgentAwaitingInput -> release writer lock (do not close) -> await hook -> continue
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
This lets clients stop consuming at a protocol-terminal event and immediately revalidate or reconnect
|
|
287
|
+
from durable state. Hosts that emit terminal events before persistence should not rely on
|
|
288
|
+
`streamAgentEventStreamUntilTerminal()` as a durable-settled signal.
|
|
289
|
+
|
|
290
|
+
Use `commitThenWriteTerminalEvent` when a step must commit host state before writing its terminal
|
|
291
|
+
event. This is an `Effect.gen` fragment whose terminal, persistence, writer, and stream values come
|
|
292
|
+
from the host:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const result =
|
|
296
|
+
yield *
|
|
297
|
+
commitThenWriteTerminalEvent({
|
|
298
|
+
terminal,
|
|
299
|
+
commit: persistState,
|
|
300
|
+
write: event => writeDurableAgentEvent({ writer, event, streamId, turn, state }),
|
|
301
|
+
writeCommitError: error =>
|
|
302
|
+
writeDurableAgentEvent({ writer, event: commitErrorEvent(error), streamId, turn, state })
|
|
303
|
+
})
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The helper never writes the success terminal before `commit` succeeds. If `commit` fails, it writes
|
|
307
|
+
the host-provided terminal error event instead. It can protect either a final event or an
|
|
308
|
+
`AgentAwaitingInput` persistence barrier; it never decides closure from `_tag` and never closes
|
|
309
|
+
writers.
|
|
310
|
+
|
|
311
|
+
`writeCommitError` receives the existing `CommitError` generic from `commit`
|
|
312
|
+
(`Effect.Effect<void, CommitError>`), via `Effect.matchEffect` `onFailure`. Typed failures—not
|
|
313
|
+
defects or interruptions—are passed to that callback. Result `commitError` and `error` fields stay
|
|
314
|
+
`unknown`.
|
|
315
|
+
Existing `(error: unknown) => …` callbacks remain assignable.
|
|
316
|
+
|
|
317
|
+
`runVercelAgentWorkflow` calls `closeStream` only after `runModelStep` returns `done: true`. On model,
|
|
318
|
+
tool, await-input, or max-turn failure it calls `writeError` best-effort and returns without calling
|
|
319
|
+
`closeStream`; `writeError` must write a safe run-final error and attempt to close that failure path.
|
|
320
|
+
Never close on resumable `AgentAwaitingInput`.
|
|
321
|
+
|
|
322
|
+
## Host responsibilities
|
|
323
|
+
|
|
324
|
+
- Own Next/Vercel routes, auth, providers, tools, persistence, and telemetry.
|
|
325
|
+
- Encode/decode app transcript/session state into serializable Workflow inputs.
|
|
326
|
+
- Preserve tool result order and never advance the next model step with dangling host tool calls.
|
|
327
|
+
- Decide cancellation/resume/conflict UX.
|
|
328
|
+
- Own hook tokens and response validation for HITL resume.
|
|
329
|
+
- Emit replay-safe event ids for durable streams; scope stream ids per independent run and de-dupe
|
|
330
|
+
by `eventId` client-side.
|
|
331
|
+
- Write durable terminal events only after host persistence has settled.
|
|
332
|
+
- Implement `closeStream` for successful final closure and `writeError` for safe final error write
|
|
333
|
+
plus failure closure. Release, but do not close, the writer after `AgentAwaitingInput`.
|
|
334
|
+
- Test host-owned Workflow directive files with `@workflow/vitest`; use the `./testing` emulator for
|
|
335
|
+
platform behavior in host tests.
|
|
336
|
+
|
|
337
|
+
## Boundaries
|
|
338
|
+
|
|
339
|
+
- No Next routes, server actions, auth, app tools, DB, UI, or provider SDKs.
|
|
340
|
+
- Effect may run inside host/package step callbacks; workflow orchestration keeps plain data contracts.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { WorkflowReadableStream, WorkflowReadableStreamOptions, WorkflowRun } from "workflow/api";
|
|
3
|
+
|
|
4
|
+
//#region src/effect.d.ts
|
|
5
|
+
type VercelWorkflowsOperation = 'start' | 'getRun' | 'getReadable' | 'tailIndex' | 'resumeHook' | 'cancel' | 'status' | 'returnValue';
|
|
6
|
+
declare const VercelWorkflowsError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & {
|
|
7
|
+
readonly _tag: "VercelWorkflowsError";
|
|
8
|
+
} & Readonly<A>;
|
|
9
|
+
declare class VercelWorkflowsError extends VercelWorkflowsError_base<{
|
|
10
|
+
readonly operation: VercelWorkflowsOperation;
|
|
11
|
+
readonly message: string;
|
|
12
|
+
readonly cause: unknown;
|
|
13
|
+
}> {}
|
|
14
|
+
type VercelWorkflowFunction<TArgs extends unknown[], TResult> = (...args: TArgs) => Promise<TResult>;
|
|
15
|
+
type VercelWorkflowReadableOptions = WorkflowReadableStreamOptions;
|
|
16
|
+
type VercelWorkflowReadableStream<Chunk> = WorkflowReadableStream<Chunk>;
|
|
17
|
+
type VercelWorkflowRunStatus = WorkflowRun['status'];
|
|
18
|
+
type VercelWorkflowsSdkRun<TResult> = {
|
|
19
|
+
readonly runId: string;
|
|
20
|
+
readonly getReadable: <Chunk>(options?: VercelWorkflowReadableOptions) => VercelWorkflowReadableStream<Chunk>;
|
|
21
|
+
readonly cancel: () => Promise<void>;
|
|
22
|
+
readonly status: Promise<VercelWorkflowRunStatus>;
|
|
23
|
+
readonly returnValue: Promise<TResult>;
|
|
24
|
+
};
|
|
25
|
+
type VercelWorkflowsSdkClient = {
|
|
26
|
+
readonly start: <TArgs extends unknown[], TResult>(workflow: VercelWorkflowFunction<TArgs, TResult>, args: TArgs) => Promise<VercelWorkflowsSdkRun<TResult>>;
|
|
27
|
+
readonly getRun: <TResult>(runId: string) => VercelWorkflowsSdkRun<TResult>;
|
|
28
|
+
readonly resumeHook: (token: string, payload: unknown) => Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
type VercelWorkflowRun<TResult> = {
|
|
31
|
+
readonly runId: string;
|
|
32
|
+
readonly getReadable: <Chunk>(options?: VercelWorkflowReadableOptions) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>;
|
|
33
|
+
readonly cancel: Effect.Effect<void, VercelWorkflowsError>;
|
|
34
|
+
readonly status: Effect.Effect<VercelWorkflowRunStatus, VercelWorkflowsError>;
|
|
35
|
+
readonly returnValue: Effect.Effect<TResult, VercelWorkflowsError>;
|
|
36
|
+
};
|
|
37
|
+
type VercelWorkflowsClient = {
|
|
38
|
+
readonly start: <TArgs extends unknown[], TResult>(workflow: VercelWorkflowFunction<TArgs, TResult>, args: TArgs) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>;
|
|
39
|
+
readonly getRun: <TResult>(runId: string) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>;
|
|
40
|
+
readonly getReadable: <Chunk>(runId: string, options?: VercelWorkflowReadableOptions) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>;
|
|
41
|
+
readonly tailIndex: (runId: string) => Effect.Effect<number, VercelWorkflowsError>;
|
|
42
|
+
readonly resumeHook: (token: string, payload: unknown) => Effect.Effect<void, VercelWorkflowsError>;
|
|
43
|
+
readonly cancel: (runId: string) => Effect.Effect<void, VercelWorkflowsError>;
|
|
44
|
+
};
|
|
45
|
+
declare const VercelWorkflowsSdk_base: Context.ServiceClass<VercelWorkflowsSdk, "@yolk-sdk/vercel-workflows/VercelWorkflowsSdk", VercelWorkflowsSdkClient>;
|
|
46
|
+
declare class VercelWorkflowsSdk extends VercelWorkflowsSdk_base {}
|
|
47
|
+
declare const VercelWorkflows_base: Context.ServiceClass<VercelWorkflows, "@yolk-sdk/vercel-workflows/VercelWorkflows", VercelWorkflowsClient> & {
|
|
48
|
+
readonly make: Effect.Effect<VercelWorkflowsClient, never, VercelWorkflowsSdk>;
|
|
49
|
+
};
|
|
50
|
+
declare class VercelWorkflows extends VercelWorkflows_base {
|
|
51
|
+
static layer: Layer.Layer<VercelWorkflows, never, never>;
|
|
52
|
+
static layerFromSdk: Layer.Layer<VercelWorkflows, never, VercelWorkflowsSdk>;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { VercelWorkflowFunction, VercelWorkflowReadableOptions, VercelWorkflowReadableStream, VercelWorkflowRun, VercelWorkflowRunStatus, VercelWorkflows, VercelWorkflowsClient, VercelWorkflowsError, VercelWorkflowsOperation, VercelWorkflowsSdk, VercelWorkflowsSdkClient, VercelWorkflowsSdkRun };
|
|
56
|
+
//# sourceMappingURL=effect.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect.d.mts","names":[],"sources":["../src/effect.ts"],"mappings":";;;;KAUY,wBAAA;AAAA,cAQK,yBAAA;;;cAEJ,oBAAA,SAA6B,yBAAA;EAAA,SAC/B,SAAA,EAAW,wBAAA;EAAA,SACX,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,KAGC,sBAAA,yCACP,IAAA,EAAM,KAAA,KACN,OAAA,CAAQ,OAAA;AAAA,KAED,6BAAA,GAAgC,6BAA6B;AAAA,KAE7D,4BAAA,UAAsC,sBAAsB,CAAC,KAAA;AAAA,KAE7D,uBAAA,GAA0B,WAAW;AAAA,KAErC,qBAAA;EAAA,SACD,KAAA;EAAA,SACA,WAAA,UACP,OAAA,GAAU,6BAAA,KACP,4BAAA,CAA6B,KAAA;EAAA,SACzB,MAAA,QAAc,OAAA;EAAA,SACd,MAAA,EAAQ,OAAA,CAAQ,uBAAA;EAAA,SAChB,WAAA,EAAa,OAAA,CAAQ,OAAA;AAAA;AAAA,KAGpB,wBAAA;EAAA,SACD,KAAA,qCACP,QAAA,EAAU,sBAAA,CAAuB,KAAA,EAAO,OAAA,GACxC,IAAA,EAAM,KAAA,KACH,OAAA,CAAQ,qBAAA,CAAsB,OAAA;EAAA,SAC1B,MAAA,YAAkB,KAAA,aAAkB,qBAAA,CAAsB,OAAA;EAAA,SAC1D,UAAA,GAAa,KAAA,UAAe,OAAA,cAAqB,OAAA;AAAA;AAAA,KAGhD,iBAAA;EAAA,SACD,KAAA;EAAA,SACA,WAAA,UACP,OAAA,GAAU,6BAAA,KACP,MAAA,CAAO,MAAA,CAAO,4BAAA,CAA6B,KAAA,GAAQ,oBAAA;EAAA,SAC/C,MAAA,EAAQ,MAAA,CAAO,MAAA,OAAa,oBAAA;EAAA,SAC5B,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,uBAAA,EAAyB,oBAAA;EAAA,SAC/C,WAAA,EAAa,MAAA,CAAO,MAAA,CAAO,OAAA,EAAS,oBAAA;AAAA;AAAA,KAGnC,qBAAA;EAAA,SACD,KAAA,qCACP,QAAA,EAAU,sBAAA,CAAuB,KAAA,EAAO,OAAA,GACxC,IAAA,EAAM,KAAA,KACH,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,OAAA,GAAU,oBAAA;EAAA,SACtC,MAAA,YACP,KAAA,aACG,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,OAAA,GAAU,oBAAA;EAAA,SACtC,WAAA,UACP,KAAA,UACA,OAAA,GAAU,6BAAA,KACP,MAAA,CAAO,MAAA,CAAO,4BAAA,CAA6B,KAAA,GAAQ,oBAAA;EAAA,SAC/C,SAAA,GAAY,KAAA,aAAkB,MAAA,CAAO,MAAA,SAAe,oBAAA;EAAA,SACpD,UAAA,GACP,KAAA,UACA,OAAA,cACG,MAAA,CAAO,MAAA,OAAa,oBAAA;EAAA,SAChB,MAAA,GAAS,KAAA,aAAkB,MAAA,CAAO,MAAA,OAAa,oBAAA;AAAA;AAAA,cACzD,uBAAA;cAEY,kBAAA,SAA2B,uBAGY;AAAA,cAAG,oBAAA;;;cAkE1C,eAAA,SAAwB,oBAAA;EAAA,OA2E5B,KAAA,EAAK,KAAA,CAAA,KAAA,CAAA,eAAA;EAAA,OACL,YAAA,EAAY,KAAA,CAAA,KAAA,CAAA,eAAA,SAAA,kBAAA;AAAA"}
|
package/dist/effect.mjs
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Context, Data, Effect, Layer } from "effect";
|
|
2
|
+
import { getRun, resumeHook, start } from "workflow/api";
|
|
3
|
+
//#region src/effect.ts
|
|
4
|
+
var VercelWorkflowsError = class extends Data.TaggedError("VercelWorkflowsError") {};
|
|
5
|
+
var VercelWorkflowsSdk = class extends Context.Service()("@yolk-sdk/vercel-workflows/VercelWorkflowsSdk") {};
|
|
6
|
+
const workflowsError = (operation, cause) => new VercelWorkflowsError({
|
|
7
|
+
operation,
|
|
8
|
+
message: `Vercel Workflow ${operation} failed`,
|
|
9
|
+
cause
|
|
10
|
+
});
|
|
11
|
+
const spanAttributes = (runId) => ({ "workflow.run.id": runId });
|
|
12
|
+
const trySdkPromise = (body) => Effect.tryPromise({
|
|
13
|
+
try: body,
|
|
14
|
+
catch: (error) => error
|
|
15
|
+
});
|
|
16
|
+
const mapSdkError = (operation) => (effect) => effect.pipe(Effect.mapError((cause) => workflowsError(operation, cause)));
|
|
17
|
+
const makeWorkflowRun = (run) => ({
|
|
18
|
+
runId: run.runId,
|
|
19
|
+
getReadable: (options) => Effect.try({
|
|
20
|
+
try: () => run.getReadable(options),
|
|
21
|
+
catch: (cause) => workflowsError("getReadable", cause)
|
|
22
|
+
}).pipe(Effect.withSpan("VercelWorkflows.run.getReadable", { attributes: spanAttributes(run.runId) })),
|
|
23
|
+
cancel: trySdkPromise(() => run.cancel()).pipe(mapSdkError("cancel"), Effect.withSpan("VercelWorkflows.run.cancel", { attributes: spanAttributes(run.runId) })),
|
|
24
|
+
status: trySdkPromise(() => run.status).pipe(mapSdkError("status"), Effect.withSpan("VercelWorkflows.run.status", { attributes: spanAttributes(run.runId) })),
|
|
25
|
+
returnValue: trySdkPromise(() => run.returnValue).pipe(mapSdkError("returnValue"), Effect.withSpan("VercelWorkflows.run.returnValue", { attributes: spanAttributes(run.runId) }))
|
|
26
|
+
});
|
|
27
|
+
const VercelWorkflowsSdkLive = Layer.succeed(VercelWorkflowsSdk, {
|
|
28
|
+
start: (workflow, args) => start(workflow, args),
|
|
29
|
+
getRun: (runId) => getRun(runId),
|
|
30
|
+
resumeHook: async (token, payload) => {
|
|
31
|
+
await resumeHook(token, payload);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
var VercelWorkflows = class extends Context.Service()("@yolk-sdk/vercel-workflows/VercelWorkflows", { make: Effect.gen(function* () {
|
|
35
|
+
const sdk = yield* VercelWorkflowsSdk;
|
|
36
|
+
const getRun = (runId) => Effect.try({
|
|
37
|
+
try: () => sdk.getRun(runId),
|
|
38
|
+
catch: (error) => error
|
|
39
|
+
}).pipe(mapSdkError("getRun"), Effect.map(makeWorkflowRun), Effect.withSpan("VercelWorkflows.getRun", { attributes: spanAttributes(runId) }));
|
|
40
|
+
return {
|
|
41
|
+
start: (workflow, args) => trySdkPromise(() => sdk.start(workflow, args)).pipe(mapSdkError("start"), Effect.map(makeWorkflowRun), Effect.withSpan("VercelWorkflows.start")),
|
|
42
|
+
getRun,
|
|
43
|
+
getReadable: (runId, options) => getRun(runId).pipe(Effect.flatMap((run) => run.getReadable(options)), Effect.withSpan("VercelWorkflows.getReadable", { attributes: spanAttributes(runId) })),
|
|
44
|
+
tailIndex: (runId) => getRun(runId).pipe(Effect.flatMap((run) => run.getReadable({ startIndex: -1 })), Effect.flatMap((readable) => Effect.tryPromise({
|
|
45
|
+
try: () => readable.getTailIndex(),
|
|
46
|
+
catch: (cause) => workflowsError("tailIndex", cause)
|
|
47
|
+
})), Effect.withSpan("VercelWorkflows.tailIndex", { attributes: spanAttributes(runId) })),
|
|
48
|
+
resumeHook: (token, payload) => trySdkPromise(() => sdk.resumeHook(token, payload)).pipe(mapSdkError("resumeHook"), Effect.withSpan("VercelWorkflows.resumeHook")),
|
|
49
|
+
cancel: (runId) => getRun(runId).pipe(Effect.flatMap((run) => run.cancel), Effect.withSpan("VercelWorkflows.cancel", { attributes: spanAttributes(runId) }))
|
|
50
|
+
};
|
|
51
|
+
}) }) {
|
|
52
|
+
static layer = Layer.effect(this, this.make).pipe(Layer.provide(VercelWorkflowsSdkLive));
|
|
53
|
+
static layerFromSdk = Layer.effect(this, this.make);
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
export { VercelWorkflows, VercelWorkflowsError, VercelWorkflowsSdk };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=effect.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect.mjs","names":["workflowStart","workflowGetRun","workflowResumeHook"],"sources":["../src/effect.ts"],"sourcesContent":["import { Context, Data, Effect, Layer } from 'effect'\nimport {\n getRun as workflowGetRun,\n resumeHook as workflowResumeHook,\n start as workflowStart,\n type WorkflowReadableStream,\n type WorkflowReadableStreamOptions,\n type WorkflowRun\n} from 'workflow/api'\n\nexport type VercelWorkflowsOperation =\n | 'start'\n | 'getRun'\n | 'getReadable'\n | 'tailIndex'\n | 'resumeHook'\n | 'cancel'\n | 'status'\n | 'returnValue'\n\nexport class VercelWorkflowsError extends Data.TaggedError('VercelWorkflowsError')<{\n readonly operation: VercelWorkflowsOperation\n readonly message: string\n readonly cause: unknown\n}> {}\n\nexport type VercelWorkflowFunction<TArgs extends unknown[], TResult> = (\n ...args: TArgs\n) => Promise<TResult>\n\nexport type VercelWorkflowReadableOptions = WorkflowReadableStreamOptions\n\nexport type VercelWorkflowReadableStream<Chunk> = WorkflowReadableStream<Chunk>\n\nexport type VercelWorkflowRunStatus = WorkflowRun['status']\n\nexport type VercelWorkflowsSdkRun<TResult> = {\n readonly runId: string\n readonly getReadable: <Chunk>(\n options?: VercelWorkflowReadableOptions\n ) => VercelWorkflowReadableStream<Chunk>\n readonly cancel: () => Promise<void>\n readonly status: Promise<VercelWorkflowRunStatus>\n readonly returnValue: Promise<TResult>\n}\n\nexport type VercelWorkflowsSdkClient = {\n readonly start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => Promise<VercelWorkflowsSdkRun<TResult>>\n readonly getRun: <TResult>(runId: string) => VercelWorkflowsSdkRun<TResult>\n readonly resumeHook: (token: string, payload: unknown) => Promise<void>\n}\n\nexport type VercelWorkflowRun<TResult> = {\n readonly runId: string\n readonly getReadable: <Chunk>(\n options?: VercelWorkflowReadableOptions\n ) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>\n readonly cancel: Effect.Effect<void, VercelWorkflowsError>\n readonly status: Effect.Effect<VercelWorkflowRunStatus, VercelWorkflowsError>\n readonly returnValue: Effect.Effect<TResult, VercelWorkflowsError>\n}\n\nexport type VercelWorkflowsClient = {\n readonly start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>\n readonly getRun: <TResult>(\n runId: string\n ) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>\n readonly getReadable: <Chunk>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>\n readonly tailIndex: (runId: string) => Effect.Effect<number, VercelWorkflowsError>\n readonly resumeHook: (\n token: string,\n payload: unknown\n ) => Effect.Effect<void, VercelWorkflowsError>\n readonly cancel: (runId: string) => Effect.Effect<void, VercelWorkflowsError>\n}\n\nexport class VercelWorkflowsSdk extends Context.Service<\n VercelWorkflowsSdk,\n VercelWorkflowsSdkClient\n>()('@yolk-sdk/vercel-workflows/VercelWorkflowsSdk') {}\n\nconst workflowsError = (operation: VercelWorkflowsOperation, cause: unknown) =>\n new VercelWorkflowsError({\n operation,\n message: `Vercel Workflow ${operation} failed`,\n cause\n })\n\nconst spanAttributes = (runId: string) => ({ 'workflow.run.id': runId })\n\nconst trySdkPromise = <A>(body: () => Promise<A>) =>\n Effect.tryPromise({\n try: body,\n catch: error => error\n })\n\nconst mapSdkError =\n (operation: VercelWorkflowsOperation) =>\n <A>(effect: Effect.Effect<A, unknown>) =>\n effect.pipe(Effect.mapError(cause => workflowsError(operation, cause)))\n\nconst makeWorkflowRun = <TResult>(\n run: VercelWorkflowsSdkRun<TResult>\n): VercelWorkflowRun<TResult> => ({\n runId: run.runId,\n getReadable: <Chunk>(options?: VercelWorkflowReadableOptions) =>\n Effect.try({\n try: () => run.getReadable<Chunk>(options),\n catch: cause => workflowsError('getReadable', cause)\n }).pipe(\n Effect.withSpan('VercelWorkflows.run.getReadable', {\n attributes: spanAttributes(run.runId)\n })\n ),\n cancel: trySdkPromise(() => run.cancel()).pipe(\n mapSdkError('cancel'),\n Effect.withSpan('VercelWorkflows.run.cancel', {\n attributes: spanAttributes(run.runId)\n })\n ),\n status: trySdkPromise(() => run.status).pipe(\n mapSdkError('status'),\n Effect.withSpan('VercelWorkflows.run.status', {\n attributes: spanAttributes(run.runId)\n })\n ),\n returnValue: trySdkPromise(() => run.returnValue).pipe(\n mapSdkError('returnValue'),\n Effect.withSpan('VercelWorkflows.run.returnValue', {\n attributes: spanAttributes(run.runId)\n })\n )\n})\n\nconst VercelWorkflowsSdkLive = Layer.succeed(VercelWorkflowsSdk, {\n start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => workflowStart(workflow, args),\n getRun: <TResult>(runId: string) => workflowGetRun<TResult>(runId),\n resumeHook: async (token: string, payload: unknown) => {\n await workflowResumeHook<unknown>(token, payload)\n }\n})\n\nexport class VercelWorkflows extends Context.Service<VercelWorkflows, VercelWorkflowsClient>()(\n '@yolk-sdk/vercel-workflows/VercelWorkflows',\n {\n make: Effect.gen(function* () {\n const sdk = yield* VercelWorkflowsSdk\n\n const getRun = <TResult>(\n runId: string\n ): Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError> =>\n Effect.try({\n try: () => sdk.getRun<TResult>(runId),\n catch: error => error\n }).pipe(\n mapSdkError('getRun'),\n Effect.map(makeWorkflowRun),\n Effect.withSpan('VercelWorkflows.getRun', {\n attributes: spanAttributes(runId)\n })\n )\n\n return {\n start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ): Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError> =>\n trySdkPromise(() => sdk.start(workflow, args)).pipe(\n mapSdkError('start'),\n Effect.map(makeWorkflowRun),\n Effect.withSpan('VercelWorkflows.start')\n ),\n\n getRun,\n\n getReadable: <Chunk>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ): Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.getReadable<Chunk>(options)),\n Effect.withSpan('VercelWorkflows.getReadable', {\n attributes: spanAttributes(runId)\n })\n ),\n\n tailIndex: (runId: string): Effect.Effect<number, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.getReadable<unknown>({ startIndex: -1 })),\n Effect.flatMap(readable =>\n Effect.tryPromise({\n try: () => readable.getTailIndex(),\n catch: cause => workflowsError('tailIndex', cause)\n })\n ),\n Effect.withSpan('VercelWorkflows.tailIndex', {\n attributes: spanAttributes(runId)\n })\n ),\n\n resumeHook: (token: string, payload: unknown): Effect.Effect<void, VercelWorkflowsError> =>\n trySdkPromise(() => sdk.resumeHook(token, payload)).pipe(\n mapSdkError('resumeHook'),\n Effect.withSpan('VercelWorkflows.resumeHook')\n ),\n\n cancel: (runId: string): Effect.Effect<void, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.cancel),\n Effect.withSpan('VercelWorkflows.cancel', {\n attributes: spanAttributes(runId)\n })\n )\n }\n })\n }\n) {\n static layer = Layer.effect(this, this.make).pipe(Layer.provide(VercelWorkflowsSdkLive))\n static layerFromSdk = Layer.effect(this, this.make)\n}\n"],"mappings":";;;AAoBA,IAAa,uBAAb,cAA0C,KAAK,YAAY,sBAAsB,EAI9E,CAAC;AA6DJ,IAAa,qBAAb,cAAwC,QAAQ,QAG9C,EAAE,+CAA+C,EAAE,CAAC;AAEtD,MAAM,kBAAkB,WAAqC,UAC3D,IAAI,qBAAqB;CACvB;CACA,SAAS,mBAAmB,UAAU;CACtC;AACF,CAAC;AAEH,MAAM,kBAAkB,WAAmB,EAAE,mBAAmB,MAAM;AAEtE,MAAM,iBAAoB,SACxB,OAAO,WAAW;CAChB,KAAK;CACL,QAAO,UAAS;AAClB,CAAC;AAEH,MAAM,eACH,eACG,WACF,OAAO,KAAK,OAAO,UAAS,UAAS,eAAe,WAAW,KAAK,CAAC,CAAC;AAE1E,MAAM,mBACJ,SACgC;CAChC,OAAO,IAAI;CACX,cAAqB,YACnB,OAAO,IAAI;EACT,WAAW,IAAI,YAAmB,OAAO;EACzC,QAAO,UAAS,eAAe,eAAe,KAAK;CACrD,CAAC,EAAE,KACD,OAAO,SAAS,mCAAmC,EACjD,YAAY,eAAe,IAAI,KAAK,EACtC,CAAC,CACH;CACF,QAAQ,oBAAoB,IAAI,OAAO,CAAC,EAAE,KACxC,YAAY,QAAQ,GACpB,OAAO,SAAS,8BAA8B,EAC5C,YAAY,eAAe,IAAI,KAAK,EACtC,CAAC,CACH;CACA,QAAQ,oBAAoB,IAAI,MAAM,EAAE,KACtC,YAAY,QAAQ,GACpB,OAAO,SAAS,8BAA8B,EAC5C,YAAY,eAAe,IAAI,KAAK,EACtC,CAAC,CACH;CACA,aAAa,oBAAoB,IAAI,WAAW,EAAE,KAChD,YAAY,aAAa,GACzB,OAAO,SAAS,mCAAmC,EACjD,YAAY,eAAe,IAAI,KAAK,EACtC,CAAC,CACH;AACF;AAEA,MAAM,yBAAyB,MAAM,QAAQ,oBAAoB;CAC/D,QACE,UACA,SACGA,MAAc,UAAU,IAAI;CACjC,SAAkB,UAAkBC,OAAwB,KAAK;CACjE,YAAY,OAAO,OAAe,YAAqB;EACrD,MAAMC,WAA4B,OAAO,OAAO;CAClD;AACF,CAAC;AAED,IAAa,kBAAb,cAAqC,QAAQ,QAAgD,EAC3F,8CACA,EACE,MAAM,OAAO,IAAI,aAAa;CAC5B,MAAM,MAAM,OAAO;CAEnB,MAAM,UACJ,UAEA,OAAO,IAAI;EACT,WAAW,IAAI,OAAgB,KAAK;EACpC,QAAO,UAAS;CAClB,CAAC,EAAE,KACD,YAAY,QAAQ,GACpB,OAAO,IAAI,eAAe,GAC1B,OAAO,SAAS,0BAA0B,EACxC,YAAY,eAAe,KAAK,EAClC,CAAC,CACH;CAEF,OAAO;EACL,QACE,UACA,SAEA,oBAAoB,IAAI,MAAM,UAAU,IAAI,CAAC,EAAE,KAC7C,YAAY,OAAO,GACnB,OAAO,IAAI,eAAe,GAC1B,OAAO,SAAS,uBAAuB,CACzC;EAEF;EAEA,cACE,OACA,YAEA,OAAgB,KAAK,EAAE,KACrB,OAAO,SAAQ,QAAO,IAAI,YAAmB,OAAO,CAAC,GACrD,OAAO,SAAS,+BAA+B,EAC7C,YAAY,eAAe,KAAK,EAClC,CAAC,CACH;EAEF,YAAY,UACV,OAAgB,KAAK,EAAE,KACrB,OAAO,SAAQ,QAAO,IAAI,YAAqB,EAAE,YAAY,GAAG,CAAC,CAAC,GAClE,OAAO,SAAQ,aACb,OAAO,WAAW;GAChB,WAAW,SAAS,aAAa;GACjC,QAAO,UAAS,eAAe,aAAa,KAAK;EACnD,CAAC,CACH,GACA,OAAO,SAAS,6BAA6B,EAC3C,YAAY,eAAe,KAAK,EAClC,CAAC,CACH;EAEF,aAAa,OAAe,YAC1B,oBAAoB,IAAI,WAAW,OAAO,OAAO,CAAC,EAAE,KAClD,YAAY,YAAY,GACxB,OAAO,SAAS,4BAA4B,CAC9C;EAEF,SAAS,UACP,OAAgB,KAAK,EAAE,KACrB,OAAO,SAAQ,QAAO,IAAI,MAAM,GAChC,OAAO,SAAS,0BAA0B,EACxC,YAAY,eAAe,KAAK,EAClC,CAAC,CACH;CACJ;AACF,CAAC,EACH,CACF,EAAE;CACA,OAAO,QAAQ,MAAM,OAAO,MAAM,KAAK,IAAI,EAAE,KAAK,MAAM,QAAQ,sBAAsB,CAAC;CACvF,OAAO,eAAe,MAAM,OAAO,MAAM,KAAK,IAAI;AACpD"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SerializableWorkflowState, VercelAgentWorkflowAwaitingInput, VercelAgentWorkflowInput, VercelAgentWorkflowLoopConfig, VercelAgentWorkflowModelStepInput, VercelAgentWorkflowModelStepResult, VercelAgentWorkflowRunResult, VercelAgentWorkflowStepRetryPolicy, VercelAgentWorkflowToolBatchStepInput, VercelAgentWorkflowToolBatchStepResult, WorkflowStepResult, defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep } from "./workflow-loop.mjs";
|
|
2
|
+
import { CommitThenWriteTerminalEventInput, CommitThenWriteTerminalEventResult, DurableAgentEvent, DurableAgentEventIdInput, DurableAgentEventSequencerState, SequenceDurableAgentEventInput, SequencedDurableAgentEvent, WriteDurableAgentEventInput, commitThenWriteTerminalEvent, durableAgentEventId, makeDurableAgentEventSequencerState, sequenceDurableAgentEvent, writeDurableAgentEvent } from "./workflow-events.mjs";
|
|
3
|
+
import { awaitWorkflowChild, orchestrateWorkflowToolBatch } from "./workflow-children.mjs";
|
|
4
|
+
export { type CommitThenWriteTerminalEventInput, type CommitThenWriteTerminalEventResult, type DurableAgentEvent, type DurableAgentEventIdInput, type DurableAgentEventSequencerState, type SequenceDurableAgentEventInput, type SequencedDurableAgentEvent, type SerializableWorkflowState, type VercelAgentWorkflowAwaitingInput, type VercelAgentWorkflowInput, type VercelAgentWorkflowLoopConfig, type VercelAgentWorkflowModelStepInput, type VercelAgentWorkflowModelStepResult, VercelAgentWorkflowRunResult, type VercelAgentWorkflowStepRetryPolicy, type VercelAgentWorkflowToolBatchStepInput, type VercelAgentWorkflowToolBatchStepResult, WorkflowStepResult, type WriteDurableAgentEventInput, awaitWorkflowChild, commitThenWriteTerminalEvent, defaultMaxWorkflowTurns, durableAgentEventId, makeDurableAgentEventSequencerState, noWorkflowStepRetry, orchestrateWorkflowToolBatch, retryWorkflowStep, runVercelAgentWorkflow, sequenceDurableAgentEvent, settleWorkflowStep, writeDurableAgentEvent };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { VercelAgentWorkflowRunResult, WorkflowStepResult, defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep } from "./workflow-loop.mjs";
|
|
2
|
+
import { commitThenWriteTerminalEvent, durableAgentEventId, makeDurableAgentEventSequencerState, sequenceDurableAgentEvent, writeDurableAgentEvent } from "./workflow-events.mjs";
|
|
3
|
+
import { awaitWorkflowChild, orchestrateWorkflowToolBatch } from "./workflow-children.mjs";
|
|
4
|
+
export { VercelAgentWorkflowRunResult, WorkflowStepResult, awaitWorkflowChild, commitThenWriteTerminalEvent, defaultMaxWorkflowTurns, durableAgentEventId, makeDurableAgentEventSequencerState, noWorkflowStepRetry, orchestrateWorkflowToolBatch, retryWorkflowStep, runVercelAgentWorkflow, sequenceDurableAgentEvent, settleWorkflowStep, writeDurableAgentEvent };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { VercelWorkflowReadableOptions, VercelWorkflowReadableStream, VercelWorkflowRunStatus, VercelWorkflowsSdkClient } from "../effect.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/testing/index.d.ts
|
|
4
|
+
type TestWorkflowRunStatus = VercelWorkflowRunStatus;
|
|
5
|
+
declare class TestWorkflowStreamConflictError extends Error {
|
|
6
|
+
readonly name = "EntityConflictError";
|
|
7
|
+
readonly status = 409;
|
|
8
|
+
constructor(runId: string);
|
|
9
|
+
}
|
|
10
|
+
declare class TestWorkflowRunConflictError extends Error {
|
|
11
|
+
readonly name = "EntityConflictError";
|
|
12
|
+
readonly status = 409;
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
declare class TestWorkflowHookNotFoundError extends Error {
|
|
16
|
+
readonly name = "HookNotFoundError";
|
|
17
|
+
constructor(token: string);
|
|
18
|
+
}
|
|
19
|
+
declare class TestWorkflowHookConflictError extends Error {
|
|
20
|
+
readonly name = "HookConflictError";
|
|
21
|
+
constructor(token: string);
|
|
22
|
+
}
|
|
23
|
+
declare class TestWorkflowRunNotFoundError extends Error {
|
|
24
|
+
readonly name = "WorkflowRunNotFoundError";
|
|
25
|
+
constructor(runId: string);
|
|
26
|
+
}
|
|
27
|
+
declare class TestWorkflowRunCancelledError extends Error {
|
|
28
|
+
readonly name = "WorkflowRunCancelledError";
|
|
29
|
+
constructor(runId: string);
|
|
30
|
+
}
|
|
31
|
+
type TestWorkflowMetadata = {
|
|
32
|
+
readonly workflowRunId: string;
|
|
33
|
+
};
|
|
34
|
+
type TestWorkflowStepMetadata = {
|
|
35
|
+
readonly attempt: number;
|
|
36
|
+
readonly stepId: string;
|
|
37
|
+
readonly stepName: string;
|
|
38
|
+
};
|
|
39
|
+
type TestWorkflowStepFunction<TArgs extends ReadonlyArray<unknown>, TResult> = ((...args: TArgs) => Promise<TResult>) & {
|
|
40
|
+
readonly maxRetries?: number;
|
|
41
|
+
};
|
|
42
|
+
declare const defaultTestWorkflowStepMaxRetries = 3;
|
|
43
|
+
type TestWorkflowRunInspection = {
|
|
44
|
+
readonly runId: string;
|
|
45
|
+
readonly status: TestWorkflowRunStatus;
|
|
46
|
+
readonly chunks: ReadonlyArray<unknown>;
|
|
47
|
+
readonly streamClosed: boolean;
|
|
48
|
+
readonly streamCloseCount: number;
|
|
49
|
+
readonly writeAfterCloseAttempts: number;
|
|
50
|
+
readonly stepAttempts: ReadonlyMap<string, number>;
|
|
51
|
+
readonly runError: unknown;
|
|
52
|
+
};
|
|
53
|
+
type TestWorkflowHook<T> = Promise<T> & {
|
|
54
|
+
readonly [Symbol.dispose]: () => void;
|
|
55
|
+
};
|
|
56
|
+
type TestWorkflowWorldStartResult = {
|
|
57
|
+
readonly runId: string;
|
|
58
|
+
};
|
|
59
|
+
declare class TestWorkflowWorld {
|
|
60
|
+
private readonly runs;
|
|
61
|
+
private runSequence;
|
|
62
|
+
start<TArgs extends ReadonlyArray<unknown>, TResult>(workflowFn: (...args: TArgs) => Promise<TResult>, args: TArgs): TestWorkflowWorldStartResult;
|
|
63
|
+
settled(runId: string): Promise<void>;
|
|
64
|
+
inspect(runId: string): TestWorkflowRunInspection;
|
|
65
|
+
status(runId: string): TestWorkflowRunStatus;
|
|
66
|
+
cancel(runId: string): Promise<void>;
|
|
67
|
+
resumeHook(token: string, payload: unknown): Promise<void>;
|
|
68
|
+
hasResumedHook(token: string): boolean;
|
|
69
|
+
runStep<TArgs extends ReadonlyArray<unknown>, TResult>(stepFn: TestWorkflowStepFunction<TArgs, TResult>, args: TArgs, stepName?: string): Promise<TResult>;
|
|
70
|
+
step<TArgs extends ReadonlyArray<unknown>, TResult>(stepFn: TestWorkflowStepFunction<TArgs, TResult>, stepName?: string): (...args: TArgs) => Promise<TResult>;
|
|
71
|
+
static getWorkflowMetadata(): TestWorkflowMetadata;
|
|
72
|
+
static getStepMetadata(): TestWorkflowStepMetadata;
|
|
73
|
+
static getWritable<T>(): WritableStream<T>;
|
|
74
|
+
static createHook<T>(input: {
|
|
75
|
+
readonly token: string;
|
|
76
|
+
}): TestWorkflowHook<T>;
|
|
77
|
+
private makeWritable;
|
|
78
|
+
private makeHook;
|
|
79
|
+
getReadable<T>(runId: string, options?: VercelWorkflowReadableOptions): VercelWorkflowReadableStream<T>;
|
|
80
|
+
private streamEnded;
|
|
81
|
+
private notifyChange;
|
|
82
|
+
private nextChange;
|
|
83
|
+
private requireRun;
|
|
84
|
+
private sdkRun;
|
|
85
|
+
readonly sdk: VercelWorkflowsSdkClient;
|
|
86
|
+
}
|
|
87
|
+
declare const testWorkflowModule: {
|
|
88
|
+
getWorkflowMetadata: () => TestWorkflowMetadata;
|
|
89
|
+
getStepMetadata: () => TestWorkflowStepMetadata;
|
|
90
|
+
getWritable: <T>() => WritableStream<T>;
|
|
91
|
+
createHook: <T>(input: {
|
|
92
|
+
readonly token: string;
|
|
93
|
+
}) => TestWorkflowHook<T>;
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { TestWorkflowHook, TestWorkflowHookConflictError, TestWorkflowHookNotFoundError, TestWorkflowMetadata, TestWorkflowRunCancelledError, TestWorkflowRunConflictError, TestWorkflowRunInspection, TestWorkflowRunNotFoundError, TestWorkflowRunStatus, TestWorkflowStepFunction, TestWorkflowStepMetadata, TestWorkflowStreamConflictError, TestWorkflowWorld, defaultTestWorkflowStepMaxRetries, testWorkflowModule };
|
|
97
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/testing/index.ts"],"mappings":";;;KAsBY,qBAAA,GAAwB,uBAAuB;AAAA,cAK9C,+BAAA,SAAwC,KAAK;EAAA,SACtC,IAAA;EAAA,SACT,MAAA;cAEG,KAAA;AAAA;AAAA,cAKD,4BAAA,SAAqC,KAAK;EAAA,SACnC,IAAA;EAAA,SACT,MAAA;cAEG,OAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,4BAAA,SAAqC,KAAK;EAAA,SACnC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,KAKF,oBAAA;EAAA,SAAkC,aAAa;AAAA;AAAA,KAE/C,wBAAA;EAAA,SACD,OAAA;EAAA,SACA,MAAA;EAAA,SACA,QAAA;AAAA;AAAA,KAGC,wBAAA,eAAuC,aAAA,2BAC9C,IAAA,EAAM,KAAA,KACN,OAAA,CAAQ,OAAA;EAAA,SAAuB,UAAA;AAAA;AAAA,cAGvB,iCAAA;AAAA,KA0CD,yBAAA;EAAA,SACD,KAAA;EAAA,SACA,MAAA,EAAQ,qBAAA;EAAA,SACR,MAAA,EAAQ,aAAA;EAAA,SACR,YAAA;EAAA,SACA,gBAAA;EAAA,SACA,uBAAA;EAAA,SACA,YAAA,EAAc,WAAA;EAAA,SACd,QAAA;AAAA;AAAA,KAGC,gBAAA,MAAsB,OAAA,CAAQ,CAAA;EAAA,UAAiB,MAAA,CAAO,OAAA;AAAA;AAAA,KAE7D,4BAAA;EAAA,SAA0C,KAAK;AAAA;AAAA,cAEvC,iBAAA;EAAA,iBACM,IAAA;EAAA,QACT,WAAA;EAER,KAAA,eAAoB,aAAA,mBAAA,CAClB,UAAA,MAAgB,IAAA,EAAM,KAAA,KAAU,OAAA,CAAQ,OAAA,GACxC,IAAA,EAAM,KAAA,GACL,4BAAA;EAmDG,OAAA,CAAQ,KAAA,WAAgB,OAAA;EAI9B,OAAA,CAAQ,KAAA,WAAgB,yBAAA;EAexB,MAAA,CAAO,KAAA,WAAgB,qBAAA;EAIjB,MAAA,CAAO,KAAA,WAAgB,OAAA;EAmBvB,UAAA,CAAW,KAAA,UAAe,OAAA,YAAmB,OAAA;EAoBnD,cAAA,CAAe,KAAA;EAaT,OAAA,eAAsB,aAAA,mBAAA,CAC1B,MAAA,EAAQ,wBAAA,CAAyB,KAAA,EAAO,OAAA,GACxC,IAAA,EAAM,KAAA,EACN,QAAA,YACC,OAAA,CAAQ,OAAA;EA2BX,IAAA,eAAmB,aAAA,mBAAA,CACjB,MAAA,EAAQ,wBAAA,CAAyB,KAAA,EAAO,OAAA,GACxC,QAAA,gBACK,IAAA,EAAM,KAAA,KAAU,OAAA,CAAQ,OAAA;EAAA,OAMxB,mBAAA,CAAA,GAAuB,oBAAA;EAAA,OAIvB,eAAA,CAAA,GAAmB,wBAAA;EAAA,OAUnB,WAAA,GAAA,CAAA,GAAkB,cAAA,CAAe,CAAA;EAAA,OAMjC,UAAA,GAAA,CAAc,KAAA;IAAA,SAAkB,KAAA;EAAA,IAAkB,gBAAA,CAAiB,CAAA;EAAA,QAQlE,YAAA;EAAA,QAwBA,QAAA;EAqBR,WAAA,GAAA,CACE,KAAA,UACA,OAAA,GAAU,6BAAA,GACT,4BAAA,CAA6B,CAAA;EAAA,QAiCxB,WAAA;EAAA,QAIA,YAAA;EAAA,QAOA,UAAA;EAAA,QAMA,UAAA;EAAA,QAUA,MAAA;EAAA,SA2BC,GAAA,EAAK,wBAAA;AAAA;AAAA,cAcH,kBAAA;;;wBAGI,cAAA,CAAA,CAAA;kBACD,KAAA;IAAA,SAAoB,KAAA;EAAA,MAAe,gBAAA,CAAA,CAAA;AAAA"}
|