---
title: runWorkflow
description: Programmatic entry point. Equivalent to `bunx smithers-orchestrator up`.
---

```ts
import { runWorkflow } from "smithers-orchestrator";
import { Effect } from "effect";

const result = await Effect.runPromise(runWorkflow(workflow, {
  input: { task: "fix bug" },
}));

result.runId;     // string
result.status;    // "finished" | "failed" | "cancelled" | "continued" | "waiting-approval" | "waiting-event" | "waiting-timer" | "waiting-quota" | "paused"
result.output;    // populated only if your schema has a key literally named `output`
result.error;     // serialized SmithersError on failure
```

Signature:

```ts
function runWorkflow<Schema>(
  workflow: SmithersWorkflow<Schema>,
  opts: RunOptions,                 // see Types
): Effect.Effect<RunResult, SmithersError>;
```

Both `RunOptions` and `RunResult` are defined in [Types](/reference/types).

## Resume

Pass the original `runId` plus `resume: true`. State loads from the configured durable store, completed tasks are skipped, in-progress attempts older than 15 minutes are abandoned and retried.

```ts
await Effect.runPromise(runWorkflow(workflow, { input: {}, runId: "my-run-123", resume: true }));
```

The original input row is loaded from durable state; pass `{}` for `input`. The workflow file hash and VCS root must match the original run.

## Cancel via AbortSignal

```ts
const controller = new AbortController();
setTimeout(() => controller.abort(), 5 * 60 * 1000);

const result = await Effect.runPromise(runWorkflow(workflow, { input: {...}, signal: controller.signal }));
// result.status === "cancelled"
```

All in-flight attempts are marked cancelled and `NodeCancelled` events are emitted.

## Hijack handoff

If a CLI hijack happens mid-run (`bunx smithers-orchestrator hijack RUN_ID`), the run ends `"cancelled"` and the latest attempt metadata stores `hijackHandoff`. On `resume: true`, Smithers waits for a safe handoff point and continues with the persisted CLI session id (see [CLI Agents](/integrations/cli-agents)) or the persisted message history (SDK agents).

## `result.output`

Populated only when the schema passed to `createSmithers()` has a key literally named `output`. Inside a workflow, consume other typed rows through `ctx.output`, `ctx.latest`, or `ctx.outputs`. Outside the workflow, expose the run through the workspace Gateway and fetch a node's typed output through `getNodeOutput`:

```ts
import { SmithersGatewayClient } from "smithers-orchestrator/gateway-client";

const gateway = new SmithersGatewayClient({
  baseUrl: process.env.SMITHERS_GATEWAY_URL,
  token: process.env.SMITHERS_API_KEY,
});

const page = await gateway.getNodeOutput({
  runId: result.runId,
  nodeId: "render-page",
});
```

Controllers and monitors must not open SQLite/PGlite/Postgres or query output and
`_smithers_*` tables directly. The Gateway preserves auth, ownership,
invalidation, event emission, and backend independence. Direct storage access is
reserved for runtime implementation, migration, backend tests, and maintainer
diagnostics.

## Notes

- On macOS, `runWorkflow` acquires a `caffeinate` lock to prevent idle sleep and releases it on completion. On other platforms this is a no-op.
- Set `SMITHERS_LOG_LEVEL=debug` to enable verbose engine logging.
- For lifecycle events, pass `onProgress` (see [Events](/runtime/events)).
