@agent-compose/sdk 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -33
- package/dist/agent/agent-loop.d.ts +83 -5
- package/dist/agent/run-agent.d.ts +34 -9
- package/dist/client.d.ts +247 -99
- package/dist/index.d.ts +26 -11
- package/dist/index.js +1967 -745
- package/dist/processors/builtins.d.ts +35 -0
- package/dist/processors/index.d.ts +4 -0
- package/dist/processors/processor.d.ts +91 -0
- package/dist/processors/processor.test.d.ts +1 -0
- package/dist/processors/runner.d.ts +19 -0
- package/dist/request-context/index.d.ts +2 -0
- package/dist/request-context/request-context.d.ts +159 -0
- package/dist/request-context/request-context.test.d.ts +1 -0
- package/dist/runtimes/claude.d.ts +27 -50
- package/dist/runtimes/openai-desktop.js +1918 -741
- package/dist/runtimes/vercel.d.ts +34 -0
- package/dist/runtimes/vercel.js +474 -0
- package/dist/sandbox.d.ts +29 -25
- package/dist/step-invocation/__tests__/invoker.test.d.ts +1 -0
- package/dist/step-invocation/__tests__/protocol.test.d.ts +1 -0
- package/dist/step-invocation/__tests__/server.test.d.ts +1 -0
- package/dist/step-invocation/index.d.ts +25 -0
- package/dist/step-invocation/invoker.d.ts +65 -0
- package/dist/step-invocation/protocol.d.ts +44 -0
- package/dist/step-invocation/server.d.ts +63 -0
- package/dist/step-invocation/types.d.ts +72 -0
- package/dist/tools/coding.d.ts +49 -0
- package/dist/tools/coding.test.d.ts +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/types/events.d.ts +36 -0
- package/dist/types/execution-context.d.ts +22 -0
- package/dist/types/runtime.d.ts +32 -0
- package/dist/types/sandbox-environment.d.ts +5 -2
- package/dist/types/sandbox.d.ts +14 -12
- package/dist/types/workflow-metadata.d.ts +51 -0
- package/dist/types/workflow-plan.d.ts +19 -0
- package/dist/types/workflow.d.ts +57 -17
- package/dist/utils/bundler.d.ts +62 -3
- package/dist/workflow-steps/__tests__/observability.test.d.ts +1 -0
- package/dist/workflow-steps/index.d.ts +10 -0
- package/dist/workflow-steps/observability.d.ts +58 -0
- package/dist/workflow-steps/runner.d.ts +96 -0
- package/dist/workflow-steps/step.d.ts +25 -0
- package/dist/workflow-steps/types.d.ts +135 -0
- package/dist/workflow-steps/workflow-steps.test.d.ts +1 -0
- package/dist/workflow-steps/workflow.d.ts +50 -0
- package/dist/workflows/engine.d.ts +27 -13
- package/dist/workflows/invoke-child.d.ts +10 -0
- package/package.json +25 -15
- package/src/agent/agent-loop.ts +197 -26
- package/src/agent/run-agent.ts +40 -15
- package/src/client.ts +326 -76
- package/src/index.ts +124 -10
- package/src/processors/builtins.ts +72 -0
- package/src/processors/index.ts +15 -0
- package/src/processors/processor.ts +103 -0
- package/src/processors/runner.ts +42 -0
- package/src/request-context/index.ts +17 -0
- package/src/request-context/request-context.ts +302 -0
- package/src/runtimes/claude.ts +123 -254
- package/src/runtimes/vercel.ts +180 -0
- package/src/sandbox.ts +53 -21
- package/src/step-invocation/index.ts +33 -0
- package/src/step-invocation/invoker.ts +204 -0
- package/src/step-invocation/protocol.ts +57 -0
- package/src/step-invocation/server.ts +184 -0
- package/src/step-invocation/types.ts +70 -0
- package/src/tools/coding.ts +126 -0
- package/src/tools/index.ts +8 -0
- package/src/types/events.ts +40 -0
- package/src/types/execution-context.ts +30 -0
- package/src/types/runtime.ts +24 -0
- package/src/types/sandbox-environment.ts +7 -5
- package/src/types/sandbox.ts +16 -12
- package/src/types/workflow-metadata.ts +84 -0
- package/src/types/workflow-plan.ts +24 -0
- package/src/types/workflow.ts +139 -25
- package/src/utils/bundler.ts +206 -18
- package/src/utils/source-loader.ts +2 -2
- package/src/workflow-steps/index.ts +30 -0
- package/src/workflow-steps/observability.ts +103 -0
- package/src/workflow-steps/runner.ts +244 -0
- package/src/workflow-steps/step.ts +38 -0
- package/src/workflow-steps/types.ts +134 -0
- package/src/workflow-steps/workflow.ts +95 -0
- package/src/workflows/engine.ts +69 -40
- package/src/workflows/invoke-child.ts +29 -0
package/dist/types/workflow.d.ts
CHANGED
|
@@ -6,29 +6,36 @@
|
|
|
6
6
|
* - `ctx` carries facts + observability about THIS run (id, input,
|
|
7
7
|
* setMetadata, step). Metadata bag.
|
|
8
8
|
* - `sandbox` is a capability handed to you by the engine for doing work
|
|
9
|
-
* (exec commands, write files). Pass it to `
|
|
9
|
+
* (exec commands, write files). Pass it to `agent({ sandbox, ... })`
|
|
10
10
|
* and to any helper that takes a SandboxProvider (git utilities, file
|
|
11
11
|
* writers). Constructed once per run; reuse it.
|
|
12
12
|
*
|
|
13
|
-
* LLM agent loops live in `
|
|
13
|
+
* LLM agent loops live in `agent(opts)` (sdk/src/agent/run-agent.ts).
|
|
14
14
|
* Invoking other workflows uses `AgentComposeClient.invoke[AndWait](...)`.
|
|
15
15
|
*/
|
|
16
16
|
import type { SandboxNetworkPolicy } from "../sandbox.js";
|
|
17
17
|
import type { SandboxProvider } from "./sandbox.js";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
import type { AgentLifecycleEvent } from "../agent/agent-loop.js";
|
|
19
|
+
import type { Processor } from "../processors/processor.js";
|
|
20
|
+
import type { StepWorkflowDefinition } from "../workflow-steps/workflow.js";
|
|
21
|
+
import type { Workflow } from "../workflow-steps/types.js";
|
|
22
|
+
import type { BaseExecutionContext } from "./execution-context.js";
|
|
23
|
+
export type { WorkflowRun, InvokeChild, BaseExecutionContext } from "./execution-context.js";
|
|
24
|
+
export type { WorkflowMetadata } from "./workflow-metadata.js";
|
|
25
|
+
export interface AgentEventSink {
|
|
26
|
+
emit(event: AgentLifecycleEvent): void | Promise<void>;
|
|
21
27
|
}
|
|
22
|
-
|
|
28
|
+
import type { WorkflowMemoryConfig } from "./workflow-metadata.js";
|
|
29
|
+
export type { WorkflowMemoryConfig };
|
|
30
|
+
/** Turn/iteration budget for `agent(opts)`. Re-exported here so authors
|
|
23
31
|
* can type per-invoke budget overrides they pass as workflow input. */
|
|
24
32
|
export interface AgentBudget {
|
|
25
33
|
turnsPerIteration: number;
|
|
26
34
|
maxIterations: number;
|
|
27
35
|
}
|
|
28
36
|
/** Context passed to a workflow function — facts + observability for this run. */
|
|
29
|
-
export interface WorkflowCtx {
|
|
30
|
-
|
|
31
|
-
input?: Record<string, unknown>;
|
|
37
|
+
export interface WorkflowCtx<TInput extends Record<string, unknown> = Record<string, unknown>> extends Omit<BaseExecutionContext, "sandbox"> {
|
|
38
|
+
input?: TInput;
|
|
32
39
|
/** Persist key-value metadata on the run record (e.g. prUrl, planUrl). */
|
|
33
40
|
setMetadata: (data: Record<string, unknown>) => Promise<void>;
|
|
34
41
|
/**
|
|
@@ -37,9 +44,19 @@ export interface WorkflowCtx {
|
|
|
37
44
|
* visible on the run's timeline (setup, external API calls, submit).
|
|
38
45
|
*/
|
|
39
46
|
step<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
47
|
+
/** Pass to `agent({ events: ctx.agentEvents })` to stream agent lifecycle events. */
|
|
48
|
+
agentEvents: AgentEventSink;
|
|
49
|
+
/**
|
|
50
|
+
* Workflow-level processors registered via `defineWorkflow({ processors })`.
|
|
51
|
+
* Read-only here. Authors merge with agent-specific lists when calling
|
|
52
|
+
* `agent({ processors: [...ctx.processors, mySpecific] })`.
|
|
53
|
+
*
|
|
54
|
+
* Empty array when the workflow declared no processors.
|
|
55
|
+
*/
|
|
56
|
+
processors: readonly Processor[];
|
|
40
57
|
}
|
|
41
|
-
/** A workflow is `async (ctx, sandbox) =>
|
|
42
|
-
export type WorkflowFn<
|
|
58
|
+
/** A workflow is `async (ctx, sandbox) => TOutput`. */
|
|
59
|
+
export type WorkflowFn<TOutput = unknown, TInput extends Record<string, unknown> = Record<string, unknown>> = (ctx: WorkflowCtx<TInput>, sandbox: SandboxProvider) => Promise<TOutput>;
|
|
43
60
|
/**
|
|
44
61
|
* Declare a workflow with server-side metadata.
|
|
45
62
|
* Use `export default defineWorkflow({ run, networkPolicy, ... })` to attach
|
|
@@ -49,8 +66,8 @@ export type WorkflowFn<T = unknown> = (ctx: WorkflowCtx, sandbox: SandboxProvide
|
|
|
49
66
|
* Without defineWorkflow, a plain `export default async (ctx) => {...}` still
|
|
50
67
|
* works — the workflow just runs with secrets passed directly in env.
|
|
51
68
|
*/
|
|
52
|
-
export interface WorkflowDefinition<
|
|
53
|
-
run: WorkflowFn<
|
|
69
|
+
export interface WorkflowDefinition<TOutput = unknown, TInput extends Record<string, unknown> = Record<string, unknown>> {
|
|
70
|
+
run: WorkflowFn<TOutput, TInput>;
|
|
54
71
|
/**
|
|
55
72
|
* Reference to a snapshot the runner should boot from at run start.
|
|
56
73
|
* Accepts a run UUID, a workflow name, or `name@version` — any workflow
|
|
@@ -82,6 +99,13 @@ export interface WorkflowDefinition<T = unknown> {
|
|
|
82
99
|
* }
|
|
83
100
|
*/
|
|
84
101
|
networkPolicy?: SandboxNetworkPolicy;
|
|
102
|
+
/**
|
|
103
|
+
* Workflow-level processor chain — runs around every `agent(...)` loop the
|
|
104
|
+
* workflow body launches, ahead of any agent-specific processors. Use for
|
|
105
|
+
* org-wide gating (deny destructive tools, require scopes). Authors merge
|
|
106
|
+
* with agent-specific lists via `[...ctx.processors, ...]`.
|
|
107
|
+
*/
|
|
108
|
+
processors?: readonly Processor[];
|
|
85
109
|
/**
|
|
86
110
|
* Optional placeholder env var values for secrets referenced in the network policy.
|
|
87
111
|
* By default, brokered secrets are removed from the runner env entirely — the real
|
|
@@ -96,16 +120,32 @@ export interface WorkflowDefinition<T = unknown> {
|
|
|
96
120
|
* }
|
|
97
121
|
*/
|
|
98
122
|
placeholders?: Record<string, string>;
|
|
123
|
+
/** Workflow Memory extraction. Defaults to "default" when omitted.
|
|
124
|
+
* Set false to skip memory extraction for this workflow, or point at a
|
|
125
|
+
* custom memory workflow once custom extractors are supported. */
|
|
126
|
+
memory?: WorkflowMemoryConfig;
|
|
99
127
|
}
|
|
100
128
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
129
|
+
* Declare a workflow. Two forms; both return a `Workflow` whose
|
|
130
|
+
* `metadata` field carries the server-readable declarations.
|
|
131
|
+
*
|
|
132
|
+
* Run form — wraps the legacy `(ctx, sandbox) => T` body as a single
|
|
133
|
+
* step internally. Lifecycle granularity is workflow-level (one step).
|
|
134
|
+
*
|
|
135
|
+
* Step form — the typed builder. Multiple steps with explicit input/
|
|
136
|
+
* output schemas, durability + replay at every step boundary.
|
|
137
|
+
*
|
|
138
|
+
* Both forms produce the same downstream shape: the bundler reads
|
|
139
|
+
* `workflow.metadata.networkPolicy` / `placeholders` / etc.; the server
|
|
140
|
+
* stores the step plan; runner subprocesses execute one step at a time
|
|
141
|
+
* via the StepInvocation seam.
|
|
104
142
|
*/
|
|
105
|
-
export declare function defineWorkflow<
|
|
143
|
+
export declare function defineWorkflow<TOutput = unknown, TInput extends Record<string, unknown> = Record<string, unknown>>(def: WorkflowDefinition<TOutput, TInput>): Workflow<TInput, TOutput>;
|
|
144
|
+
export declare function defineWorkflow<TInput, TOutput>(def: StepWorkflowDefinition<TInput, TOutput>): import("../workflow-steps/workflow.js").WorkflowBuilder<TInput, TInput>;
|
|
106
145
|
/** Observability-only lifecycle hooks — passed to the workflow engine, not workflow authors. */
|
|
107
146
|
export interface WorkflowHooks {
|
|
108
147
|
onStepStart?: (step: string) => void;
|
|
109
148
|
onStepComplete?: (step: string, durationMs: number) => void;
|
|
110
149
|
onStepFail?: (step: string, durationMs: number, reason: string) => void;
|
|
150
|
+
onAgentLifecycleEvent?: (event: AgentLifecycleEvent) => void;
|
|
111
151
|
}
|
package/dist/utils/bundler.d.ts
CHANGED
|
@@ -4,13 +4,51 @@
|
|
|
4
4
|
* Shared between the CLI (agentc register) and tests. Uses `Bun.build`
|
|
5
5
|
* to bundle TypeScript sources into a self-contained ESM module.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Two layers of validation run before the bundle leaves this function:
|
|
8
|
+
* 1. AST check (`assertDefaultExportIsDefineWorkflow`) on the bundled
|
|
9
|
+
* output — proves the default export is a `defineWorkflow(...)` call,
|
|
10
|
+
* not a raw `async function`. Catches harnesses that hand-roll their
|
|
11
|
+
* own register flow without going through `defineWorkflow`.
|
|
12
|
+
* 2. Runtime brand check (`isWorkflow`) — confirms the value the module
|
|
13
|
+
* actually exports carries the `WORKFLOW_BRAND` symbol that
|
|
14
|
+
* `defineWorkflow` / `defineWorkflow(...).build()` stamps. Catches
|
|
15
|
+
* indirection (re-exports, aliasing) that the AST walk can't follow.
|
|
16
|
+
*
|
|
17
|
+
* Both checks happen here, in the user's dev environment. The server NEVER
|
|
18
|
+
* parses or imports user source — it only validates the structured manifest
|
|
19
|
+
* this function returns alongside the bundled bytes, and cross-checks the
|
|
20
|
+
* manifest's `sourceHash` against the source it received.
|
|
10
21
|
*/
|
|
22
|
+
import type { WorkflowMemoryConfig } from "../types/workflow.js";
|
|
11
23
|
import type { SandboxNetworkPolicy } from "../sandbox.js";
|
|
24
|
+
import { type WorkflowPlan } from "../types/workflow-plan.js";
|
|
25
|
+
/** Bumped when the manifest contract changes in a way the server should
|
|
26
|
+
* notice. The server pins its manifest schema to this exact value (no
|
|
27
|
+
* `>=` accepted) so a manifest forged with a future version is refused
|
|
28
|
+
* by zod before any other validation runs. */
|
|
29
|
+
export declare const BUNDLER_VERSION = 1;
|
|
30
|
+
/**
|
|
31
|
+
* Manifest emitted alongside the bundled source. Fully serialisable JSON;
|
|
32
|
+
* the server validates its shape with zod and never inspects the source
|
|
33
|
+
* bytes themselves. Manifest is bound to source via `sourceHash` so the
|
|
34
|
+
* server can refuse manifests detached from the source they describe.
|
|
35
|
+
*/
|
|
36
|
+
export interface WorkflowManifest {
|
|
37
|
+
/** Always `true`. The bundler stamps this only after both validation
|
|
38
|
+
* gates pass; the server's zod schema pins it as `z.literal(true)`. */
|
|
39
|
+
definitionBrand: true;
|
|
40
|
+
/** sha256 hex digest of `source`. The server cross-checks this against
|
|
41
|
+
* `hashSource(body.source)` to bind the manifest to the bytes it describes. */
|
|
42
|
+
sourceHash: string;
|
|
43
|
+
/** Bundler contract version. See `BUNDLER_VERSION`. */
|
|
44
|
+
bundlerVersion: number;
|
|
45
|
+
}
|
|
46
|
+
export declare class WorkflowSourceValidationError extends Error {
|
|
47
|
+
constructor(message: string);
|
|
48
|
+
}
|
|
12
49
|
export interface BundledWorkflow {
|
|
13
50
|
source: string;
|
|
51
|
+
manifest: WorkflowManifest;
|
|
14
52
|
networkPolicy?: SandboxNetworkPolicy;
|
|
15
53
|
placeholders?: Record<string, string>;
|
|
16
54
|
/** Run UUID, workflow name, or `name@version` referencing the snapshot
|
|
@@ -19,7 +57,28 @@ export interface BundledWorkflow {
|
|
|
19
57
|
snapshot?: string;
|
|
20
58
|
/** Default capture-on-success flag from the workflow definition. */
|
|
21
59
|
saveSnapshot?: boolean;
|
|
60
|
+
workflowPlan: WorkflowPlan;
|
|
61
|
+
/** Workflow Memory extractor config. */
|
|
62
|
+
memory?: WorkflowMemoryConfig;
|
|
22
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Parse the bundled source and assert the default export is a CallExpression
|
|
66
|
+
* to an identifier named `defineWorkflow` (or to a `.step(...).build()` chain
|
|
67
|
+
* rooted in one). This catches:
|
|
68
|
+
*
|
|
69
|
+
* - Raw TypeScript shipped to the registry (parse fails on type syntax)
|
|
70
|
+
* - `export default async function run(ctx)` (default is not a call)
|
|
71
|
+
* - `export default { run }` etc. (default is not a call)
|
|
72
|
+
* - `export default someFunction` (default is not a defineWorkflow call)
|
|
73
|
+
*
|
|
74
|
+
* The check is intentionally syntactic — it doesn't follow imports or trace
|
|
75
|
+
* through aliases. The runtime brand check (`isWorkflow`) picks up edge
|
|
76
|
+
* cases the AST can't see, so the two layers compose.
|
|
77
|
+
*
|
|
78
|
+
* Exported so the unit tests can exercise this exact code path; do NOT call
|
|
79
|
+
* from outside the SDK — `bundleWorkflow` is the supported entrypoint.
|
|
80
|
+
*/
|
|
81
|
+
export declare function assertDefaultExportIsDefineWorkflow(source: string, label: string): void;
|
|
23
82
|
/** Bundle a workflow from source. */
|
|
24
83
|
export declare function bundleWorkflow(workflowPath: string, overrides?: {
|
|
25
84
|
networkPolicy?: SandboxNetworkPolicy;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { defineStep } from "./step.js";
|
|
2
|
+
export type { DefineStepOpts } from "./step.js";
|
|
3
|
+
export { createStepWorkflow, isWorkflow } from "./workflow.js";
|
|
4
|
+
export type { StepWorkflowDefinition, WorkflowBuilder } from "./workflow.js";
|
|
5
|
+
export { runWorkflowSteps, runWorkflowSingleStep, StepValidationError, WorkflowInputValidationError, WorkflowOutputValidationError, } from "./runner.js";
|
|
6
|
+
export type { RunWorkflowStepsOpts, RunWorkflowStepsResult, RunWorkflowSingleStepOpts, RunWorkflowSingleStepResult, } from "./runner.js";
|
|
7
|
+
export type { Step, StepContext, StepRunResult, Workflow, } from "./types.js";
|
|
8
|
+
export { WORKFLOW_BRAND } from "./types.js";
|
|
9
|
+
export { StepObservabilityCollector } from "./observability.js";
|
|
10
|
+
export type { StepObservability, SubStepEvent } from "./observability.js";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-step observability collector — buffers metadata writes, agent
|
|
3
|
+
* lifecycle events, and `ctx.step("name", fn)` sub-step records during one
|
|
4
|
+
* step's execution. The runner flushes the snapshot through the
|
|
5
|
+
* tokenised stdout sentinel; the activity persists the snapshot to the
|
|
6
|
+
* run's metadata + lifecycle event tables.
|
|
7
|
+
*
|
|
8
|
+
* STREAMING — known gap. `agentEvents` are currently batched at step
|
|
9
|
+
* end. For long-running steps that emit many agent events, this hides
|
|
10
|
+
* progress from the dashboard until the step completes. The follow-up
|
|
11
|
+
* is to bring back a minimal `/internal/runs/:runId/steps/:stepIndex/events`
|
|
12
|
+
* route gated by a per-step JIT-signed token (mint in the activity,
|
|
13
|
+
* stamp into the runner env, verify on receive) so `agentEvents.emit`
|
|
14
|
+
* POSTs in real time. Until then `metadata` and `subSteps` are
|
|
15
|
+
* effectively boundary events (no streaming need) and batching them
|
|
16
|
+
* matches their natural granularity.
|
|
17
|
+
*/
|
|
18
|
+
import type { AgentLifecycleEvent } from "../agent/agent-loop.js";
|
|
19
|
+
import type { AgentEventSink } from "../types/workflow.js";
|
|
20
|
+
/** One named sub-step (from `ctx.step("name", async () => ...)`).
|
|
21
|
+
* Becomes a `workflow_substep_*` lifecycle event on the run timeline. */
|
|
22
|
+
export interface SubStepEvent {
|
|
23
|
+
name: string;
|
|
24
|
+
startedAt: number;
|
|
25
|
+
durationMs: number;
|
|
26
|
+
status: "completed" | "failed";
|
|
27
|
+
/** Present when status="failed" — the user error's message. */
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Snapshot of everything the step's observability hooks recorded. All
|
|
31
|
+
* fields are optional so a step that uses none of the hooks produces an
|
|
32
|
+
* empty snapshot (and the wire payload omits the field entirely). */
|
|
33
|
+
export interface StepObservability {
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
events?: AgentLifecycleEvent[];
|
|
36
|
+
subSteps?: SubStepEvent[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Append-only collector bound to a single step's `StepContext`. The
|
|
40
|
+
* step's `setMetadata` / `step` / `agentEvents` properties all point at
|
|
41
|
+
* this instance. After the step finishes, the engine calls `snapshot()`
|
|
42
|
+
* to extract the bundle for transport.
|
|
43
|
+
*
|
|
44
|
+
* Metadata writes merge (later keys win) — matches the legacy
|
|
45
|
+
* `mergeRunMetadata` semantics so authors don't see a behaviour change
|
|
46
|
+
* across migrations.
|
|
47
|
+
*/
|
|
48
|
+
export declare class StepObservabilityCollector {
|
|
49
|
+
private metadata;
|
|
50
|
+
private events;
|
|
51
|
+
private subSteps;
|
|
52
|
+
readonly setMetadata: (data: Record<string, unknown>) => Promise<void>;
|
|
53
|
+
readonly step: <T>(name: string, fn: () => Promise<T>) => Promise<T>;
|
|
54
|
+
readonly agentEvents: AgentEventSink;
|
|
55
|
+
/** Snapshot the accumulated state. Returns `undefined` when nothing
|
|
56
|
+
* was recorded so the wire payload can drop the field entirely. */
|
|
57
|
+
snapshot(): StepObservability | undefined;
|
|
58
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `runWorkflowSteps` — in-process step executor for a `Workflow`.
|
|
3
|
+
*
|
|
4
|
+
* Walks the step chain sequentially:
|
|
5
|
+
* 1. Validate workflow input against `workflow.input`.
|
|
6
|
+
* 2. For each step:
|
|
7
|
+
* a. Optionally check `getCachedOutput(stepIndex, step.name)` — if a
|
|
8
|
+
* previous run completed this step, skip and reuse its output.
|
|
9
|
+
* (Phase 1b uses this for crash recovery.)
|
|
10
|
+
* b. Validate current input against `step.input`.
|
|
11
|
+
* c. Call `step.run({ input, ... })`.
|
|
12
|
+
* d. Validate return value against `step.output`.
|
|
13
|
+
* e. Call `onStepCompleted(stepIndex, step.name, output, durationMs)`.
|
|
14
|
+
* f. Output becomes the next step's input.
|
|
15
|
+
* 3. Validate final output against `workflow.output`.
|
|
16
|
+
*
|
|
17
|
+
* Errors during any step bubble through `onStepFailed` and re-throw so the
|
|
18
|
+
* caller can decide whether to mark the run failed.
|
|
19
|
+
*
|
|
20
|
+
* The cache + completion hooks are injection points — a sandbox engine
|
|
21
|
+
* adapter (Phase 1c) wires them to `workflow_step_runs` rows. The default
|
|
22
|
+
* is an in-process map for tests.
|
|
23
|
+
*/
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
import type { Workflow, StepRunResult } from "./types.js";
|
|
26
|
+
import type { RequestContext } from "../request-context/request-context.js";
|
|
27
|
+
import type { SandboxProvider } from "../types/sandbox.js";
|
|
28
|
+
import type { WorkflowRun, WorkflowCtx } from "../types/workflow.js";
|
|
29
|
+
import { type StepObservability } from "./observability.js";
|
|
30
|
+
export declare class StepValidationError extends Error {
|
|
31
|
+
readonly stepName: string;
|
|
32
|
+
readonly side: "input" | "output";
|
|
33
|
+
readonly kind: "step-validation";
|
|
34
|
+
constructor(stepName: string, side: "input" | "output", cause: z.ZodError);
|
|
35
|
+
}
|
|
36
|
+
export declare class WorkflowInputValidationError extends Error {
|
|
37
|
+
readonly workflowId: string;
|
|
38
|
+
readonly kind: "workflow-input-validation";
|
|
39
|
+
constructor(workflowId: string, cause: z.ZodError);
|
|
40
|
+
}
|
|
41
|
+
export declare class WorkflowOutputValidationError extends Error {
|
|
42
|
+
readonly workflowId: string;
|
|
43
|
+
readonly kind: "workflow-output-validation";
|
|
44
|
+
constructor(workflowId: string, cause: z.ZodError);
|
|
45
|
+
}
|
|
46
|
+
export interface RunWorkflowStepsOpts<TInput, TOutput> {
|
|
47
|
+
workflow: Workflow<TInput, TOutput>;
|
|
48
|
+
input: TInput;
|
|
49
|
+
run: WorkflowRun;
|
|
50
|
+
requestContext: RequestContext;
|
|
51
|
+
sandbox?: SandboxProvider;
|
|
52
|
+
abortSignal?: AbortSignal;
|
|
53
|
+
/**
|
|
54
|
+
* Crash-recovery hook. Called before a step executes. Return the cached
|
|
55
|
+
* output to skip execution; return undefined to run the step.
|
|
56
|
+
*
|
|
57
|
+
* Phase 1b implementations will look up `workflow_step_runs` rows for
|
|
58
|
+
* (runId, stepIndex, stepName) and return completed step outputs here.
|
|
59
|
+
* Default: always undefined (no caching).
|
|
60
|
+
*/
|
|
61
|
+
getCachedOutput?(stepIndex: number, stepName: string): unknown | undefined | Promise<unknown | undefined>;
|
|
62
|
+
/** Fire after a step's `execute` and output validation succeed. */
|
|
63
|
+
onStepCompleted?(stepIndex: number, stepName: string, output: unknown, durationMs: number): void | Promise<void>;
|
|
64
|
+
/** Fire when a step throws or fails validation. The error is re-thrown after this returns. */
|
|
65
|
+
onStepFailed?(stepIndex: number, stepName: string, error: Error, durationMs: number): void | Promise<void>;
|
|
66
|
+
/** Fire before a step runs (after cache check / before input validation). */
|
|
67
|
+
onStepStarted?(stepIndex: number, stepName: string): void | Promise<void>;
|
|
68
|
+
/** Child workflow invocation implementation. Defaults to a clear unsupported error. */
|
|
69
|
+
invokeChild?: WorkflowCtx["invokeChild"];
|
|
70
|
+
}
|
|
71
|
+
export interface RunWorkflowStepsResult<TOutput> {
|
|
72
|
+
output: TOutput;
|
|
73
|
+
/** Per-step results in order — useful for tests and lifecycle event emission. */
|
|
74
|
+
steps: ReadonlyArray<{
|
|
75
|
+
name: string;
|
|
76
|
+
} & StepRunResult>;
|
|
77
|
+
}
|
|
78
|
+
export interface RunWorkflowSingleStepOpts {
|
|
79
|
+
workflow: Workflow<unknown, unknown>;
|
|
80
|
+
stepIndex: number;
|
|
81
|
+
input: unknown;
|
|
82
|
+
run: WorkflowRun;
|
|
83
|
+
requestContext: RequestContext;
|
|
84
|
+
sandbox?: SandboxProvider;
|
|
85
|
+
abortSignal?: AbortSignal;
|
|
86
|
+
invokeChild?: WorkflowCtx["invokeChild"];
|
|
87
|
+
}
|
|
88
|
+
/** Result of one step run — output plus whatever the step's observability
|
|
89
|
+
* hooks recorded. `observability` is undefined when nothing was buffered,
|
|
90
|
+
* so callers can drop it from the wire payload entirely. */
|
|
91
|
+
export interface RunWorkflowSingleStepResult {
|
|
92
|
+
output: unknown;
|
|
93
|
+
observability?: StepObservability;
|
|
94
|
+
}
|
|
95
|
+
export declare function runWorkflowSingleStep(opts: RunWorkflowSingleStepOpts): Promise<RunWorkflowSingleStepResult>;
|
|
96
|
+
export declare function runWorkflowSteps<TInput, TOutput>(opts: RunWorkflowStepsOpts<TInput, TOutput>): Promise<RunWorkflowStepsResult<TOutput>>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineStep` — typed factory for one workflow step.
|
|
3
|
+
*
|
|
4
|
+
* Required:
|
|
5
|
+
* - `name` stable identifier (also the step row key)
|
|
6
|
+
* - `input` Zod schema; validated before `run` executes
|
|
7
|
+
* - `output` Zod schema; validated against `run`'s return value
|
|
8
|
+
* - `run` step body
|
|
9
|
+
*
|
|
10
|
+
* Validation is required (not optional) because the durability story rides
|
|
11
|
+
* on every step boundary being a recordable, replayable JSON value. A step
|
|
12
|
+
* without a schema is invisible to the engine's persistence layer.
|
|
13
|
+
*
|
|
14
|
+
* Type inference flows: `defineStep` infers TInput/TOutput from the schemas
|
|
15
|
+
* so `run(ctx)` is fully typed via `ctx.input` at the call site.
|
|
16
|
+
*/
|
|
17
|
+
import type { z } from "zod";
|
|
18
|
+
import type { Step, StepContext } from "./types.js";
|
|
19
|
+
export interface DefineStepOpts<TInput, TOutput> {
|
|
20
|
+
name: string;
|
|
21
|
+
input: z.ZodType<TInput>;
|
|
22
|
+
output: z.ZodType<TOutput>;
|
|
23
|
+
run(ctx: StepContext<TInput>): TOutput | Promise<TOutput>;
|
|
24
|
+
}
|
|
25
|
+
export declare function defineStep<TInput, TOutput>(opts: DefineStepOpts<TInput, TOutput>): Step<TInput, TOutput>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the declarative workflow shape.
|
|
3
|
+
*
|
|
4
|
+
* Workflows-as-data — every workflow is a list of typed steps with explicit
|
|
5
|
+
* input/output Zod schemas. Mastra's shape, adapted for our runtime.
|
|
6
|
+
*
|
|
7
|
+
* Why: durable suspend/resume requires step boundaries to be addressable as
|
|
8
|
+
* data, not opaque async-function bodies. Each step's input + output is
|
|
9
|
+
* serialisable JSON so engine adapters (in-process, sandbox, future
|
|
10
|
+
* Inngest/Temporal) can record completion and replay from the last
|
|
11
|
+
* completed step.
|
|
12
|
+
*/
|
|
13
|
+
import type { z } from "zod";
|
|
14
|
+
import type { BaseExecutionContext } from "../types/execution-context.js";
|
|
15
|
+
import type { AgentEventSink } from "../types/workflow.js";
|
|
16
|
+
import type { WorkflowMetadata } from "../types/workflow-metadata.js";
|
|
17
|
+
/**
|
|
18
|
+
* Per-step execution context. Threaded into every step's `execute(...)` so
|
|
19
|
+
* the step can read tenant identity and run identity, log progress, and
|
|
20
|
+
* invoke sandbox commands.
|
|
21
|
+
*
|
|
22
|
+
* Sandbox is optional because in-process tests run steps without a sandbox.
|
|
23
|
+
*/
|
|
24
|
+
export interface StepContext<TInput = unknown> extends BaseExecutionContext {
|
|
25
|
+
/** Validated step input — already parsed against the step's `input` schema. */
|
|
26
|
+
input: TInput;
|
|
27
|
+
/** Step name — useful for logging. */
|
|
28
|
+
stepName: string;
|
|
29
|
+
/** AbortSignal that fires on workflow cancellation. */
|
|
30
|
+
abortSignal: AbortSignal;
|
|
31
|
+
/**
|
|
32
|
+
* Merge key-value metadata onto the run record. Buffered during the
|
|
33
|
+
* step and flushed when the step completes; the durable engine writes
|
|
34
|
+
* it via the same `mergeRunMetadata` path the legacy runner used, so
|
|
35
|
+
* the dashboard sees the same shape. Later keys win.
|
|
36
|
+
*/
|
|
37
|
+
setMetadata(data: Record<string, unknown>): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Wrap a named sub-step for observability. Emits
|
|
40
|
+
* `workflow_substep_started` / `workflow_substep_completed` /
|
|
41
|
+
* `workflow_substep_failed` lifecycle events on the run timeline with
|
|
42
|
+
* the measured `durationMs`. Use for long sub-phases inside one step
|
|
43
|
+
* (setup, external API call, submit).
|
|
44
|
+
*
|
|
45
|
+
* The block runs even if observability flushing fails — instrumentation
|
|
46
|
+
* never breaks the workflow.
|
|
47
|
+
*/
|
|
48
|
+
step<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Sink for `agent({ events: ctx.agentEvents })`. Each emitted
|
|
51
|
+
* `AgentLifecycleEvent` is buffered and flushed at step end, then
|
|
52
|
+
* fanned out to the run's SSE stream. Mid-step the events are not
|
|
53
|
+
* yet visible to consumers — that's a deliberate tradeoff for the
|
|
54
|
+
* step-mode, /internal/*-free runner.
|
|
55
|
+
*/
|
|
56
|
+
agentEvents: AgentEventSink;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Step definition — a single typed unit of work in a workflow chain.
|
|
60
|
+
*
|
|
61
|
+
* Both `input` and `output` are required: the workflow shape's value
|
|
62
|
+
* comes from every step boundary being recordable. Optional schemas
|
|
63
|
+
* would defeat the durability story.
|
|
64
|
+
*
|
|
65
|
+
* `run` may be sync or async; the engine awaits it uniformly.
|
|
66
|
+
*/
|
|
67
|
+
export interface Step<TInput, TOutput> {
|
|
68
|
+
/** Stable identifier — used as the step row key and for logs/metrics. */
|
|
69
|
+
readonly name: string;
|
|
70
|
+
/** Zod schema validated against the step's input before `run`. */
|
|
71
|
+
readonly input: z.ZodType<TInput>;
|
|
72
|
+
/** Zod schema validated against `run`'s return value. */
|
|
73
|
+
readonly output: z.ZodType<TOutput>;
|
|
74
|
+
/** Step body. Receives a `StepContext<TInput>` and returns the typed output. */
|
|
75
|
+
run(ctx: StepContext<TInput>): TOutput | Promise<TOutput>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The result of running one step. Engine adapters persist these into the
|
|
79
|
+
* `workflow_step_runs` table (Phase 1b) so subsequent runs can skip
|
|
80
|
+
* completed steps. `observability` carries the snapshot of
|
|
81
|
+
* `ctx.setMetadata` / `ctx.step` / `ctx.agentEvents` recorded during
|
|
82
|
+
* the step; undefined when no hooks were used.
|
|
83
|
+
*/
|
|
84
|
+
export type StepRunResult<TOutput = unknown> = {
|
|
85
|
+
status: "completed";
|
|
86
|
+
output: TOutput;
|
|
87
|
+
durationMs: number;
|
|
88
|
+
observability?: import("./observability.js").StepObservability;
|
|
89
|
+
} | {
|
|
90
|
+
status: "failed";
|
|
91
|
+
error: string;
|
|
92
|
+
durationMs: number;
|
|
93
|
+
observability?: import("./observability.js").StepObservability;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Workflow — a list of typed steps plus the workflow's input/output
|
|
97
|
+
* schemas plus its server-side metadata bag. Returned by `defineWorkflow(...)`
|
|
98
|
+
* (run form) and `defineWorkflow(...).step(...)...build()` (step form).
|
|
99
|
+
* Engine adapters consume this shape.
|
|
100
|
+
*
|
|
101
|
+
* `input` validates the workflow input before the first step runs.
|
|
102
|
+
* `output` validates the final step's output before the workflow
|
|
103
|
+
* completes successfully.
|
|
104
|
+
*
|
|
105
|
+
* `metadata` carries server-readable declarations — network policy,
|
|
106
|
+
* brokered-secret placeholders, snapshot reference/capture defaults,
|
|
107
|
+
* workflow-level processor chain. The bundler reads these at registration
|
|
108
|
+
* time. Always present, possibly empty.
|
|
109
|
+
*
|
|
110
|
+
* Historically named `CompiledWorkflow` back when an uncompiled form
|
|
111
|
+
* existed (a bare `WorkflowFn` was its own engine input). After the
|
|
112
|
+
* legacy delete in #55 every workflow goes through this shape, so the
|
|
113
|
+
* "Compiled" prefix carried no information — it's just `Workflow` now.
|
|
114
|
+
*/
|
|
115
|
+
export interface Workflow<TInput, TOutput> {
|
|
116
|
+
/** Stable workflow identifier. */
|
|
117
|
+
readonly id: string;
|
|
118
|
+
/** Validates the initial workflow input. */
|
|
119
|
+
readonly input: z.ZodType<TInput>;
|
|
120
|
+
/** Validates the final step's output. */
|
|
121
|
+
readonly output: z.ZodType<TOutput>;
|
|
122
|
+
/** Sequential list of steps to execute. Type chain proven at compile time. */
|
|
123
|
+
readonly steps: ReadonlyArray<Step<any, any>>;
|
|
124
|
+
/** Server-readable declarations: network policy, placeholders, snapshot
|
|
125
|
+
* defaults, processors. Always set; empty object when nothing declared. */
|
|
126
|
+
readonly metadata: WorkflowMetadata;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Discriminator brand stamped on every `Workflow` so `isWorkflow` can
|
|
130
|
+
* distinguish a real workflow from an arbitrary default export at
|
|
131
|
+
* registration / dispatch time. Workflow authors never see this —
|
|
132
|
+
* `defineWorkflow` and `defineWorkflow(...).step(...).build()` set it
|
|
133
|
+
* internally.
|
|
134
|
+
*/
|
|
135
|
+
export declare const WORKFLOW_BRAND: unique symbol;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createStepWorkflow` — internal typed builder for a step-based workflow.
|
|
3
|
+
*
|
|
4
|
+
* const wf = defineWorkflow({
|
|
5
|
+
* id: "my-workflow",
|
|
6
|
+
* input: z.object({ url: z.string().url() }),
|
|
7
|
+
* output: z.object({ pageTitle: z.string() }),
|
|
8
|
+
* })
|
|
9
|
+
* .step(fetchStep)
|
|
10
|
+
* .step(parseStep)
|
|
11
|
+
* .build();
|
|
12
|
+
*
|
|
13
|
+
* The chain enforces that each step's input matches the previous step's
|
|
14
|
+
* output at compile time. `.build()` returns a `Workflow` and verifies
|
|
15
|
+
* at construction time that the workflow's declared `output` schema
|
|
16
|
+
* matches the final step's `output` schema.
|
|
17
|
+
*
|
|
18
|
+
* Workflows-as-data — the result is consumable by any engine adapter
|
|
19
|
+
* (in-process today; sandbox + Inngest/Temporal in future PRs).
|
|
20
|
+
*/
|
|
21
|
+
import type { z } from "zod";
|
|
22
|
+
import type { Step, Workflow } from "./types.js";
|
|
23
|
+
import type { SandboxNetworkPolicy } from "../sandbox.js";
|
|
24
|
+
import type { Processor } from "../processors/processor.js";
|
|
25
|
+
export interface WorkflowBuilder<TInput, TCurrent> {
|
|
26
|
+
/** Append a step whose input matches the current builder output. */
|
|
27
|
+
step<TOutput>(step: Step<TCurrent, TOutput>): WorkflowBuilder<TInput, TOutput>;
|
|
28
|
+
/**
|
|
29
|
+
* Freeze the chain into a `Workflow`. Throws when no steps have been
|
|
30
|
+
* added or when the final step's `output` schema is not the same Zod
|
|
31
|
+
* schema instance as the workflow's declared `output` schema.
|
|
32
|
+
*
|
|
33
|
+
* The schema-identity check catches the "I edited one of two schemas"
|
|
34
|
+
* footgun that's easy to introduce when output types drift.
|
|
35
|
+
*/
|
|
36
|
+
build(): Workflow<TInput, TCurrent>;
|
|
37
|
+
}
|
|
38
|
+
export interface StepWorkflowDefinition<TInput, TOutput> {
|
|
39
|
+
id: string;
|
|
40
|
+
input: z.ZodType<TInput>;
|
|
41
|
+
output: z.ZodType<TOutput>;
|
|
42
|
+
networkPolicy?: SandboxNetworkPolicy;
|
|
43
|
+
placeholders?: Record<string, string>;
|
|
44
|
+
snapshot?: string;
|
|
45
|
+
saveSnapshot?: boolean;
|
|
46
|
+
processors?: readonly Processor[];
|
|
47
|
+
}
|
|
48
|
+
export declare function createStepWorkflow<TInput, TOutput>(opts: StepWorkflowDefinition<TInput, TOutput>): WorkflowBuilder<TInput, TInput>;
|
|
49
|
+
/** Type guard — true when `value` is a `Workflow`. */
|
|
50
|
+
export declare function isWorkflow(value: unknown): value is Workflow<unknown, unknown>;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workflow engine —
|
|
2
|
+
* Workflow engine — drives a `Workflow`'s steps.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `AgentComposeClient.invoke[AndWait](...)`. The engine's only job now is:
|
|
4
|
+
* Single execution entry point: `runWorkflow`. Every workflow has the same
|
|
5
|
+
* shape (defineWorkflow always returns a `Workflow`), so there's one
|
|
6
|
+
* runner.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - classify errors into `WorkflowError` vs `EngineError` for the runner
|
|
12
|
-
* harness to emit on /fail
|
|
8
|
+
* Errors classified into `WorkflowError` (user code threw) vs `EngineError`
|
|
9
|
+
* (platform problem) for the runner harness to surface upstream.
|
|
13
10
|
*/
|
|
14
|
-
import type { WorkflowHooks, WorkflowCtx
|
|
11
|
+
import type { WorkflowHooks, WorkflowCtx } from "../types/workflow.js";
|
|
12
|
+
import { RequestContext } from "../request-context/request-context.js";
|
|
13
|
+
import type { Workflow } from "../workflow-steps/types.js";
|
|
14
|
+
import { type RunWorkflowStepsOpts } from "../workflow-steps/runner.js";
|
|
15
15
|
/**
|
|
16
16
|
* Thrown from `runWorkflow` when the authored workflow threw.
|
|
17
17
|
* User-level; not a bug in the platform.
|
|
@@ -44,7 +44,21 @@ export interface WorkflowResult<T = unknown> {
|
|
|
44
44
|
/** The value returned by the workflow function. `null` for void workflows. */
|
|
45
45
|
response: T | null;
|
|
46
46
|
}
|
|
47
|
-
export
|
|
47
|
+
export interface RunWorkflowOptions {
|
|
48
48
|
hooks?: WorkflowHooks;
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
requestContext?: RequestContext;
|
|
50
|
+
getCachedOutput?: RunWorkflowStepsOpts<unknown, unknown>["getCachedOutput"];
|
|
51
|
+
onStepStarted?: RunWorkflowStepsOpts<unknown, unknown>["onStepStarted"];
|
|
52
|
+
onStepCompleted?: RunWorkflowStepsOpts<unknown, unknown>["onStepCompleted"];
|
|
53
|
+
onStepFailed?: RunWorkflowStepsOpts<unknown, unknown>["onStepFailed"];
|
|
54
|
+
/** Provider-specific child workflow invocation. Temporal/Inngest providers
|
|
55
|
+
* inject their native child-workflow primitive; the LocalProvider injects
|
|
56
|
+
* the public Agent Compose API client. */
|
|
57
|
+
invokeChild?: WorkflowCtx["invokeChild"];
|
|
58
|
+
}
|
|
59
|
+
export declare function runWorkflow<TInput, TOutput>(wf: Workflow<TInput, TOutput>, ctx: {
|
|
60
|
+
run: {
|
|
61
|
+
id: string;
|
|
62
|
+
};
|
|
63
|
+
input?: TInput;
|
|
64
|
+
}, opts?: RunWorkflowOptions): Promise<WorkflowResult<TOutput>>;
|