Design partner program is openThree partners, real decision traffic →
razoo

Product · SDK

One package. Your domains folder. Your SQLite file.

The SDK is the product; the console is its reference implementation. Install the tarball into your TypeScript application, keep your agent YAML in your repository, and drive submit, propose, confirm, drain, and explain from the interface your users already have. Nothing leaves the tenant.

The loop

Submit, propose, confirm, drain, explain.

Five calls cover the whole governed loop. The shape is deliberate: propose and confirm are different functions, there is no execute export, and every entry takes an evaluation context so replay can inject the same one later.

01

Boot

Point the engine at your repository root and your SQLite file. It migrates and registers handlers.

02

Submit

Work enters through the same ingest path the API, CLI, and email poll use. Identity keys and action selectors are stripped.

03

Propose

Stage an action from the ids your agent bound in skills.yaml. The row is pending. auto_confirm on the definition does not change that.

04

Confirm and drain

A person confirms from your UI. The outbox job is enqueued in the same transaction and executed on the next drain.

05

Explain

Ask why. Rung, artifacts, versions, envelope, tokens, and the confirm, for any work item, at any time.

app/server/razoo.ts
import path from "node:path";
import { bootEngine, submitWork, proposeAction, confirmAction,
  drainOnce, explainOf, clockFromIso } from "@engine/sdk";

// once per process, before any govern call
const root = process.cwd();
bootEngine({ root, sqlitePath: path.join(root, "data", "razoo.sqlite") });

export async function approveInvoice(subject: Subject, now: string) {
  const ctx = clockFromIso(now);
  const work = await submitWork({
    work_type: "ap_invoice", source: "erp", subject, ctx,
  });
  if (!work.ok) return work; // IngestFail names the field and a fix

  const staged = await proposeAction({
    workItemId: work.id, actionId: "approve", ctx,
  });
  // staged.status === "pending". It stays that way until a person confirms.
  return staged;
}

export async function onApproverClick(stagedId: string, now: string) {
  const ctx = clockFromIso(now);
  await confirmAction(stagedId, ctx); // authority re-checked here
  await drainOnce(ctx);           // and again in the handler, before any effect
}

export const why = (workItemId: string) => explainOf(workItemId);

@engine/sdk 0.1.0

The export list, as shipped.

One Node 20+ ESM package that bundles the kernel and the govern surface. Native and heavy dependencies stay external and install with the tarball. This table is the public surface of packages/sdk/src/index.ts, not a roadmap.

ExportKindRole
bootEngine({ root, sqlitePath?, drain? }) / createEngine(opts)functionbootEngine sets the process-default instance. createEngine is instance-scoped: two engines on two files in one process, each with its own close(). Once per process or per instance, before any other call.
submitWorkfunctionIngest a work item through the same path as the API and CLI. Returns IngestOk or IngestFail; never throws a validation error.
proposeActionfunctionStage an action bound to the agent's govern.propose overlay. The row is left pending. Always.
confirmAction(id, ctx, actor?, reason?)functionThe separate act. Re-checks the envelope and the authority binding, then enqueues the outbox job in the same transaction.
nextWork / resolveWork / captureNotefunctionThe judgment API: claim the next work item, record the human's choice, capture a note. Every write takes an actor.
dismissAction / editActionPayloadfunctionDecline or patch a pending staged row before it is confirmed, attributed to a named actor.
listPending / listStaged / listWork / listDecisions / listPatterns / patternViewfunctionTyped rows, cursor-paginated where the set can grow. The console reads the same functions.
retirePattern / revokeDecisionfunctionRetire a pattern, or withdraw one learned decision's authority with a required reason and a correct-choice counterexample. One click plus a reason, from your UI.
decisionOffunctionOne stored Decision by id, validated against the evaluation context.
explainOffunctionRung, fired artifacts, pattern id and version, envelope, tokens, and verdict for a work item. VAA fields on a learned fire.
workStatusfunctionWhere a work item is in its process, why it stopped if it did, and autonomy_progress: how close it is to running itself.
drainOnce / startDrainLoopfunctionThe transactional outbox. Run one drain, or a loop that catches up on wake.
enqueueOutbox(topic, payload, ctx, idempotencyKey?) / registerHandlerfunctionEnqueue a job on the host's SQLite, and the one piece of wiring a host does once per engine: map a confirmed topic onto your own integration.
listOutbox / requeueOutboxfunctionDead-letter listing and requeue, the same function the operator route calls.
engineHealth / onEngineEvent / ENGINE_EVENTSfunctionThe same shape as the appliance's /api/health, and the eight lifecycle events, emitted after commit.
backupDatabase / restoreDatabasefunctionOnline-safe backup and restore against a live, WAL-mode database, with a manifest naming the schema and secrets-key id.
doctorAgent / doctorAll / formatDoctorfunctionThe same validator as engine doctor: errors name the file, what is wrong, and a valid example.
clockFromIso / wallClockfunctionBuild an EvalContext. Rules and the kernel never read the wall clock themselves.
RazooError / isRazooError / RAZOO_ERROR_CODESconstEvery SDK error carries a closed, documented code enum. Branch on code, never on message text.
GOVERN_SURFACE / SURFACE_MIN_ROLEconstThe govern function names and the minimum role each requires, used by the MCP catalog parity test.
BootEngineOptions, BootedEngine, CreateEngineOptions, Engine, SubmitWorkInput, ProposeActionInput, ProposeActionResult, IngestOk, IngestFail, DecisionExplanation, WorkStatus, AutonomyProgress, RevokeDecisionInput, DecisionRevocation, EngineHealth, EngineEventName, EngineEventPayloads, EvalContexttypeThe exported types. Nothing else is typed for a customer.

