> ## Documentation Index
> Fetch the complete documentation index at: https://obversa.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Runner

> @obversa/runner: start a stored graph in a worker under a watchdog, read its progress, and resume a recorded pause.

`@obversa/runner` starts a stored graph in a worker process and makes the
calling process its watchdog: it checks the engines before the first step,
restarts the worker after a crash within the limits you set, and resumes a
paused run from its exact record. The guide is
[Supervised local runs](/docs/driving/runner); this page is the surface.

## Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @obversa/runner
  ```

  ```bash pnpm theme={null}
  pnpm add @obversa/runner
  ```
</CodeGroup>

Included in `@obversa/obversa`.

## Quickstart

Start one graph under the watchdog and wait for its result:

```ts examples/preflight-supervised-run.ts (excerpt) {1,4,13-15} theme={null}
  handle = await startSupervisedRun({
    directory: runnerDirectory,
    runRoot,
    module: './host.mjs',
    storage,
    workspace,
    definition: {
      runId,
      graphDefinition: graph.definition,
      resolvedPlan,
      resolvedInputs: { controlFile, callsFile },
    },
    limits: { timeoutMs: 20_000, maxDispatches: 1 },
    restart,
    teardownGraceMs: 100,
  });
```

`startSupervisedRun` returns a `SupervisedRunHandle` with `done`,
`status()` and `stop()`. `done` resolves to a `SupervisedRunResult`:
`complete`, `pause` or `fail`. The host module named by `module` exports
`bindRun`, which returns the compiled graph and the node and engine
bindings; `examples/preflight-host.mjs` is one.

## Read progress

`readSupervisedRunStatus` reads a run's recorded progress from any process
with the same storage settings, without taking ownership of the run:

```ts examples/supervised-run.ts (excerpt) {3-5} theme={null}
  const result = await handle.done;
  assert.equal(result.kind, 'complete', JSON.stringify(result));
  const status = await readSupervisedRunStatus({ storage, runId: 'example' });
  assert.equal(status.phase, 'completed');
  assert.equal(status.workerAlive, false);
  assert.deepEqual(status.active, []);
```

A `SupervisedRunStatus` carries the run phase, worker liveness, inspected
processes, the restart count, backoff, elapsed and remaining time, and
pause reasons. `cleanupVerified` is `null` before a terminal result, `true`
when cleanup was verified, `false` when it couldn't be; `leaseRetained`
reports a lease still held after a terminal failure. Read both before
treating the workspace as released.

## Resume a pause

`resumeSupervisedRun` reopens one recorded pause. For a preflight pause,
pass the `preflightEventId`; for a node pause, the exact `position`:

```ts examples/preflight-supervised-run.ts (excerpt) {1,9} theme={null}
  handle = await resumeSupervisedRun({
    directory: runnerDirectory,
    runRoot,
    storage,
    workspace,
    restart,
    teardownGraceMs: 100,
    runId,
    preflightEventId: paused.preflightEventId,
  });
```

The definition, host module and limits come from the stored run. Resume
writes no second start event and repeats no finished occurrence.

## Options

### `limits` and `restart`

`limits.timeoutMs` covers worker execution and restart backoff across
pauses and resumes; it counts from the stored run timestamp, freezes at each
settled pause, and resumes at the next launch. `limits.maxDispatches`
counts recorded dispatches across workers. `restart.maxRestarts` caps
replacement workers, with backoff from `initialBackoffMs` to
`maxBackoffMs`. A run over a limit ends with `fail`.

### `module` and `environmentVariables`

`module` is a relative specifier inside `runRoot`; a path outside it after
symlinks resolve is refused with `HOST_MODULE`, and changed bytes between
imports fail with `HOST_MODULE_CHANGED`. The worker inherits only `PATH`,
`HOME`, `TMPDIR`, `TMP`, `TEMP`, `SystemRoot`, `USERPROFILE` and `PATHEXT`;
list any other variable name in `environmentVariables` and the runner
copies its value when set, storing neither name nor value.

### The rest

| Field | Type | Default | Description |
| - | - | - | - |
| `directory` | `string` | required | The runner's working directory. |
| `runRoot` | `string` | required | The root the host module must live under. |
| `storage` | `LocalRunStorageOptions` | required | The on-disk store the run definition and events go to. |
| `workspace` | `WorkspaceProvider` | required | The provider whose lease the watchdog holds. |
| `definition` | run id, graph definition, resolved plan, resolved inputs | required | The stored definition, minus the fields the runner persists itself. |
| `teardownGraceMs` | `number` | required | Time processes get to stop before they are killed. |
| `runId`, `position` | `string` | required on resume | Which pause to reopen. `preflightEventId` replaces `position` for a preflight pause. |

## Errors

* **`SupervisedRunError`** is the runner's own error class. The codes on a
  `fail` result: `STOPPED`, `PREFLIGHT_FAILED`, `OUTPUT_LIMIT`,
  `TERMINAL_ARTIFACT`, `RUN_STORAGE`, `PROCESS_LOCKED`, `HOST_MODULE`,
  `HOST_MODULE_CHANGED`, `DAG_NODE_FAILED`; on a `pause`: `PREFLIGHT_PAUSED`,
  `WORKSPACE_DRIFT`, `WORKSPACE_ANCHOR_MISSING`, `WORKSPACE_ANCHOR_INVALID`,
  `WORKSPACE_ANCHOR_WRITE`, `RESUME_EVENT_MISMATCH`. What each means is on
  [Supervised local runs](/docs/driving/runner#failure).

## API

* **`startSupervisedRun(options)`**: start a worker under the calling
  process's supervision. [Quickstart](#quickstart).
* **`resumeSupervisedRun(options)`**: reopen one recorded pause.
  [Resume a pause](#resume-a-pause).
* **`readSupervisedRunStatus({ storage, runId })`**: read recorded progress
  from another process. [Read progress](#read-progress).
* **`SupervisedRunError`**: the error class. [Errors](#errors).
* **Types**: `SupervisedRunOptions`, `ResumeSupervisedRunOptions`,
  `ResumePreflightSupervisedRunOptions`, `ReadSupervisedRunStatusOptions`,
  `SupervisedRunHandle`, `SupervisedRunResult`, `SupervisedRunStatus`,
  `SupervisedRunUsage`, `SupervisedRunBindings`, `SupervisedHostContext`.

## Next steps

* [Supervised local runs](/docs/driving/runner): the guide, with the whole
  example and its output.
* [The record](/docs/concepts/record): what a restarted run skips and repeats.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.