workflow 5.0.0-beta.35 → 5.0.0-beta.37
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/dist/nest-builder.d.ts +2 -0
- package/dist/nest-builder.d.ts.map +1 -0
- package/dist/nest-builder.js +2 -0
- package/dist/nest-vercel-builder.d.ts +2 -0
- package/dist/nest-vercel-builder.d.ts.map +1 -0
- package/dist/nest-vercel-builder.js +2 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/docs/ai/index.mdx +1 -1
- package/docs/api-reference/workflow/create-hook.mdx +43 -2
- package/docs/api-reference/workflow/define-hook.mdx +26 -24
- package/docs/api-reference/workflow/fatal-error.mdx +29 -7
- package/docs/api-reference/workflow/fetch.mdx +3 -4
- package/docs/api-reference/workflow/set-attributes.mdx +0 -4
- package/docs/api-reference/workflow/sleep.mdx +1 -1
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +2 -0
- package/docs/api-reference/workflow-api/resume-hook.mdx +2 -0
- package/docs/api-reference/workflow-api/resume-webhook.mdx +6 -4
- package/docs/api-reference/workflow-api/start.mdx +1 -1
- package/docs/api-reference/workflow-globals.mdx +4 -1
- package/docs/api-reference/workflow-nest/nest-local-builder.mdx +1 -1
- package/docs/api-reference/workflow-runtime/health-check.mdx +4 -4
- package/docs/api-reference/workflow-runtime/world/queue.mdx +4 -4
- package/docs/comparisons/index.mdx +66 -0
- package/docs/comparisons/meta.json +11 -0
- package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
- package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
- package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
- package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
- package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
- package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +103 -0
- package/docs/configuration/build-and-diagnostics.mdx +19 -0
- package/docs/configuration/runtime-tuning.mdx +30 -6
- package/docs/configuration/worlds.mdx +22 -8
- package/docs/cookbook/advanced/publishing-libraries.mdx +17 -13
- package/docs/cookbook/common-patterns/idempotency.mdx +1 -1
- package/docs/{deploying/index.mdx → deploying.mdx} +6 -8
- package/docs/foundations/hooks.mdx +1 -1
- package/docs/foundations/idempotency.mdx +16 -9
- package/docs/getting-started/astro.mdx +2 -2
- package/docs/getting-started/express.mdx +2 -2
- package/docs/getting-started/fastify.mdx +2 -2
- package/docs/getting-started/hono.mdx +2 -2
- package/docs/getting-started/index.mdx +3 -3
- package/docs/getting-started/meta.json +2 -1
- package/docs/getting-started/nestjs.mdx +64 -3
- package/docs/getting-started/next.mdx +3 -3
- package/docs/getting-started/nitro.mdx +2 -2
- package/docs/getting-started/nuxt.mdx +2 -2
- package/docs/getting-started/sveltekit.mdx +2 -2
- package/docs/getting-started/tanstack-start.mdx +1 -1
- package/docs/getting-started/vite.mdx +2 -2
- package/docs/how-it-works/cancellation.mdx +2 -2
- package/docs/how-it-works/code-transform.mdx +19 -15
- package/docs/how-it-works/encryption.mdx +3 -3
- package/docs/how-it-works/event-sourcing.mdx +6 -6
- package/docs/how-it-works/framework-integrations.mdx +96 -342
- package/docs/meta.json +1 -1
- package/package.json +14 -12
- package/docs/deploying/building-a-world.mdx +0 -251
- package/docs/deploying/meta.json +0 -4
- package/docs/deploying/world/local-world.mdx +0 -105
- package/docs/deploying/world/meta.json +0 -4
- package/docs/deploying/world/postgres-world.mdx +0 -288
- package/docs/deploying/world/vercel-world.mdx +0 -270
- package/docs/migration-guides/index.mdx +0 -34
- package/docs/migration-guides/meta.json +0 -9
- package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -362
- package/docs/migration-guides/migrating-from-inngest.mdx +0 -308
- package/docs/migration-guides/migrating-from-temporal.mdx +0 -317
- package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -332
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nest-builder.d.ts","sourceRoot":"","sources":["../src/nest-builder.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export * from '@workflow/nest/builder';
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmVzdC1idWlsZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL25lc3QtYnVpbGRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHdCQUF3QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnQHdvcmtmbG93L25lc3QvYnVpbGRlcic7XG4iXX0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nest-vercel-builder.d.ts","sourceRoot":"","sources":["../src/nest-vercel-builder.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export * from '@workflow/nest/vercel-builder';
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmVzdC12ZXJjZWwtYnVpbGRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9uZXN0LXZlcmNlbC1idWlsZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsK0JBQStCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICdAd29ya2Zsb3cvbmVzdC92ZXJjZWwtYnVpbGRlcic7XG4iXX0=
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import '@workflow/core/runtime/world-init';
|
|
2
|
-
export { createWorld, getWorld, getWorldHandlers, type
|
|
2
|
+
export { createWorld, getWorld, getWorldHandlers, type HealthCheckOptions, type HealthCheckResult, healthCheck, setWorld, workflowEntrypoint, } from '@workflow/core/runtime';
|
|
3
3
|
//# sourceMappingURL=runtime.d.ts.map
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,mCAAmC,CAAC;AAE3C,OAAO,EACL,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,KAAK,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,mCAAmC,CAAC;AAE3C,OAAO,EACL,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,WAAW,EACX,QAAQ,EACR,kBAAkB,GACnB,MAAM,wBAAwB,CAAC"}
|
package/dist/runtime.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
// through stream helpers, so register getWorld before exporting runtime APIs.
|
|
3
3
|
import '@workflow/core/runtime/world-init';
|
|
4
4
|
export { createWorld, getWorld, getWorldHandlers, healthCheck, setWorld, workflowEntrypoint, } from '@workflow/core/runtime';
|
|
5
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVudGltZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ydW50aW1lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDZFQUE2RTtBQUM3RSw4RUFBOEU7QUFDOUUsT0FBTyxtQ0FBbUMsQ0FBQztBQUUzQyxPQUFPLEVBQ0wsV0FBVyxFQUNYLFFBQVEsRUFDUixnQkFBZ0IsRUFHaEIsV0FBVyxFQUNYLFFBQVEsRUFDUixrQkFBa0IsR0FDbkIsTUFBTSx3QkFBd0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEhvc3Qtc2lkZSBzaWRlIGVmZmVjdDogcnVudGltZS1vbmx5IGltcG9ydHMgY2FuIHN0aWxsIHJlYWNoIGdldFdvcmxkTGF6eSgpXG4vLyB0aHJvdWdoIHN0cmVhbSBoZWxwZXJzLCBzbyByZWdpc3RlciBnZXRXb3JsZCBiZWZvcmUgZXhwb3J0aW5nIHJ1bnRpbWUgQVBJcy5cbmltcG9ydCAnQHdvcmtmbG93L2NvcmUvcnVudGltZS93b3JsZC1pbml0JztcblxuZXhwb3J0IHtcbiAgY3JlYXRlV29ybGQsXG4gIGdldFdvcmxkLFxuICBnZXRXb3JsZEhhbmRsZXJzLFxuICB0eXBlIEhlYWx0aENoZWNrT3B0aW9ucyxcbiAgdHlwZSBIZWFsdGhDaGVja1Jlc3VsdCxcbiAgaGVhbHRoQ2hlY2ssXG4gIHNldFdvcmxkLFxuICB3b3JrZmxvd0VudHJ5cG9pbnQsXG59IGZyb20gJ0B3b3JrZmxvdy9jb3JlL3J1bnRpbWUnO1xuIl19
|
package/docs/ai/index.mdx
CHANGED
|
@@ -382,7 +382,7 @@ export async function checkBaggageAllowance(
|
|
|
382
382
|
|
|
383
383
|
With `"use step"`:
|
|
384
384
|
|
|
385
|
-
- The tool execution runs
|
|
385
|
+
- The tool execution runs as a durable step with full Node.js access. The combined runtime may execute it inline in the flow request or resume it through the workflow queue, and can scale queued work with your workload.
|
|
386
386
|
- Failed tool calls are automatically retried (up to 3 times by default). See [Errors and Retries](/docs/foundations/errors-and-retries) for more details.
|
|
387
387
|
- Each tool execution appears as a discrete step in observability tools. See [Observability](/docs/observability) for more details.
|
|
388
388
|
</Step>
|
|
@@ -66,7 +66,7 @@ export default Hook;`}
|
|
|
66
66
|
|
|
67
67
|
The returned `Hook` object also implements `AsyncIterable<T>`, which allows you to iterate over incoming payloads using `for await...of` syntax.
|
|
68
68
|
|
|
69
|
-
Use `hook.getConflict()` to check whether the hook token is already claimed by another
|
|
69
|
+
Use `hook.getConflict()` to check whether the hook token is already claimed by another hook, including one kept reserved after its run ends, without waiting for hook payload data. Calling `createHook()` on its own does not register the hook — registration is only committed when the workflow suspends. Awaiting `hook.getConflict()` suspends the workflow to commit the registration, then resolves with `null` once `hook_created` is recorded, or with the conflicting [`Run`](/docs/api-reference/workflow-api/get-run).
|
|
70
70
|
|
|
71
71
|
## Examples
|
|
72
72
|
|
|
@@ -143,12 +143,53 @@ async function processOrder(orderId: string) {
|
|
|
143
143
|
|
|
144
144
|
Because `createHook()` alone does not suspend the workflow, awaiting `hook.getConflict()` is what actually suspends the run and commits the hook registration. It only waits for registration — to receive payload data from a future `resumeHook()` call, await the hook itself or iterate it with `for await...of`.
|
|
145
145
|
|
|
146
|
-
On a conflict, the resolved value is a `Run` handle for the run that
|
|
146
|
+
On a conflict, the resolved value is a `Run` handle for the run that owns the token, with durable step-backed accessors. The duplicate run can decide in code how to handle it: return or log `conflict.runId`, inspect `await conflict.status`, wait on `await conflict.returnValue`, or cancel the owner with `await conflict.cancel()` and continue in the current run. See [Run idempotency](/docs/foundations/idempotency#run-idempotency) for these strategies in context.
|
|
147
147
|
|
|
148
148
|
<Callout type="info">
|
|
149
149
|
Custom hook tokens are the recommended way to coordinate active workflow runs. Use a deterministic token from your domain, such as an order ID or conversation ID, create the hook near the beginning of the workflow, and check `await hook.getConflict()` before work that depends on owning the token. See [Run idempotency](/docs/foundations/idempotency#run-idempotency).
|
|
150
150
|
</Callout>
|
|
151
151
|
|
|
152
|
+
### Keep a Token Unavailable After the Run Ends
|
|
153
|
+
|
|
154
|
+
By default, another Hook can use the token after its workflow ends. Set `experimental_minRetention` to keep the token unavailable for at least a specific time after `createHook()` runs:
|
|
155
|
+
|
|
156
|
+
```typescript lineNumbers
|
|
157
|
+
import { createHook } from "workflow";
|
|
158
|
+
|
|
159
|
+
declare function processOwnedOrder(orderId: string): Promise<void>; // @setup
|
|
160
|
+
|
|
161
|
+
export async function processOrder(orderId: string) {
|
|
162
|
+
"use workflow";
|
|
163
|
+
|
|
164
|
+
const hook = createHook({ // [!code highlight]
|
|
165
|
+
token: `order:${orderId}`, // [!code highlight]
|
|
166
|
+
experimental_minRetention: "30d", // [!code highlight]
|
|
167
|
+
}); // [!code highlight]
|
|
168
|
+
|
|
169
|
+
const conflict = await hook.getConflict();
|
|
170
|
+
if (conflict) {
|
|
171
|
+
return { status: "duplicate" as const, runId: conflict.runId };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await processOwnedOrder(orderId);
|
|
175
|
+
return { status: "processed" as const };
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`experimental_minRetention` accepts the same values as [`sleep()`](/docs/api-reference/workflow/sleep): a duration string such as `"30d"`, a number of milliseconds, or an absolute `Date`. Durations start when `createHook()` runs.
|
|
180
|
+
|
|
181
|
+
The Hook remains active until the workflow ends, even if the configured time passes first. Another Hook can use the token only after both the workflow has ended and the configured time has passed. For example, `"30d"` keeps the token unavailable for 29 more days if the workflow ends after 1 day. A workflow that runs for more than 30 days releases the token when it ends.
|
|
182
|
+
|
|
183
|
+
After the workflow ends, [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token) can still find the Hook until retention ends, but the Hook cannot be resumed.
|
|
184
|
+
|
|
185
|
+
<Callout type="warn">
|
|
186
|
+
`using` auto-disposes the Hook at scope exit, which releases the token immediately and defeats `experimental_minRetention`. Declare retained Hooks with `const` and let the runtime clean them up when the run ends.
|
|
187
|
+
</Callout>
|
|
188
|
+
|
|
189
|
+
<Callout type="warn">
|
|
190
|
+
This option is experimental. If the configured World does not support it, the workflow fails when registering the Hook. `createWebhook()` does not accept this option.
|
|
191
|
+
</Callout>
|
|
192
|
+
|
|
152
193
|
### Waiting for Multiple Payloads
|
|
153
194
|
|
|
154
195
|
You can also wait for multiple payloads by using the `for await...of` syntax.
|
|
@@ -46,24 +46,21 @@ showSections={['parameters']}
|
|
|
46
46
|
|
|
47
47
|
### Returns
|
|
48
48
|
|
|
49
|
+
`defineHook()` returns a `TypedHook<TInput, TOutput>`:
|
|
50
|
+
|
|
49
51
|
<TSDoc
|
|
50
52
|
definition={`
|
|
51
|
-
interface
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
create: (options?: HookOptions) => Hook<T>;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
* Resumes a hook by sending a payload with the defined type.
|
|
61
|
-
*/
|
|
62
|
-
resume: (token: string, payload: T) => Promise<HookEntity | null>;
|
|
53
|
+
interface TypedHook<TInput, TOutput> {
|
|
54
|
+
/** Creates the hook. Call inside a "use workflow" function. */
|
|
55
|
+
create(options?: HookOptions): Hook<TOutput>;
|
|
56
|
+
/** Resumes the hook from runtime code. Resolves to the resumed hook; throws HookNotFoundError if the token does not match an active hook. */
|
|
57
|
+
resume(token: string, payload: TInput): Promise<HookEntity>;
|
|
63
58
|
}
|
|
64
|
-
export default
|
|
59
|
+
export default TypedHook;`}
|
|
65
60
|
/>
|
|
66
61
|
|
|
62
|
+
`create()` is called inside a `"use workflow"` function to create the hook; `resume()` is called from runtime code (an API route or server action). When a `schema` is provided, `resume()` accepts the raw input type (`TInput`) and the workflow receives the validated and possibly transformed output type (`TOutput`); without a schema, `TOutput` defaults to `TInput`. `resume()` throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active hook — it does not return `null`.
|
|
63
|
+
|
|
67
64
|
## Examples
|
|
68
65
|
|
|
69
66
|
### Basic Type-Safe Hook Definition
|
|
@@ -93,24 +90,29 @@ export async function workflowWithApproval() {
|
|
|
93
90
|
|
|
94
91
|
### Resuming with Type Safety
|
|
95
92
|
|
|
96
|
-
Hooks can be resumed using the same defined hook and a token. By using the same hook, you can ensure that the payload matches the defined type when resuming a hook.
|
|
93
|
+
Hooks can be resumed using the same defined hook and a token. By using the same hook, you can ensure that the payload matches the defined type when resuming a hook. `resume()` resolves to the resumed hook and throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active hook.
|
|
97
94
|
|
|
98
95
|
```typescript lineNumbers
|
|
96
|
+
import { HookNotFoundError } from "workflow/errors";
|
|
97
|
+
|
|
99
98
|
// Use the same defined hook to resume
|
|
100
99
|
export async function POST(request: Request) {
|
|
101
100
|
const { token, approved, comment } = await request.json();
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return Response.json({
|
|
102
|
+
try {
|
|
103
|
+
// Type-safe resumption - TypeScript ensures the payload matches
|
|
104
|
+
const hook = await approvalHook.resume(token, { // [!code highlight]
|
|
105
|
+
approved, // [!code highlight]
|
|
106
|
+
comment, // [!code highlight]
|
|
107
|
+
}); // [!code highlight]
|
|
108
|
+
|
|
109
|
+
return Response.json({ success: true, runId: hook.runId });
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (HookNotFoundError.is(error)) { // [!code highlight]
|
|
112
|
+
return Response.json({ error: "Hook not found" }, { status: 404 });
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
111
115
|
}
|
|
112
|
-
|
|
113
|
-
return Response.json({ success: true, runId: result.runId });
|
|
114
116
|
}
|
|
115
117
|
```
|
|
116
118
|
|
|
@@ -29,16 +29,38 @@ async function fallibleStep() {
|
|
|
29
29
|
|
|
30
30
|
## API Signature
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### Constructor
|
|
33
|
+
|
|
34
|
+
`new FatalError(message)`
|
|
35
|
+
|
|
36
|
+
| Parameter | Type | Description |
|
|
37
|
+
| --------- | -------- | ------------------ |
|
|
38
|
+
| `message` | `string` | The error message. |
|
|
39
|
+
|
|
40
|
+
### Properties
|
|
33
41
|
|
|
34
42
|
<TSDoc
|
|
35
43
|
definition={`
|
|
36
|
-
interface
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
* The error message.
|
|
40
|
-
*/
|
|
44
|
+
interface FatalError {
|
|
45
|
+
/** The error message. */
|
|
41
46
|
message: string;
|
|
47
|
+
/** Always true. Marks the error as non-retryable. */
|
|
48
|
+
fatal: boolean;
|
|
42
49
|
}
|
|
43
|
-
export default
|
|
50
|
+
export default FatalError;`}
|
|
44
51
|
/>
|
|
52
|
+
|
|
53
|
+
### Static Methods
|
|
54
|
+
|
|
55
|
+
#### `FatalError.is(value)`
|
|
56
|
+
|
|
57
|
+
Type-safe check for `FatalError` instances. Preferred over `instanceof` because it works across module boundaries and VM contexts.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { FatalError } from "workflow"
|
|
61
|
+
declare const error: unknown; // @setup
|
|
62
|
+
|
|
63
|
+
if (FatalError.is(error)) {
|
|
64
|
+
// error is typed as FatalError
|
|
65
|
+
}
|
|
66
|
+
```
|
|
@@ -93,7 +93,7 @@ This API is provided as a convenience to easily use `fetch` in workflow, but oft
|
|
|
93
93
|
|
|
94
94
|
### Customizing Fetch Behavior
|
|
95
95
|
|
|
96
|
-
Here's an example of a custom fetch wrapper that provides more sophisticated error handling with custom retry logic:
|
|
96
|
+
Here's an example of a custom fetch wrapper that provides more sophisticated error handling with custom retry logic. Call `globalThis.fetch` inside your own `"use step"` function — calling the workflow `fetch` imported from `workflow` would nest a step inside a step:
|
|
97
97
|
|
|
98
98
|
```typescript lineNumbers
|
|
99
99
|
import { FatalError, RetryableError } from "workflow"
|
|
@@ -104,7 +104,7 @@ export async function customFetch(
|
|
|
104
104
|
) {
|
|
105
105
|
"use step"
|
|
106
106
|
|
|
107
|
-
const response = await fetch(url, init)
|
|
107
|
+
const response = await globalThis.fetch(url, init)
|
|
108
108
|
|
|
109
109
|
// Handle client errors (4xx) - don't retry
|
|
110
110
|
if (response.status >= 400 && response.status < 500) {
|
|
@@ -145,7 +145,6 @@ export async function customFetch(
|
|
|
145
145
|
|
|
146
146
|
This example demonstrates:
|
|
147
147
|
|
|
148
|
-
- Setting custom `maxRetries` to 5 retries (6 total attempts including the initial attempt).
|
|
149
148
|
- Throwing [`FatalError`](/docs/api-reference/workflow/fatal-error) for client errors (400-499) to prevent retries.
|
|
150
149
|
- Handling 429 rate limiting by reading the `Retry-After` header and using [`RetryableError`](/docs/api-reference/workflow/retryable-error).
|
|
151
|
-
- Allowing automatic retries for server errors (5xx)
|
|
150
|
+
- Allowing automatic retries for server errors (5xx) by throwing a plain `Error`.
|
|
@@ -59,7 +59,3 @@ Validation errors throw [`FatalError`](/docs/api-reference/workflow/fatal-error)
|
|
|
59
59
|
Calls from both workflow and step bodies append a native `attr_set` event, which the World materializes onto `run.attributes`. Workflow-originated events record a workflow writer; step-originated events record the originating step ID and attempt.
|
|
60
60
|
|
|
61
61
|
Native attributes require spec version 4 or later. Step-body storage errors throw from `setAttributes`; catch them inside the step if the write should be best-effort. Workflow-body writes are committed when the workflow suspends: transient storage errors are retried with the suspension, while a write the World rejects as invalid — such as exceeding the per-run attribute cap across multiple calls — fails the run with the validation error.
|
|
62
|
-
|
|
63
|
-
<Callout>
|
|
64
|
-
This function was previously exported as `experimental_setAttributes`. The old name still works as a deprecated alias — update imports to `setAttributes`.
|
|
65
|
-
</Callout>
|
|
@@ -14,7 +14,7 @@ Suspends a workflow for a specified duration or until an end date without consum
|
|
|
14
14
|
This is useful when you want to resume a workflow after some duration or date.
|
|
15
15
|
|
|
16
16
|
<Callout>
|
|
17
|
-
`sleep` is a
|
|
17
|
+
`sleep` is a built-in workflow runtime function (backed by a timer event in the event log, not a step) and should be called directly inside workflow functions.
|
|
18
18
|
</Callout>
|
|
19
19
|
|
|
20
20
|
```typescript lineNumbers
|
|
@@ -11,6 +11,8 @@ related:
|
|
|
11
11
|
|
|
12
12
|
Retrieves a hook by its unique token, returning the associated workflow run information and any metadata that was set when the hook was created. This function is useful for inspecting hook details before deciding whether to resume a workflow.
|
|
13
13
|
|
|
14
|
+
When `experimental_minRetention` is set, this function continues to return the Hook after its workflow ends until retention ends. That Hook cannot be resumed. Use `getRun(hook.runId)` to inspect the finished run.
|
|
15
|
+
|
|
14
16
|
<Callout type="warn">
|
|
15
17
|
`getHookByToken` is a runtime function that must be called from outside a workflow function.
|
|
16
18
|
</Callout>
|
|
@@ -14,6 +14,8 @@ Resumes a workflow run by sending a payload to a hook identified by its token.
|
|
|
14
14
|
|
|
15
15
|
It creates a `hook_received` event and re-triggers the workflow to continue execution.
|
|
16
16
|
|
|
17
|
+
A Hook kept by `experimental_minRetention` after its workflow ends cannot be resumed. `resumeHook()` throws `HookNotFoundError` in that case.
|
|
18
|
+
|
|
17
19
|
<Callout type="warn">
|
|
18
20
|
`resumeHook` is a runtime function that must be called from outside a workflow function.
|
|
19
21
|
</Callout>
|
|
@@ -50,11 +50,13 @@ showSections={['parameters']}
|
|
|
50
50
|
|
|
51
51
|
### Returns
|
|
52
52
|
|
|
53
|
-
Returns a `Promise<Response>` that resolves to:
|
|
53
|
+
Returns a `Promise<Response>` that resolves to one of:
|
|
54
54
|
|
|
55
|
-
- `
|
|
55
|
+
- A `202 Accepted` response when the webhook was created in the default mode (no `respondWith` option).
|
|
56
|
+
- The exact `Response` object configured via `createWebhook({ respondWith: new Response(...) })`.
|
|
57
|
+
- The workflow's manual `Response` when the webhook was created with `createWebhook({ respondWith: "manual" })` and a step calls `request.respondWith(response)`.
|
|
56
58
|
|
|
57
|
-
Throws
|
|
59
|
+
Throws [`HookNotFoundError`](/docs/api-reference/workflow-errors/hook-not-found-error) if the token does not match an active webhook.
|
|
58
60
|
|
|
59
61
|
## Usage Note
|
|
60
62
|
|
|
@@ -81,7 +83,7 @@ export async function POST(request: Request) {
|
|
|
81
83
|
|
|
82
84
|
try {
|
|
83
85
|
const response = await resumeWebhook(token, request); // [!code highlight]
|
|
84
|
-
return response; //
|
|
86
|
+
return response; // 202 Accepted, a configured static Response, or a manual workflow response
|
|
85
87
|
} catch (error) {
|
|
86
88
|
return new Response("Webhook not found", { status: 404 });
|
|
87
89
|
}
|
|
@@ -62,7 +62,7 @@ Learn more about [`WorkflowReadableStreamOptions`](/docs/api-reference/workflow-
|
|
|
62
62
|
* `attributes` seeds plaintext run metadata as part of creation and requires a World implementing spec version 4 or later. Keys that start with `$` are reserved for framework and library code; framework-level callers can pass `allowReservedAttributes: true` to seed reserved keys, with the same semantics as the [`setAttributes`](/docs/api-reference/workflow/set-attributes) option of the same name.
|
|
63
63
|
|
|
64
64
|
<Callout type="info">
|
|
65
|
-
If `start()` throws `'start' received an invalid workflow function. Ensure the Workflow
|
|
65
|
+
If `start()` throws `'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.`, the passed function was not transformed as a workflow. The two most common causes are a missing `"use workflow"` directive or missing framework integration. See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function).
|
|
66
66
|
</Callout>
|
|
67
67
|
|
|
68
68
|
## Examples
|
|
@@ -26,7 +26,7 @@ These APIs are available but are **seeded or fixed** to ensure deterministic beh
|
|
|
26
26
|
| [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) / `Date.now()` / `new Date()` | Returns a fixed timestamp that advances with the workflow's logical clock |
|
|
27
27
|
| [`crypto.getRandomValues()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues) | Seeded — produces deterministic output for a given workflow run |
|
|
28
28
|
| [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) | Seeded — produces deterministic UUIDs for a given workflow run |
|
|
29
|
-
| [`crypto.subtle.digest()`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) |
|
|
29
|
+
| [`crypto.subtle.digest()`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) | Computed synchronously via `node:crypto` (values are byte-identical to WebCrypto), so the promise settles at a deterministic point during replay |
|
|
30
30
|
|
|
31
31
|
<Callout type="info">
|
|
32
32
|
You can safely use `Math.random()`, `Date.now()`, and `crypto.randomUUID()` in workflow functions. The framework ensures these return the same values across replays.
|
|
@@ -100,3 +100,6 @@ The following are **not available** in workflow functions. Move this logic to [s
|
|
|
100
100
|
- **Global `fetch`**: Use [`import { fetch } from "workflow"`](/docs/api-reference/workflow/fetch) instead. See [fetch-in-workflow](/docs/errors/fetch-in-workflow).
|
|
101
101
|
- **Timers**: `setTimeout`, `setInterval`, `setImmediate`, and their `clear*` counterparts. Use [`sleep()`](/docs/api-reference/workflow/sleep) instead. See [timeout-in-workflow](/docs/errors/timeout-in-workflow).
|
|
102
102
|
- **`Buffer`**: Node.js-specific API. Use `Uint8Array` with `toBase64()` / `fromBase64()` / `toHex()` / `fromHex()` for binary data encoding, or `atob()` / `btoa()` for string-based base64.
|
|
103
|
+
- **`WeakRef` and `FinalizationRegistry`**: garbage-collection timing is not deterministic, so observing it would make workflow code impossible to replay. (`WeakMap` and `WeakSet` remain available — they do not expose GC state.)
|
|
104
|
+
- **`Atomics.waitAsync`**: a wall-clock timer, which cannot be replayed. Use [`sleep()`](/docs/api-reference/workflow/sleep) instead.
|
|
105
|
+
- **Async `WebAssembly` compilation** (`compile`, `instantiate`, `compileStreaming`, `instantiateStreaming`): resolves on compile-thread timing. The synchronous `new WebAssembly.Module()` and `new WebAssembly.Instance()` constructors remain available.
|
|
@@ -12,7 +12,7 @@ Builder that scans a NestJS project for workflow files and compiles them into th
|
|
|
12
12
|
## Usage
|
|
13
13
|
|
|
14
14
|
```typescript title="scripts/build-workflows.ts" lineNumbers
|
|
15
|
-
import { NestLocalBuilder } from "workflow/nest"; // [!code highlight]
|
|
15
|
+
import { NestLocalBuilder } from "workflow/nest/builder"; // [!code highlight]
|
|
16
16
|
|
|
17
17
|
const builder = new NestLocalBuilder({
|
|
18
18
|
dirs: ["src"],
|
|
@@ -13,7 +13,7 @@ Performs an end-to-end health check of a deployment's workflow infrastructure by
|
|
|
13
13
|
import { getWorld, healthCheck } from "workflow/runtime";
|
|
14
14
|
|
|
15
15
|
const world = await getWorld();
|
|
16
|
-
const result = await healthCheck(world
|
|
16
|
+
const result = await healthCheck(world); // [!code highlight]
|
|
17
17
|
|
|
18
18
|
if (!result.healthy) {
|
|
19
19
|
console.error("Workflow infrastructure unhealthy:", result.error);
|
|
@@ -27,8 +27,7 @@ if (!result.healthy) {
|
|
|
27
27
|
| Parameter | Type | Description |
|
|
28
28
|
|-----------|------|-------------|
|
|
29
29
|
| `world` | `World` | The World instance to send the health check through |
|
|
30
|
-
| `
|
|
31
|
-
| `options` | `HealthCheckOptions & { namespace?: string }` | Optional configuration |
|
|
30
|
+
| `options` | `HealthCheckOptions` | Optional configuration |
|
|
32
31
|
|
|
33
32
|
Where `HealthCheckOptions` is:
|
|
34
33
|
|
|
@@ -36,6 +35,7 @@ Where `HealthCheckOptions` is:
|
|
|
36
35
|
|--------|------|-------------|
|
|
37
36
|
| `timeout` | `number` | Milliseconds to wait for the health check response. Default: `30000`. |
|
|
38
37
|
| `deploymentId` | `string` | Deployment to target. Falls back to `process.env.VERCEL_DEPLOYMENT_ID`. |
|
|
38
|
+
| `namespace` | `string` | Queue namespace of the target deployment. Falls back to `WORKFLOW_QUEUE_NAMESPACE`. |
|
|
39
39
|
|
|
40
40
|
### Returns
|
|
41
41
|
|
|
@@ -43,7 +43,7 @@ Returns a `Promise<HealthCheckResult>`:
|
|
|
43
43
|
|
|
44
44
|
| Property | Type | Description |
|
|
45
45
|
|----------|------|-------------|
|
|
46
|
-
| `healthy` | `boolean` | Whether the endpoint processed the health check message |
|
|
46
|
+
| `healthy` | `boolean` | Whether the combined workflow endpoint processed the health check message |
|
|
47
47
|
| `error` | `string \| undefined` | Error message when the check failed |
|
|
48
48
|
| `latencyMs` | `number \| undefined` | Round-trip latency when the check succeeded |
|
|
49
49
|
| `specVersion` | `number \| undefined` | Workflow spec version of the responding deployment |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Queue
|
|
3
|
-
description: Low-level queue interface for dispatching workflow
|
|
3
|
+
description: Low-level queue interface for dispatching workflow invocations.
|
|
4
4
|
type: reference
|
|
5
5
|
summary: "Methods: getDeploymentId(), queue(), createQueueHandler(). Internal queue dispatch — normally handled by the SDK."
|
|
6
6
|
prerequisites:
|
|
@@ -17,10 +17,10 @@ keywords:
|
|
|
17
17
|
- queue dispatch
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
Queue methods live directly on the `world` object (not nested). They dispatch internal workflow
|
|
20
|
+
Queue methods live directly on the `world` object (not nested). They dispatch internal workflow invocations, including queued step work, to the queue backend.
|
|
21
21
|
|
|
22
22
|
<Callout type="warn">
|
|
23
|
-
These methods are used internally by the Workflow SDK to dispatch execution. You do not need to call them in normal operations — use [`start()`](/docs/api-reference/workflow-api/start) to trigger workflows instead. Direct queue access is only needed if you programmatically create a run via `world.events.create()` with a `run_created` event and need to kick off its initial execution, or for debugging
|
|
23
|
+
These methods are used internally by the Workflow SDK to dispatch execution. You do not need to call them in normal operations — use [`start()`](/docs/api-reference/workflow-api/start) to trigger workflows instead. Direct queue access is only needed if you programmatically create a run via `world.events.create()` with a `run_created` event and need to kick off its initial execution, or for debugging flow resumption.
|
|
24
24
|
</Callout>
|
|
25
25
|
|
|
26
26
|
## Import
|
|
@@ -46,7 +46,7 @@ const deploymentId = await world.getDeploymentId(); // [!code highlight]
|
|
|
46
46
|
|
|
47
47
|
### queue()
|
|
48
48
|
|
|
49
|
-
Dispatch a message to a named queue. The message payload is an internal SDK type (`WorkflowInvokePayload
|
|
49
|
+
Dispatch a message to a named queue. The message payload is an internal SDK type (`WorkflowInvokePayload` or `HealthCheckPayload`). Queued steps use `WorkflowInvokePayload` with `stepId` and `stepName`.
|
|
50
50
|
|
|
51
51
|
```typescript lineNumbers
|
|
52
52
|
const { messageId } = await world.queue(queueName, payload, opts); // [!code highlight]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Comparisons
|
|
3
|
+
description: How the Workflow SDK compares to other durable execution, workflow, and AI-agent frameworks — Temporal, Cloudflare Workflows, AWS Step Functions, AWS Bedrock AgentCore, Inngest, and trigger.dev.
|
|
4
|
+
type: overview
|
|
5
|
+
summary: Side-by-side comparisons of the Workflow SDK against Temporal, Cloudflare Workflows, AWS Step Functions, AWS Bedrock AgentCore, Inngest, and trigger.dev.
|
|
6
|
+
related:
|
|
7
|
+
- /docs/foundations/workflows-and-steps
|
|
8
|
+
- /docs/how-it-works/event-sourcing
|
|
9
|
+
- /worlds/vercel
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
The Workflow SDK overlaps with several categories: durable execution engines, background-job platforms, managed orchestrators, and AI-agent runtimes. This section compares it against the most common alternatives, one deep dive per tool.
|
|
13
|
+
|
|
14
|
+
## What makes the Workflow SDK different
|
|
15
|
+
|
|
16
|
+
- **It's an open-source SDK, not a hosted product.** Your workflows are plain TypeScript in your existing app. Run them on the managed [Vercel World](/worlds/vercel), or self-host on the [Postgres World](/worlds/postgres) — the [World abstraction](/worlds/building-a-world) lets you own and swap the storage, queue, and streaming layers independently.
|
|
17
|
+
- **Versioning is safe by default.** Runs are pinned to the immutable deployment that started them, so shipping new code never disturbs in-flight runs. Upgrading a run is explicit and opt-in. See [Versioning](/docs/comparisons/workflow-sdk-vs-temporal#versioning).
|
|
18
|
+
- **Realtime durable streaming is built in.** Stream partial output (LLM tokens, progress) to clients with [streams](/docs/foundations/streaming) that survive reconnects, cold starts, and replays — essential for chat and agent UIs.
|
|
19
|
+
- **First-class AI agents.** `WorkflowAgent` ships inside the [AI SDK](/docs/ai), turning an agent loop into a durable workflow with automatic step retries and human-in-the-loop pauses.
|
|
20
|
+
|
|
21
|
+
## Snapshot
|
|
22
|
+
|
|
23
|
+
<Callout type="info">
|
|
24
|
+
These comparisons are compiled from each product's public documentation and are **not** based on head-to-head benchmarks — durable engines differ enough that a single number rarely compares cleanly. Treat them as directional and verify current pricing and limits against each vendor's docs.
|
|
25
|
+
</Callout>
|
|
26
|
+
|
|
27
|
+
| Tool | Category | Durability model | Open source / self-host | Language(s) |
|
|
28
|
+
| --- | --- | --- | --- | --- |
|
|
29
|
+
| **Workflow SDK** | Durable functions SDK | Event-log + deterministic replay | ✅ Apache-2.0 — self-host (Postgres) or Vercel | TypeScript (Python beta) |
|
|
30
|
+
| [Temporal](/docs/comparisons/workflow-sdk-vs-temporal) | Durable execution platform | Event-sourced replay | ✅ MIT server — self-host or Temporal Cloud | Go, Java, TS, Python, .NET, PHP, Ruby |
|
|
31
|
+
| [Cloudflare Workflows](/docs/comparisons/workflow-sdk-vs-cloudflare-workflows) | Durable execution engine | Step-result memoization + replay | ❌ Cloudflare-only | TypeScript (Python beta) |
|
|
32
|
+
| [AWS Step Functions](/docs/comparisons/workflow-sdk-vs-aws-step-functions) | Managed state-machine orchestrator | Declarative ASL state machine | ❌ AWS-only | ASL JSON (tasks: any language) |
|
|
33
|
+
| [AWS Bedrock AgentCore](/docs/comparisons/workflow-sdk-vs-aws-agentcore) | AI-agent hosting platform | Not durable execution (ephemeral sessions) | ❌ AWS-only | Python, Node.js |
|
|
34
|
+
| [Inngest](/docs/comparisons/workflow-sdk-vs-inngest) | Durable functions / event platform | Step-result memoization | ◑ SSPL — self-host (community/best-effort) or SaaS | TypeScript (Python/Go pre-1.0) |
|
|
35
|
+
| [trigger.dev](/docs/comparisons/workflow-sdk-vs-trigger-dev) | Durable task platform | Process checkpoint/restore (CRIU) | ✅ Apache-2.0 — self-host or Cloud | TypeScript only |
|
|
36
|
+
|
|
37
|
+
## Deep dives
|
|
38
|
+
|
|
39
|
+
<Cards>
|
|
40
|
+
<Card href="/docs/comparisons/workflow-sdk-vs-temporal" title="vs Temporal">
|
|
41
|
+
The mature, language-agnostic durable-execution platform. You run the workers; Workflow SDK runs in your app.
|
|
42
|
+
</Card>
|
|
43
|
+
<Card href="/docs/comparisons/workflow-sdk-vs-cloudflare-workflows" title="vs Cloudflare Workflows">
|
|
44
|
+
A durable engine on Workers + Durable Objects. Both replay; they handle versioning and encryption very differently.
|
|
45
|
+
</Card>
|
|
46
|
+
<Card href="/docs/comparisons/workflow-sdk-vs-aws-step-functions" title="vs AWS Step Functions">
|
|
47
|
+
Declarative ASL JSON state machines vs. plain TypeScript control flow.
|
|
48
|
+
</Card>
|
|
49
|
+
<Card href="/docs/comparisons/workflow-sdk-vs-aws-agentcore" title="vs AWS Bedrock AgentCore">
|
|
50
|
+
An AI-agent hosting platform — not a durable-execution engine. Different axis.
|
|
51
|
+
</Card>
|
|
52
|
+
<Card href="/docs/comparisons/workflow-sdk-vs-inngest" title="vs Inngest">
|
|
53
|
+
Event-driven durable functions that run on your own infra over HTTP.
|
|
54
|
+
</Card>
|
|
55
|
+
<Card href="/docs/comparisons/workflow-sdk-vs-trigger-dev" title="vs trigger.dev">
|
|
56
|
+
A TypeScript task platform that achieves durability by snapshotting the process (CRIU).
|
|
57
|
+
</Card>
|
|
58
|
+
</Cards>
|
|
59
|
+
|
|
60
|
+
<Callout type="info">
|
|
61
|
+
Moving an existing system over? Each deep dive includes a concept-mapping section, and the Workflow SDK migration skill can translate code for you:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx skills add https://github.com/vercel/workflow --skill migrating-to-workflow-sdk
|
|
65
|
+
```
|
|
66
|
+
</Callout>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Workflow SDK vs AWS Bedrock AgentCore
|
|
3
|
+
description: How the Workflow SDK compares to AWS Bedrock AgentCore — a durable-execution framework versus an AI-agent hosting platform. They solve different problems.
|
|
4
|
+
type: conceptual
|
|
5
|
+
summary: AgentCore hosts and operates AI agents in isolated microVMs but is not a durable-execution engine. The Workflow SDK provides durable orchestration and resumable streaming for agents.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/foundations/workflows-and-steps
|
|
8
|
+
related:
|
|
9
|
+
- /docs/ai
|
|
10
|
+
- /docs/ai/resumable-streams
|
|
11
|
+
- /docs/foundations/streaming
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
[Amazon Bedrock AgentCore](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/what-is-bedrock-agentcore.html) is AWS's platform for **hosting and operating AI agents** — secure microVM runtime, plus building blocks for Memory, tool Gateways, and Identity. It is *not* a durable-execution engine, which makes this less a head-to-head and more a "different axis" comparison.
|
|
15
|
+
|
|
16
|
+
<Callout type="info">
|
|
17
|
+
**These solve different problems.** AgentCore answers "where do I securely run and operate an agent on AWS?" The Workflow SDK answers "how do I make a multi-step, tool-calling agent loop durable, resumable, and streamable?" AWS itself pairs AgentCore with a durable layer (Step Functions, or Temporal) for resumability — the Workflow SDK provides that durable layer natively, in your own app.
|
|
18
|
+
</Callout>
|
|
19
|
+
|
|
20
|
+
## At a glance
|
|
21
|
+
|
|
22
|
+
| | Workflow SDK | AWS Bedrock AgentCore |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| **Category** | Open-source durable-functions SDK | AI-agent hosting & operations platform |
|
|
25
|
+
| **Durable execution?** | ✅ Event-log replay; the agent loop resumes from its last checkpoint after a crash | ❌ Not built-in — sessions are ephemeral microVMs; durability is opt-in via Memory or a framework checkpointer |
|
|
26
|
+
| **What it gives you** | Durable orchestration, steps, hooks, streaming, observability | Runtime (microVM hosting), Harness (managed agent loop), Memory, Gateway (tools/MCP), Identity, Browser, Code Interpreter, Observability, Policy, Evaluations |
|
|
27
|
+
| **Languages** | TypeScript / JS (Python beta) | Python-first for authoring; TypeScript or Python project scaffolding via the `@aws/agentcore` CLI; framework-agnostic (LangGraph, CrewAI, Strands, etc.) |
|
|
28
|
+
| **Isolation** | VM-sandboxed workflow code + full-Node steps | Dedicated Firecracker microVM per session (strong hardware isolation) |
|
|
29
|
+
| **Max duration** | No limit ([Vercel World limits](https://vercel.com/docs/workflows/pricing)) | 8-hour hard cap per session |
|
|
30
|
+
| **AI streaming** | Native **durable, resumable** streaming (survives reconnect, cold start, replay) | Live SSE / WebSocket only — no buffering or replay of missed tokens |
|
|
31
|
+
| **Versioning** | Runs pinned to immutable deployment | Immutable runtime versions + endpoints; in-flight sessions stay on their launch version (deployment/rollback only — no replay) |
|
|
32
|
+
| **Portability** | Apache-2.0; runs anywhere Node runs; World abstraction | AWS-only; agent *code* is portable, the operating platform is not |
|
|
33
|
+
| **Compliance** | Inherits your platform | HIPAA-eligible; SOC/PCI/ISO not third-party certified; not FedRAMP-authorized |
|
|
34
|
+
| **Pricing** | SDK free; pay your platform | Per-module consumption (vCPU-hr / GB-hr, etc.) + model inference billed via Bedrock; no minimums |
|
|
35
|
+
|
|
36
|
+
**What the limits mean in practice:** AgentCore's 8-hour session cap means an agent that waits on a human, a long-running job, or a slow external system can't span that wait in one session. Workflow SDK runs have [no duration cap](https://vercel.com/docs/workflows/pricing) — they suspend durably at `sleep()` and hooks for hours or weeks.
|
|
37
|
+
|
|
38
|
+
## The core distinction: durability
|
|
39
|
+
|
|
40
|
+
AgentCore Runtime gives each session an isolated microVM with up to 8 hours of runtime, but the compute is **ephemeral** — on a crash or stop, the next invocation gets a fresh microVM with no automatic replay of the agent loop. AWS's own guidance is to use AgentCore Memory or a framework checkpointer for state durability, and to layer a workflow engine (Step Functions or Temporal) on top when you need durable orchestration.
|
|
41
|
+
|
|
42
|
+
The Workflow SDK *is* that durable layer. With `WorkflowAgent` (in the [AI SDK](/docs/ai)), the agent loop becomes a durable workflow: each model call and tool execution is a checkpointed step, the run resumes mid-loop after a failure, and partial output streams to the client through [resumable streams](/docs/ai/resumable-streams) that survive disconnects and cold starts.
|
|
43
|
+
|
|
44
|
+
## Where AgentCore leads
|
|
45
|
+
|
|
46
|
+
AgentCore is purpose-built for operating agents on AWS, and brings things the Workflow SDK doesn't try to be:
|
|
47
|
+
|
|
48
|
+
- **MicroVM isolation per session** — the strongest hardware isolation among the tools in this section.
|
|
49
|
+
- **Managed agent infrastructure** — Memory, a tool Gateway (turn APIs/Lambda/MCP servers into tools), and Identity (credential vaulting, OAuth) as first-class managed services.
|
|
50
|
+
- **Enterprise/AWS compliance breadth** and VPC/PrivateLink networking.
|
|
51
|
+
|
|
52
|
+
If your priority is running agents inside AWS with managed memory, tools, and identity, AgentCore is a strong fit. If your priority is **durable, resumable, streamable** agent execution that lives in your own TypeScript app and isn't tied to AWS, the Workflow SDK fits better — and the two can be combined (host on AgentCore, orchestrate durably with the Workflow SDK).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
*Compiled from public documentation. AgentCore cold-start figures are community-sourced (no published SLA). Verify against [the AgentCore docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/what-is-bedrock-agentcore.html). Not based on head-to-head benchmarks.*
|