Distribution is a compiled tarball handed to you under the commercial agreement, not a public registry publish. You receive JavaScript, a curated .d.ts, a licence, and a README. Your domains folder and your data folder stay in your repository.

What the SDK guarantees

The guarantees do not change because you embedded it.

The govern surface composes existing kernel paths. It does not write projections, touch patterns, or bypass the ladder, the outbox, or the accept path. Each line below is pinned by a test.

Propose stays pending

proposeAction always leaves the row pending, even when the action definition says auto_confirm. There is no execute export and no flag that adds one.

Confirm is a separate act

confirmAction is the only way a staged row moves. It enqueues the outbox job in the same SQLite transaction. Direct execution is not a code path.

money_legal needs a person

A field or an action marked money_legal never auto-executes, whatever the envelope says and whatever the model was confident about. No envelope, flag, or confidence can override it.

Envelope and binding rechecked at the handler

After confirm, before any side effect, the outbox handler re-checks the dependency binding and the envelope. A mismatch marks the row invalidated and routes the work to judgment.

Adapters append facts

A connector response is appended as a fact against the work item. Accept is the only writer of business truth, and it is yours.

Plumbing never calls a model

A plumbing step cannot reach the gateway. A model is called only on a step your process declared as reasoning, on a tier named fast or capable, never a vendor.

Next.js notes

Boot once, on the server, on Node.

The kernel is a native SQLite library and a set of synchronous functions. That rules out the Edge runtime and client components, and it means the boot module must be a server-only singleton.

  • Mark the boot module server-only, or import it only from Route Handlers and server actions.
  • better-sqlite3 is native. Declare it in serverExternalPackages and keep every route that touches the engine on the Node runtime.
  • bootEngine runs once per process. Guard it against hot reload with a global, as above.
  • Keep domains/ and data/ in your repository, next to the app. Doctor and simulate run against the same folder.
next.config.ts · and the boot module
// next.config.ts
export default {
  serverExternalPackages: ["better-sqlite3"],
};

// lib/razoo.ts · imported only from Route Handlers and server actions
import "server-only";
import { bootEngine } from "@engine/sdk";

declare global { var razooBooted: boolean | undefined }
if (!globalThis.razooBooted) {
  bootEngine({ root: process.cwd() });
  globalThis.razooBooted = true;
}

// app/api/confirm/route.ts
export const runtime = "nodejs"; // never edge
known limits

What the surface does not do. Stated plainly.

The govern surface, the judgment API, listings, events, health, revocation, and the JSON Schemas are all in 0.1.0. Four things are genuinely narrower than the console.

Three listings are unpaginated

listPatterns, listPending, and listOutbox take limit only, no cursor. The promotable set and the dead-letter set an operator acts on are both small.

Revocation acts on the pattern

revokeDecision pulls authority back at the pattern, as a counterexample against its signature, not by editing or deleting the individual decision row. The decision stays in the log.

No replay-drift upgrade check

engine upgrade's replay-drift check is not part of the tarball. An SDK-embedded host gets automatic, additive schema migration on boot, not a decision-drift check before it.

Backup and restore are free functions

backupDatabase and restoreDatabase operate on the engine current for the call (bind one with runWithEngine), the same as every other free-standing SDK function — not methods on Engine itself.

Install the tarball. Author one agent. Drive the loop.

Design partners embedding the SDK get the tarball, the schema documentation, and an engineer on the first integration call.