Rulvar API reference / @rulvar/executor
@rulvar/executor
Isolated tool executors for Rulvar: the subprocess ToolExecutorProvider (fresh workdir per dispatch, replaced environment, timeout and output bounds) and the container adapter over the same seam, plus the executor conformance kit and the optional two-phase effect ledger (a durable intent BEFORE the external effect, the outcome after, so a crash leaves an orphan intent as the reconciliation signal instead of an untracked effect).
Part of Rulvar, an embeddable TypeScript engine for durable, budget-bounded multi-agent LLM workflows, where a completed LLM call is never paid for twice. Full documentation: docs.rulvar.com.
Install
pnpm add @rulvar/core @rulvar/executorWhat it is NOT
The executors isolate a dispatch; they are not a security sandbox by themselves (bring a container or OS boundary for hostile code), and the effect ledger is not a transactional outbox, not an authorization surface, and not exactly-once delivery: the host's reconciliation against provider receipts stays mandatory.
Documentation
License
Classes
| Class | Description |
|---|---|
| ExecutorError | A failed isolated dispatch. The engine catches whatever a ToolExecutorProvider throws and turns it into the call's error tool result, so message is what the model sees: it is kept concise and carries a stderr tail on exit. |
| LedgerCorruptionError | The fail-closed refusal of loadEffectLedger (RV502, widened by RV607): the file holds at least one line the scan cannot admit, unparseable bytes on an interior line, invalid UTF-8, a JSON value that is not an object, a missing or mistyped required field, or an unknown phase, none of which the writer's tail repair can produce, so it means external damage or a foreign writer, never a normal crash artifact. Reconciling from a partial scan would silently drop intents; triage the named lines instead (tolerateCorrupt: true surfaces them as data). |
Interfaces
| Interface | Description |
|---|---|
| ChildResult | - |
| ChildSpec | - |
| ConformanceExecutorConfig | The executor options the shared contract exercises. |
| ContainerExecutorOptions | - |
| CorruptLedgerLine | One malformed line of the ledger file, surfaced for triage. |
| EffectLedgerScan | What loadEffectLedger reads back from a JSONL ledger file. |
| ExecutorConformanceCheck | - |
| ExecutorConformanceSuite | - |
| ExecutorTestRegistrar | Structural subset of the Vitest/Jest registration API. |
| SubprocessCommandSpec | The command a subprocess tool runs, carried on its executorSpec. |
| SubprocessExecutorOptions | @rulvar/executor: isolated tool executors (RV-216). Reference ToolExecutorProvider adapters that run a tool's work OUT of the engine process, so a tool whose input is hostile or model-generated cannot reach host capabilities the way an in-process tool (an ordinary function call) can. |
| SubprocessToolInit | @rulvar/executor: isolated tool executors (RV-216). Reference ToolExecutorProvider adapters that run a tool's work OUT of the engine process, so a tool whose input is hostile or model-generated cannot reach host capabilities the way an in-process tool (an ordinary function call) can. |
| ToolEffectIntent | The pre-dispatch half of a two-phase ledger entry (RV404): everything the executor knows BEFORE the external effect is dispatched, which is exactly the set a host needs to reconcile an orphaned effect with the effect's provider (look the idempotency key up, correlate by tool and argsHash). attemptId is the attempt join key (RV501): the outcome record of the same attempt carries the identical value. startedAt remains the documented legacy join for rows written before the id shipped; a wall-clock millisecond is not unique, which is why the id exists. |
| ToolEffectLedger | The side-effect ledger seam. An executor calls record once per dispatch (success or failure). Binding an approval to its effect is then a lookup: the approval entry and the effect share (runId, tool, argsHash), and the idempotency key is stable across a rerun of the same call. |
| ToolEffectRecord | One dispatch's side-effect facts, for the ledger. |
| TornLedgerArtifact | A torn fragment the writer quarantined while repairing a tail (RV502). |
Type Aliases
| Type Alias | Description |
|---|---|
| ChildStopReason | - |
| ConformanceExecutorFactory | Builds the provider under test from a shared-contract config. |
| ExecutorErrorCode | Why an isolated dispatch failed. |
Functions
| Function | Description |
|---|---|
| containerExecutor | Builds a container ToolExecutorProvider over a docker-compatible CLI. Register it as createEngine({ executors: { container: containerExecutor({ image }) } }); tools declaring executor: 'container' dispatch through it. Define such tools with subprocessTool and set executor to 'container', or hand-build a ToolDef. |
| executorConformance | Builds the conformance suite. factory produces the provider under test from a shared config; the kit supplies the command (its own runner, run by runtime, default the current Node) and the per-check options. |
| hashArgs | A stable content hash of the arguments for the ledger's argsHash. It canonicalizes object key order so equal arguments hash equally regardless of property order. |
| jsonlEffectLedger | A two-phase ToolEffectLedger appending JSON lines to path (`{ phase: 'intent' |
| loadEffectLedger | Scans a JSONL ledger file into intents, outcomes, and the orphaned intents a host must reconcile, pairing attempts exactly (RV501). A torn TRAILING fragment (the crash-mid-write artifact) is tolerated and reported; everything else the scan cannot decode, parse, and validate, invalid UTF-8, non-object JSON, a missing required field, an unknown phase (RV607), fails the scan closed with a typed LedgerCorruptionError unless tolerateCorrupt asks for the lines as data (RV502). Under tolerateCorrupt the scan never throws anything rawer than that: a malformed line is data, not an exception. |
| memoryEffectLedger | An in-memory ledger for tests and single-process hosts. It implements the two-phase capability: intents() exposes the pre-dispatch rows, entries() the outcomes, exactly as before. |
| parseToolResult | The tool-program result protocol: the child's stdout, trimmed, is the JSON result. Empty stdout is the null result; anything else must parse as JSON or the dispatch fails typed protocol. Diagnostics belong on stderr, which never enters the result. |
| registerExecutorConformance | - |
| runChildProcess | Spawns one child and resolves with its captured output and exit status, or rejects if the process could not be spawned at all (e.g. the command is a bare name and PATH is not in env, so it cannot be resolved). A child that exits non-zero or is killed resolves normally; interpreting that is the caller's job. |
| subprocessExecutor | @rulvar/executor: isolated tool executors (RV-216). Reference ToolExecutorProvider adapters that run a tool's work OUT of the engine process, so a tool whose input is hostile or model-generated cannot reach host capabilities the way an in-process tool (an ordinary function call) can. |
| subprocessTool | @rulvar/executor: isolated tool executors (RV-216). Reference ToolExecutorProvider adapters that run a tool's work OUT of the engine process, so a tool whose input is hostile or model-generated cannot reach host capabilities the way an in-process tool (an ordinary function call) can. |