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.
Boot
Point the engine at your repository root and your SQLite file. It migrates and registers handlers.
Submit
Work enters through the same ingest path the API, CLI, and email poll use. Identity keys and action selectors are stripped.
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.
Confirm and drain
A person confirms from your UI. The outbox job is enqueued in the same transaction and executed on the next drain.
Explain
Ask why. Rung, artifacts, versions, envelope, tokens, and the confirm, for any work item, at any time.
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.
| Export | Kind | Role |
|---|---|---|
| bootEngine({ root, sqlitePath?, drain? }) / createEngine(opts) | function | bootEngine 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. |
| submitWork | function | Ingest a work item through the same path as the API and CLI. Returns IngestOk or IngestFail; never throws a validation error. |
| proposeAction | function | Stage an action bound to the agent's govern.propose overlay. The row is left pending. Always. |
| confirmAction(id, ctx, actor?, reason?) | function | The separate act. Re-checks the envelope and the authority binding, then enqueues the outbox job in the same transaction. |
| nextWork / resolveWork / captureNote | function | The judgment API: claim the next work item, record the human's choice, capture a note. Every write takes an actor. |
| dismissAction / editActionPayload | function | Decline or patch a pending staged row before it is confirmed, attributed to a named actor. |
| listPending / listStaged / listWork / listDecisions / listPatterns / patternView | function | Typed rows, cursor-paginated where the set can grow. The console reads the same functions. |
| retirePattern / revokeDecision | function | Retire 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. |
| decisionOf | function | One stored Decision by id, validated against the evaluation context. |
| explainOf | function | Rung, fired artifacts, pattern id and version, envelope, tokens, and verdict for a work item. VAA fields on a learned fire. |
| workStatus | function | Where a work item is in its process, why it stopped if it did, and autonomy_progress: how close it is to running itself. |
| drainOnce / startDrainLoop | function | The transactional outbox. Run one drain, or a loop that catches up on wake. |
| enqueueOutbox(topic, payload, ctx, idempotencyKey?) / registerHandler | function | Enqueue 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 / requeueOutbox | function | Dead-letter listing and requeue, the same function the operator route calls. |
| engineHealth / onEngineEvent / ENGINE_EVENTS | function | The same shape as the appliance's /api/health, and the eight lifecycle events, emitted after commit. |
| backupDatabase / restoreDatabase | function | Online-safe backup and restore against a live, WAL-mode database, with a manifest naming the schema and secrets-key id. |
| doctorAgent / doctorAll / formatDoctor | function | The same validator as engine doctor: errors name the file, what is wrong, and a valid example. |
| clockFromIso / wallClock | function | Build an EvalContext. Rules and the kernel never read the wall clock themselves. |
| RazooError / isRazooError / RAZOO_ERROR_CODES | const | Every SDK error carries a closed, documented code enum. Branch on code, never on message text. |
| GOVERN_SURFACE / SURFACE_MIN_ROLE | const | The 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, EvalContext | type | The 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
Confirm is a separate act
money_legal needs a person
Envelope and binding rechecked at the handler
Adapters append facts
Plumbing never calls a model
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 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
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.