@sapiom/orchestration
v0.4.2
Published
Versioned public contract for authoring Sapiom orchestrations: types, directive constructors/guards, defineOrchestration, defineStep, InMemoryContextStore. Shared by customer orchestration definitions, the sandbox step-runner, and the engine.
Readme
@sapiom/orchestration
The versioned public contract for authoring Sapiom orchestrations.
A lean, dependency-light package (types + a small protocol runtime) shared by three consumers:
- Customer orchestration definitions — authored against this package's types and compiled by the build.
- The sandbox step-runner — reads a step's input, builds
ctx, runs one step. - The engine — uses the directive guards + manifest schema; it never runs customer code, only validates the pure-data completion payload.
Install
npm install @sapiom/orchestrationAuthoring surface
import {
defineOrchestration,
defineStep,
goto,
terminate,
} from "@sapiom/orchestration";
const start = defineStep({
name: "start",
next: ["finish"],
async run(input, ctx) {
return goto("finish", { greeting: `hello ${input.name}` });
},
});
const finish = defineStep({
name: "finish",
next: [],
terminal: true,
async run() {
return terminate({ done: true });
},
});
export const hello = defineOrchestration({
name: "hello",
entry: "start",
steps: { start, finish },
});A step declares the transitions it may take (next / terminal / canFail /
pause); the run return type is derived from those declarations, so an
undeclared transition is a compile error. The build reads those same declarations
to render the orchestration graph without executing anything.
Pausing on a long-running capability
Some ctx.sapiom capabilities are dispatched: you launch them, they run far
past one step's budget, and they report back when they finish (a coding agent
today; more below). A step can't inline-await one — it pauses, and a later step
resumes with the result. pauseUntilSignal accepts the launch handle (or the
launch promise itself) and reads everything it needs off it:
import { defineStep, pauseUntilSignal, terminate } from "@sapiom/orchestration";
import { CODING_RESULT_SIGNAL } from "@sapiom/tools";
const code = defineStep({
name: "code",
next: ["review"],
// capability's exported signal constant so the decl can't drift from the handle.
pause: { signal: CODING_RESULT_SIGNAL, resumeStep: "review" },
async run(input, ctx) {
// launch returns immediately; hand it straight to pauseUntilSignal. The run
// parks at status='paused' and the dispatch loop exits.
return pauseUntilSignal(
ctx.sapiom.agent.coding.launch({ task: input.task }),
{
resumeStep: "review",
},
);
},
});
const review = defineStep({
name: "review",
next: [],
terminal: true,
// Give this step an `inputSchema` (a zod schema for the capability's result
// shape) to type + validate what it receives.
async run(result, ctx) {
// Fires on success OR failure — branch on the terminal result.
return terminate({ ok: result.status === "completed" });
},
});Things to know:
The pause is a real suspend across processes, so the launch and the resume are two steps — you can't fold them into one inline
await.The resumed step receives the capability's result as its input. Declare its
inputSchemato type + validate it (each capability documents its result shape).Pass the launch promise directly for the one-liner above, or
awaitit first when you need the handle — to stash the run id inctx.shared, or totry/catcha launch failure and route somewhere other than a retry. Awaiting doesn't lose the pause; the resolved handle still flows intopauseUntilSignal:async run(input, ctx) { const run = await ctx.sapiom.agent.coding.launch({ task: input.task }); ctx.shared.set("codingRunId", run.runId); // readable from the resumed step return pauseUntilSignal(run, { resumeStep: "review" }); }Outside a workflow nothing changes —
await launch().wait()the capability as usual; the pause wiring only engages when a step pauses on the handle.
Compatible capabilities
Any capability whose launch returns a DispatchHandle (a dispatch member) is
pausable; each ships a stable result-signal constant for the pause decl. This
list grows as capabilities land:
| Capability | Launch | Pause signal |
| ------------ | ----------------------------------- | ---------------------------------------- |
| Coding agent | ctx.sapiom.agent.coding.launch(…) | CODING_RESULT_SIGNAL (@sapiom/tools) |
