Agent workflows that survive the process.

Rulvar is an embeddable TypeScript runtime for multi-agent work. Route every model call, bound priced spend, and keep completed work in a durable journal. After a crash or deploy, journal-matched calls replay without a new provider request.

pnpm add @rulvar/rulvar
ship-feature-042
00:00.000
Implement, verify, and return one explicit outcome.
  1. 01
    Orchestrator anthropic:claude-fable-5
    live
  2. 02
    Implementation openai:gpt-5.6-terra
    queued
  3. 03
    Local review ollama:qwen3.8:27b
    queued
  4. 04
    Structured output openai:gpt-5.6-luna
    queued
SEGMENT CEILING $5.00
journal: durable events: typed
Explore the runtime

Your coding agent helps you build it. Rulvar runs it inside your product.

For working on your code.

Use a coding agent for interactive development: explore a codebase, make changes, and review the result.

For workflows your application runs.

Use Rulvar when the workflow must own routing, budgets, policy, durable state, tests, and a typed outcome without requiring an operator at every step.

Rulvar is a library, not a hosted control plane. It runs in your Node.js application and keeps workflow logic in ordinary TypeScript.

Follow one run.

Follow ship-feature-042 through execution, a crash, and recovery. Scroll to move through the run.

  1. 01
    Open the segment.

    Bind a goal, policy, and USD ceiling before work begins.

  2. 02
    Resolve every call.

    Choose a model by call, profile, workflow, or engine default.

  3. 03
    Admit and execute.

    Priced work passes budget admission before provider dispatch.

  4. 04
    Record completion.

    Completed LLM calls settle into the content-addressed journal.

  5. 05
    The process disappears.

    The coordinator is checkpointed, one child is complete, and another is in flight.

  6. 06
    Resume from durable state.

    Restore the checkpoint, replay the completed child, and continue unfinished work live.

  7. 07
    Settle explicitly.

    Receive a typed outcome with status, usage, and an estimated cost report.

COMPLETION

Only completed and journaled calls replay. An in-flight turn may run and be billed again.

STORAGE

Cross-process resume requires durable journal storage. Turn checkpoints and planner-compiled workflows also need a durable transcript store; the defaults are in memory.

PRICING

A model without a price row is reported as unpriced and cannot be bounded by the USD ceiling.

Control the models, cost, and recovery.

Routing, budgets, journal identity, events, and tests share the same execution path in every authoring mode.

orchestrateanthropic:claude-fable-5HOSTED
planopenai:gpt-5.6-solHOSTED
loopopenai:gpt-5.6-terraHOSTED
extractopenai:gpt-5.6-lunaHOSTED
summarizeollama:qwen3.8:27bLOCAL / UNPRICED

The model is resolved for every invocation.

The runtime routes all seven invocation roles independently. Within one agent call, loop, finalize, extract, and summarize may use different providers; plan, orchestrate, and post-fan-in synthesize are routed separately.

vllm:zai-org/GLM-5.3-Flash vllm:moonshotai/Kimi-K3
Example endpoint IDs. Compatible model IDs and capabilities are declared by the host. Model routing guide ↗

Choose how the workflow is defined.

Write the sequence, generate a script once, or let an orchestrator decide the next step. All three use the same journal, budgets, and model routing.

ship-feature.mts
const shipFeature = defineWorkflow(
  { name: 'ship-feature' },
  async (ctx, goal) => {
    const plan = await ctx.agent(goal, {
      agentType: 'planner',
    });
    return ctx.agent(String(plan), {
      agentType: 'implementer',
    });
  },
);

You own the sequence.

The clearest mode for stable workflows. Use normal control flow and the injected ctx primitives.

Workflow guide ↗

When to use Rulvar.

A good fit
  • Runs are long or expensive enough that a retry should not re-bill completed model calls.
  • Different roles need different hosted, gateway, or local models in one workflow.
  • Your application needs explicit budgets, permissions, events, tests, and outcomes.
  • You want orchestration in TypeScript instead of a graph or YAML core.
You may not need it
  • You only need one prompt and one response.
  • You want a hosted control plane to own execution and persistence.
  • Your primary interface must be a visual graph editor.
  • The workflow has no meaningful retry cost, policy, or durability requirement.

External side effects need their own safety design. Rulvar's effect lane records intent before effect and binds retries to attempts, but it does not promise exactly-once external execution. Provider-side fencing or idempotency and host reconciliation remain required. Read the effects guide ↗

Configure the runtime in code.

Register only the adapters you use, route invocation roles, attach durable stores, and start the workflow with a segment ceiling.

pnpm add @rulvar/rulvar @rulvar/openai
workflow.mts
import {
  anthropic,
  createEngine,
  FileTranscriptStore,
  JsonlFileStore,
  openai,
  orchestrate,
} from '@rulvar/rulvar';
import { openaiCompatible } from '@rulvar/openai';

const engine = createEngine({
  adapters: [
    anthropic(),
    openai(),
    openaiCompatible({
      id: 'ollama',
      baseURL: 'http://127.0.0.1:11434/v1',
    }),
  ],
  defaults: {
    routing: {
      orchestrate: 'anthropic:claude-fable-5',
      plan: 'openai:gpt-5.6-sol',
      loop: 'openai:gpt-5.6-terra',
      finalize: 'openai:gpt-5.6-terra',
      extract: 'openai:gpt-5.6-luna',
      summarize: 'ollama:qwen3.8:27b',
      synthesize: 'anthropic:claude-fable-5',
    },
    profiles: {
      implementer: {
        description: 'Implements the requested change.',
        model: 'openai:gpt-5.6-terra',
      },
      'local-reviewer': {
        description: 'Reviews the patch on a private endpoint.',
        model: 'ollama:qwen3.8:27b',
      },
      extractor: {
        description: 'Returns the structured result.',
        model: 'openai:gpt-5.6-luna',
      },
    },
  },
  stores: {
    journal: new JsonlFileStore({ dir: '.rulvar/journal' }),
    transcripts: new FileTranscriptStore({ dir: '.rulvar/transcripts' }),
  },
});

const goal = 'Implement and verify the requested feature';
const run = orchestrate(
  engine,
  goal,
  {
    profiles: ['implementer', 'local-reviewer', 'extractor'],
    maxSpawns: 8,
  },
  {
    budgetUsd: 5,
    runId: 'ship-feature-042',
  },
);

const outcome = await run.result;
console.log(outcome.status, outcome.cost.totalUsd, outcome.cost.unpriced);