@agent-compose/sdk 0.5.8 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/agent-context.d.ts +1 -1
- package/dist/agent/agent-loop.d.ts +14 -12
- package/dist/agent/pause-client.d.ts +50 -0
- package/dist/agent/pause-client.test.d.ts +1 -0
- package/dist/agent/steer-control.d.ts +22 -6
- package/dist/client.d.ts +12 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +2379 -1463
- package/dist/pause/checkpoint.d.ts +27 -10
- package/dist/pause/manager.d.ts +1 -0
- package/dist/pause/pause-core.d.ts +23 -0
- package/dist/pause/state-dir.d.ts +1 -1
- package/dist/processors/builtins.d.ts +20 -1
- package/dist/processors/gate-pause.d.ts +46 -0
- package/dist/processors/gate-pause.test.d.ts +1 -0
- package/dist/processors/index.d.ts +3 -1
- package/dist/processors/processor.d.ts +13 -0
- package/dist/runtimes/_acp-client.d.ts +140 -0
- package/dist/runtimes/_cli-agent.d.ts +155 -3
- package/dist/runtimes/amp.d.ts +2 -2
- package/dist/runtimes/cli-agent-acp-live.test.d.ts +30 -0
- package/dist/runtimes/cli-agent.test.d.ts +22 -6
- package/dist/runtimes/codex.d.ts +7 -2
- package/dist/runtimes/openai-desktop.js +2365 -1463
- package/dist/runtimes/vercel.js +389 -2
- package/dist/sandbox.d.ts +113 -19
- package/dist/step-invocation/__tests__/background-invoker.test.d.ts +1 -0
- package/dist/step-invocation/index.d.ts +2 -1
- package/dist/step-invocation/invoker.d.ts +36 -0
- package/dist/types/__tests__/environment-build-flag.test.d.ts +1 -0
- package/dist/types/__tests__/workflow-metadata-provider.test.d.ts +1 -0
- package/dist/types/execution-context.d.ts +0 -8
- package/dist/types/protocol.d.ts +32 -1
- package/dist/types/runtime.d.ts +14 -0
- package/dist/types/sandbox-environment.d.ts +6 -1
- package/dist/types/sandbox.d.ts +86 -6
- package/dist/types/workflow-metadata.d.ts +40 -10
- package/dist/types/workflow.d.ts +22 -6
- package/dist/utils/bundler.d.ts +5 -1
- package/dist/workflow-steps/observability.d.ts +28 -2
- package/dist/workflow-steps/types.d.ts +11 -7
- package/dist/workflow-steps/workflow.d.ts +3 -2
- package/package.json +3 -2
- package/src/agent/agent-context.ts +14 -6
- package/src/agent/agent-loop.ts +32 -10
- package/src/agent/pause-client.ts +108 -0
- package/src/agent/run-agent.ts +9 -4
- package/src/agent/steer-control.ts +21 -7
- package/src/client.ts +35 -1
- package/src/index.ts +20 -2
- package/src/pause/checkpoint.ts +33 -14
- package/src/pause/manager.ts +2 -2
- package/src/pause/pause-core.ts +35 -0
- package/src/pause/state-dir.ts +2 -2
- package/src/processors/builtins.ts +44 -1
- package/src/processors/gate-pause.ts +94 -0
- package/src/processors/index.ts +7 -0
- package/src/processors/processor.ts +13 -0
- package/src/runtimes/_acp-client.ts +516 -0
- package/src/runtimes/_cli-agent.ts +416 -3
- package/src/runtimes/claude.ts +31 -3
- package/src/runtimes/codex.ts +21 -1
- package/src/runtimes/vercel.ts +4 -1
- package/src/sandbox.ts +426 -56
- package/src/step-invocation/index.ts +2 -1
- package/src/step-invocation/invoker.ts +195 -84
- package/src/types/execution-context.ts +0 -8
- package/src/types/protocol.ts +27 -1
- package/src/types/runtime.ts +14 -0
- package/src/types/sandbox-environment.ts +12 -1
- package/src/types/sandbox.ts +84 -6
- package/src/types/workflow-metadata.ts +42 -10
- package/src/types/workflow.ts +22 -7
- package/src/utils/bundler.ts +6 -1
- package/src/workflow-steps/observability.ts +51 -5
- package/src/workflow-steps/runner.ts +9 -5
- package/src/workflow-steps/types.ts +11 -7
- package/src/workflow-steps/workflow.ts +3 -2
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
* `workflow-steps/workflow.ts` — it is the cycle-break point.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { SandboxNetworkPolicy, SandboxSize } from "../sandbox.js";
|
|
12
|
+
import type { SandboxNetworkPolicy, SandboxSize, SandboxProviderName } from "../sandbox.js";
|
|
13
13
|
import type { Processor } from "../processors/processor.js";
|
|
14
14
|
|
|
15
|
+
/** The sandbox providers selectable per-workflow. A narrowing of
|
|
16
|
+
* `SandboxProviderName` to the two substrates that execute step workloads —
|
|
17
|
+
* `e2b-desktop` (registry-only, never a workflow's runtime) is deliberately
|
|
18
|
+
* excluded so a workflow can only ask for a substrate that actually runs steps. */
|
|
19
|
+
export type WorkflowSandboxProvider = Extract<SandboxProviderName, "vercel" | "e2b">;
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Workflow-level metadata read by the server at registration. Lives on
|
|
17
23
|
* every `Workflow` as `workflow.metadata`, regardless of which form of
|
|
@@ -77,11 +83,14 @@ export interface IOSchema {
|
|
|
77
83
|
* output-only release. New code should prefer `IOSchema`. */
|
|
78
84
|
export type OutputSchema = IOSchema;
|
|
79
85
|
|
|
80
|
-
/** Per-connector HTTP request matcher (Tier-2 capability narrowing).
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
86
|
+
/** Per-connector HTTP request matcher (Tier-2 capability narrowing). Vercel's
|
|
87
|
+
* firewall consults these when deciding whether to attach the brokered
|
|
88
|
+
* Authorization header to an outbound request: a request to the connector
|
|
89
|
+
* host whose method is not in `methods`, or whose path matches no entry in
|
|
90
|
+
* `pathPrefixes`, goes out WITHOUT the token. NOT enforced on E2B — its
|
|
91
|
+
* native rules carry only a header transform, no method/path matcher, so the
|
|
92
|
+
* token rides every request to an allowed connector host there (the host
|
|
93
|
+
* allowlist still confines which hosts are reachable). See `toE2bNetwork`. */
|
|
85
94
|
export interface ConnectorRequestRules {
|
|
86
95
|
/** Allowed HTTP methods (upper-case). Omit = any method (subject to
|
|
87
96
|
* `access`). */
|
|
@@ -145,10 +154,19 @@ export interface InvokePolicy {
|
|
|
145
154
|
* today; kept as its own object so finer controls (disk, gpu, …) can be
|
|
146
155
|
* added later without reshaping `WorkflowMetadata`. */
|
|
147
156
|
export interface SandboxResources {
|
|
148
|
-
/** Machine
|
|
149
|
-
*
|
|
150
|
-
*
|
|
157
|
+
/** Machine hardware SKU — one of the `SandboxSize` vCPU strings
|
|
158
|
+
* (`2vcpu-4gb` | `4vcpu-8gb` | `8vcpu-16gb` | `32vcpu-64gb`). Maps to
|
|
159
|
+
* provider specs at create time (Vercel: 2 / 4 / 8 / 32 vCPU, 2048 MB RAM
|
|
160
|
+
* per vCPU). Omit → the smallest SKU. E2B sizing is template-defined and
|
|
161
|
+
* ignores this. */
|
|
151
162
|
size?: SandboxSize;
|
|
163
|
+
/** Sandbox provider this workflow's runs execute on — `"vercel"` or
|
|
164
|
+
* `"e2b"`. Optional and additive: omit and the run resolves to the
|
|
165
|
+
* platform default (`vercel`). Set explicitly to pin a workflow to a
|
|
166
|
+
* substrate regardless of the deployment default. Snapshot formats are
|
|
167
|
+
* provider-specific, so the resolved provider is stamped on the run row
|
|
168
|
+
* at first dispatch and reused across pause/resume/replay. */
|
|
169
|
+
provider?: WorkflowSandboxProvider;
|
|
152
170
|
}
|
|
153
171
|
|
|
154
172
|
export interface WorkflowMetadata {
|
|
@@ -171,7 +189,8 @@ export interface WorkflowMetadata {
|
|
|
171
189
|
outputSchema?: IOSchema;
|
|
172
190
|
/** All snapshot config — boot source + capture mode. */
|
|
173
191
|
snapshots?: SnapshotConfig;
|
|
174
|
-
/** Sandbox machine resources
|
|
192
|
+
/** Sandbox machine resources — size + provider. Optional; omit → smallest
|
|
193
|
+
* SKU on the platform-default provider (`vercel`). */
|
|
175
194
|
resources?: SandboxResources;
|
|
176
195
|
processors?: readonly Processor[];
|
|
177
196
|
/** Connector requirements — providers whose APIs this workflow calls.
|
|
@@ -184,6 +203,18 @@ export interface WorkflowMetadata {
|
|
|
184
203
|
/** Tier-1 invoke ACL — who may dispatch this connector-brokering workflow.
|
|
185
204
|
* See `InvokePolicy`. */
|
|
186
205
|
invokePolicy?: InvokePolicy;
|
|
206
|
+
/** Set by `defineSandboxEnvironment` to mark this workflow as an ENVIRONMENT
|
|
207
|
+
* BUILD — a setup-only workflow whose job is to leave its VM configured and
|
|
208
|
+
* snapshot it (base-env / agent-env). Environment builds build a platform
|
|
209
|
+
* IMAGE and never use the shared factory drive, so the server SKIPS mounting
|
|
210
|
+
* /factory for them: a live Archil mount baked into the captured snapshot
|
|
211
|
+
* fails the NEXT boot's re-mount ("an older Archil process is still running
|
|
212
|
+
* for this mountpoint"), degrading /factory for every workflow booting from
|
|
213
|
+
* that snapshot. Absent on ordinary workflows — which mount /factory exactly
|
|
214
|
+
* as before. Optional + additive: an ABSENT flag contributes nothing to the
|
|
215
|
+
* canonical metadata hash (frozen-metadata rule), so existing workflows are
|
|
216
|
+
* not forced to re-register. */
|
|
217
|
+
environmentBuild?: boolean;
|
|
187
218
|
}
|
|
188
219
|
|
|
189
220
|
/**
|
|
@@ -215,6 +246,7 @@ export function extractMetadata(source: Partial<WorkflowMetadata>): WorkflowMeta
|
|
|
215
246
|
if (source.connectors !== undefined) out.connectors = freezeMetadataValue(source.connectors);
|
|
216
247
|
if (source.connectorOperation !== undefined) out.connectorOperation = Object.freeze({ ...source.connectorOperation });
|
|
217
248
|
if (source.invokePolicy !== undefined) out.invokePolicy = freezeMetadataValue(source.invokePolicy);
|
|
249
|
+
if (source.environmentBuild !== undefined) out.environmentBuild = source.environmentBuild;
|
|
218
250
|
return Object.freeze(out);
|
|
219
251
|
}
|
|
220
252
|
|
package/src/types/workflow.ts
CHANGED
|
@@ -53,9 +53,16 @@ export interface WorkflowCtx<TInput extends Record<string, unknown> = Record<str
|
|
|
53
53
|
/** Persist key-value metadata on the run record (e.g. prUrl, planUrl). */
|
|
54
54
|
setMetadata: (data: Record<string, unknown>) => Promise<void>;
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
56
|
+
* Durable named step (ADR-0012). Runs `fn` once and memoises its result to
|
|
57
|
+
* the sandbox state-dir; on a pause-resume re-entry the recorded value is
|
|
58
|
+
* returned and `fn` is NOT re-run (a duration-0 "restored" sub-step). Also
|
|
59
|
+
* emits substep_started / substep_completed / substep_failed lifecycle
|
|
60
|
+
* events with duration — use for long phases you want both durable and
|
|
58
61
|
* visible on the run's timeline (setup, external API calls, submit).
|
|
62
|
+
*
|
|
63
|
+
* Names must be unique within a step body (they key the memoise file).
|
|
64
|
+
* A body that pauses is never memoised — the resume re-runs it. For
|
|
65
|
+
* cross-process side effects (DB writes, emails) use `invokeChild`.
|
|
59
66
|
*/
|
|
60
67
|
step<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
61
68
|
/** Pass to `agent({ events: ctx.agentEvents })` to stream agent lifecycle events. */
|
|
@@ -121,10 +128,12 @@ export interface WorkflowDefinition<
|
|
|
121
128
|
*/
|
|
122
129
|
snapshots?: SnapshotConfig;
|
|
123
130
|
/**
|
|
124
|
-
* Sandbox
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
131
|
+
* Sandbox resources — machine SKU (`size`, a `SandboxSize` vCPU string:
|
|
132
|
+
* `2vcpu-4gb` | `4vcpu-8gb` | `8vcpu-16gb` | `32vcpu-64gb`) and `provider`
|
|
133
|
+
* (`vercel` | `e2b`). `size` maps to provider machine specs at create
|
|
134
|
+
* (Vercel: vCPUs, 2048 MB RAM per vCPU); E2B sizing is template-defined and
|
|
135
|
+
* ignores it. Optional; omit → smallest SKU on the default provider.
|
|
136
|
+
* Per-invocation `invoke({ size })` overrides the size.
|
|
128
137
|
*/
|
|
129
138
|
resources?: SandboxResources;
|
|
130
139
|
/**
|
|
@@ -198,6 +207,13 @@ export interface WorkflowDefinition<
|
|
|
198
207
|
* invokePolicy: { users: "owner", workflows: ["nightly-orchestrator"] }
|
|
199
208
|
*/
|
|
200
209
|
invokePolicy?: InvokePolicy;
|
|
210
|
+
/**
|
|
211
|
+
* Internal — set by `defineSandboxEnvironment`, not by workflow authors.
|
|
212
|
+
* Marks the workflow as an environment build (base-env / agent-env) so the
|
|
213
|
+
* server skips mounting the shared factory drive for its runs (#13). See
|
|
214
|
+
* `WorkflowMetadata.environmentBuild`.
|
|
215
|
+
*/
|
|
216
|
+
environmentBuild?: boolean;
|
|
201
217
|
}
|
|
202
218
|
|
|
203
219
|
/**
|
|
@@ -244,7 +260,6 @@ function compileRunForm<TOutput, TInput extends Record<string, unknown>>(
|
|
|
244
260
|
setMetadata: stepCtx.setMetadata,
|
|
245
261
|
step: stepCtx.step,
|
|
246
262
|
agentEvents: stepCtx.agentEvents,
|
|
247
|
-
checkpoint: stepCtx.checkpoint,
|
|
248
263
|
pause: stepCtx.pause,
|
|
249
264
|
sleep: stepCtx.sleep,
|
|
250
265
|
waitForEvent: stepCtx.waitForEvent,
|
package/src/utils/bundler.ts
CHANGED
|
@@ -98,7 +98,8 @@ export interface BundledWorkflow {
|
|
|
98
98
|
/** Snapshot config from the workflow definition — `bootFrom` (where to
|
|
99
99
|
* restore at run start), `save`, `retain`. */
|
|
100
100
|
snapshots?: SnapshotConfig;
|
|
101
|
-
/** Sandbox
|
|
101
|
+
/** Sandbox resources declared via `defineWorkflow({ resources })` — machine
|
|
102
|
+
* SKU (`size`) and `provider` (`vercel` | `e2b`). */
|
|
102
103
|
resources?: SandboxResources;
|
|
103
104
|
workflowPlan: WorkflowPlan;
|
|
104
105
|
/** Compact JSON-Schema-shaped description of the workflow's input
|
|
@@ -114,6 +115,9 @@ export interface BundledWorkflow {
|
|
|
114
115
|
/** Tier-1 invoke ACL — who may dispatch this connector-brokering
|
|
115
116
|
* workflow (`defineWorkflow({ invokePolicy })`). */
|
|
116
117
|
invokePolicy?: InvokePolicy;
|
|
118
|
+
/** Set by `defineSandboxEnvironment` — marks an environment build so the
|
|
119
|
+
* server skips the /factory mount for its runs (#13). */
|
|
120
|
+
environmentBuild?: boolean;
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
async function bundle(path: string, label: string): Promise<string> {
|
|
@@ -340,6 +344,7 @@ export async function bundleWorkflow(
|
|
|
340
344
|
...(metadata.connectors !== undefined ? { connectors: metadata.connectors } : {}),
|
|
341
345
|
...(metadata.connectorOperation !== undefined ? { connectorOperation: metadata.connectorOperation } : {}),
|
|
342
346
|
...(metadata.invokePolicy !== undefined ? { invokePolicy: metadata.invokePolicy } : {}),
|
|
347
|
+
...(metadata.environmentBuild !== undefined ? { environmentBuild: metadata.environmentBuild } : {}),
|
|
343
348
|
};
|
|
344
349
|
}
|
|
345
350
|
|
|
@@ -32,15 +32,21 @@
|
|
|
32
32
|
import type { AgentLifecycleEvent } from "../agent/agent-loop.js";
|
|
33
33
|
import type { AgentEventSink } from "../types/workflow.js";
|
|
34
34
|
import type { LiveAgentEventEmitter } from "./run-callback.js";
|
|
35
|
-
import {
|
|
35
|
+
import { isPauseSignal } from "../pause/pause-core.js";
|
|
36
|
+
import type { ScopedMemoize } from "../pause/checkpoint.js";
|
|
36
37
|
|
|
37
38
|
/** One named sub-step (from `ctx.step("name", async () => ...)`).
|
|
38
|
-
* Becomes a `workflow_substep_*` lifecycle event on the run timeline.
|
|
39
|
+
* Becomes a `workflow_substep_*` lifecycle event on the run timeline.
|
|
40
|
+
*
|
|
41
|
+
* `restored` is the durable-step resume case (ADR-0012): the body did NOT
|
|
42
|
+
* re-run — its memoised result was read from the state-dir checkpoint a
|
|
43
|
+
* prior subprocess wrote. Always `durationMs: 0` (no work happened this
|
|
44
|
+
* pass) and never carries `error`. */
|
|
39
45
|
export interface SubStepEvent {
|
|
40
46
|
name: string;
|
|
41
47
|
startedAt: number;
|
|
42
48
|
durationMs: number;
|
|
43
|
-
status: "completed" | "failed";
|
|
49
|
+
status: "completed" | "failed" | "restored";
|
|
44
50
|
/** Present when status="failed" — the user error's message. */
|
|
45
51
|
error?: string;
|
|
46
52
|
}
|
|
@@ -76,22 +82,60 @@ export class StepObservabilityCollector {
|
|
|
76
82
|
private events: AgentLifecycleEvent[] = [];
|
|
77
83
|
private subSteps: SubStepEvent[] = [];
|
|
78
84
|
private readonly liveEmitter?: LiveAgentEventEmitter;
|
|
85
|
+
/** State-dir memoise bound to this step's `step<idx>` scope. Present in
|
|
86
|
+
* the sandbox runner (durable `ctx.step`); absent in-process so unit
|
|
87
|
+
* tests record observability without touching disk. */
|
|
88
|
+
private readonly memoize?: ScopedMemoize;
|
|
89
|
+
/** Sub-step names already used in THIS step body. A durable `ctx.step`
|
|
90
|
+
* memoises by name, so a reused name would alias two distinct bodies
|
|
91
|
+
* onto one checkpoint file — guarded by throwing on reuse (ADR-0012). */
|
|
92
|
+
private readonly usedStepNames: Set<string> = new Set();
|
|
79
93
|
/** Seqs of events the server confirmed via the live route. */
|
|
80
94
|
private readonly ackedSeqs: Set<number> = new Set();
|
|
81
95
|
/** Promises for in-flight live emits — awaited at snapshot time. */
|
|
82
96
|
private readonly inFlight: Set<Promise<void>> = new Set();
|
|
83
97
|
|
|
84
|
-
constructor(opts: { liveEmitter?: LiveAgentEventEmitter } = {}) {
|
|
98
|
+
constructor(opts: { liveEmitter?: LiveAgentEventEmitter; memoize?: ScopedMemoize } = {}) {
|
|
85
99
|
this.liveEmitter = opts.liveEmitter;
|
|
100
|
+
this.memoize = opts.memoize;
|
|
86
101
|
}
|
|
87
102
|
|
|
88
103
|
readonly setMetadata = async (data: Record<string, unknown>): Promise<void> => {
|
|
89
104
|
Object.assign(this.metadata, data);
|
|
90
105
|
};
|
|
91
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Durable named sub-step (ADR-0012). The body's result is memoised to the
|
|
109
|
+
* state-dir checkpoint keyed `step<idx>.<name>`; on a pause-resume re-entry
|
|
110
|
+
* the value is read from disk and `fn` is NOT re-run (recorded as a
|
|
111
|
+
* duration-0 `"restored"` sub-step). A body that PAUSES (PauseSignal)
|
|
112
|
+
* propagates BEFORE any memoise write — partial work is never stored, so
|
|
113
|
+
* the resume re-runs it.
|
|
114
|
+
*
|
|
115
|
+
* When no memoise store is wired (in-process tests), the body always runs
|
|
116
|
+
* and is recorded as a normal timed `"completed"` / `"failed"` sub-step.
|
|
117
|
+
*/
|
|
92
118
|
readonly step = async <T>(name: string, fn: () => Promise<T>): Promise<T> => {
|
|
119
|
+
if (this.usedStepNames.has(name)) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`ctx.step("${name}", …) reuses a step name already used in this step body. ` +
|
|
122
|
+
`Durable step names must be unique — they key the resume-time memoise file.`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
this.usedStepNames.add(name);
|
|
126
|
+
|
|
93
127
|
const startedAt = Date.now();
|
|
94
128
|
try {
|
|
129
|
+
if (this.memoize) {
|
|
130
|
+
const { value, restored } = await this.memoize(name, fn);
|
|
131
|
+
this.subSteps.push({
|
|
132
|
+
name,
|
|
133
|
+
startedAt,
|
|
134
|
+
durationMs: restored ? 0 : Date.now() - startedAt,
|
|
135
|
+
status: restored ? "restored" : "completed",
|
|
136
|
+
});
|
|
137
|
+
return value;
|
|
138
|
+
}
|
|
95
139
|
const result = await fn();
|
|
96
140
|
this.subSteps.push({
|
|
97
141
|
name,
|
|
@@ -102,7 +146,9 @@ export class StepObservabilityCollector {
|
|
|
102
146
|
return result;
|
|
103
147
|
} catch (err) {
|
|
104
148
|
// A pause inside ctx.step is control flow, not a failed sub-step — let
|
|
105
|
-
// it propagate untouched so serveStep emits the pause sentinel.
|
|
149
|
+
// it propagate untouched so serveStep emits the pause sentinel. The
|
|
150
|
+
// memoise write is skipped (it only runs after `fn` resolves), so no
|
|
151
|
+
// partial result is stored — the resume re-enters and re-runs the body.
|
|
106
152
|
if (isPauseSignal(err)) throw err;
|
|
107
153
|
this.subSteps.push({
|
|
108
154
|
name,
|
|
@@ -29,7 +29,7 @@ import type { SandboxProvider } from "../types/sandbox.js";
|
|
|
29
29
|
import type { WorkflowRun, WorkflowCtx } from "../types/workflow.js";
|
|
30
30
|
import { StepObservabilityCollector, type StepObservability } from "./observability.js";
|
|
31
31
|
import type { LiveAgentEventEmitter } from "./run-callback.js";
|
|
32
|
-
import {
|
|
32
|
+
import { scopedMemoize } from "../pause/checkpoint.js";
|
|
33
33
|
import { corePause, PauseSignal, isPauseSignal, type PauseRequest } from "../pause/pause-core.js";
|
|
34
34
|
import type { StepPauseRequest } from "../step-invocation/types.js";
|
|
35
35
|
import { buildPauseWrappers, type PauseFn, type KindedPauseFn } from "../pause/wrappers.js";
|
|
@@ -129,7 +129,10 @@ export async function runWorkflowSingleStep(opts: RunWorkflowSingleStepOpts): Pr
|
|
|
129
129
|
if (!step) throw new Error(`Step index ${opts.stepIndex} not found in workflow "${opts.workflow.id}"`);
|
|
130
130
|
const parsedInput = step.input.safeParse(opts.input);
|
|
131
131
|
if (!parsedInput.success) throw new StepValidationError(step.name, "input", parsedInput.error);
|
|
132
|
-
const collector = new StepObservabilityCollector({
|
|
132
|
+
const collector = new StepObservabilityCollector({
|
|
133
|
+
liveEmitter: opts.liveAgentEventEmitter,
|
|
134
|
+
memoize: scopedMemoize(`step${opts.stepIndex}`),
|
|
135
|
+
});
|
|
133
136
|
const coord = { runId: opts.run.id, stepIndex: opts.stepIndex };
|
|
134
137
|
const pauseWithKind: KindedPauseFn = function <T>(req: PauseRequest<T>, kind: StepPauseRequest["kind"]): Promise<T> {
|
|
135
138
|
return corePause(req, coord, kind);
|
|
@@ -148,7 +151,6 @@ export async function runWorkflowSingleStep(opts: RunWorkflowSingleStepOpts): Pr
|
|
|
148
151
|
setMetadata: collector.setMetadata,
|
|
149
152
|
step: collector.step,
|
|
150
153
|
agentEvents: collector.agentEvents,
|
|
151
|
-
checkpoint: scopedCheckpoint(`step${opts.stepIndex}`),
|
|
152
154
|
pause,
|
|
153
155
|
...buildPauseWrappers(pauseWithKind),
|
|
154
156
|
};
|
|
@@ -223,7 +225,10 @@ export async function runWorkflowSteps<TInput, TOutput>(
|
|
|
223
225
|
throw err;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
const collector = new StepObservabilityCollector({
|
|
228
|
+
const collector = new StepObservabilityCollector({
|
|
229
|
+
liveEmitter: opts.liveAgentEventEmitter,
|
|
230
|
+
memoize: scopedMemoize(`step${i}`),
|
|
231
|
+
});
|
|
227
232
|
const coord = { runId: run.id, stepIndex: i };
|
|
228
233
|
const pauseWithKind: KindedPauseFn = function <T>(req: PauseRequest<T>, kind: StepPauseRequest["kind"]): Promise<T> {
|
|
229
234
|
return corePause(req, coord, kind);
|
|
@@ -242,7 +247,6 @@ export async function runWorkflowSteps<TInput, TOutput>(
|
|
|
242
247
|
setMetadata: collector.setMetadata,
|
|
243
248
|
step: collector.step,
|
|
244
249
|
agentEvents: collector.agentEvents,
|
|
245
|
-
checkpoint: scopedCheckpoint(`step${i}`),
|
|
246
250
|
pause,
|
|
247
251
|
...buildPauseWrappers(pauseWithKind),
|
|
248
252
|
};
|
|
@@ -38,14 +38,18 @@ export interface StepContext<TInput = unknown> extends BaseExecutionContext {
|
|
|
38
38
|
*/
|
|
39
39
|
setMetadata(data: Record<string, unknown>): Promise<void>;
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
41
|
+
* Durable named sub-step (ADR-0012). Runs `fn` once and memoises its
|
|
42
|
+
* result to the state-dir checkpoint keyed `step<idx>.<name>`; on a
|
|
43
|
+
* pause-resume re-entry the value is read from disk and `fn` is NOT
|
|
44
|
+
* re-run. Emits `workflow_substep_started` /
|
|
45
|
+
* `workflow_substep_completed` / `workflow_substep_failed` lifecycle
|
|
46
|
+
* events on the run timeline with the measured `durationMs` — a restored
|
|
47
|
+
* sub-step reports `durationMs: 0` and status `"restored"`.
|
|
46
48
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
+
* Names must be unique within one step body — they key the memoise file,
|
|
50
|
+
* so a reused name throws. A body that pauses (PauseSignal) is never
|
|
51
|
+
* memoised: the resume re-enters and re-runs it. For cross-process side
|
|
52
|
+
* effects (DB writes, emails) use `invokeChild`, not `step`.
|
|
49
53
|
*/
|
|
50
54
|
step<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
51
55
|
/**
|
|
@@ -51,8 +51,9 @@ export interface StepWorkflowDefinition<TInput, TOutput> {
|
|
|
51
51
|
networkPolicy?: SandboxNetworkPolicy;
|
|
52
52
|
placeholders?: Record<string, string>;
|
|
53
53
|
snapshots?: SnapshotConfig;
|
|
54
|
-
/** Sandbox
|
|
55
|
-
*
|
|
54
|
+
/** Sandbox resources — machine SKU (`size`, a `SandboxSize` vCPU string)
|
|
55
|
+
* and `provider` (`vercel` | `e2b`). Vercel maps `size` to vCPUs; E2B
|
|
56
|
+
* sizing is template-defined. Omit → smallest SKU on the default provider. */
|
|
56
57
|
resources?: SandboxResources;
|
|
57
58
|
processors?: readonly Processor[];
|
|
58
59
|
}
|