Skip to content

Repository files navigation

looprun

A governance layer for AI agents, on top of the framework you already use.

You write three cards. The engine holds every destructive act for a human's word, says what agreeing would do in the domain's own words, and seals what happened into a record nobody can edit — including the model.

npm install looprun
import{LoopRunAgent,world}from'looprun';consthotel=world({records: {bookings: {bk_1: {room: 'Blue Room',day: 'Friday',status: 'CONFIRMED'}}},reads: {getBooking: {form: 'get',entity: 'bookings',label: 'Look up one booking'}},destructive: {cancelBooking: {form: 'remove',entity: 'bookings',label: 'cancel a booking'}}});constagent=newLoopRunAgent({spec: {name: 'concierge',persona: 'A friendly hotel concierge who manages room bookings.'},world: hotel,model: 'google/gemini-2.5-flash'});console.log((awaitagent.generate('Please cancel booking bk_1.')).text);
Nothing changed.
cancelBooking(bk_1) — not-done (awaiting approval)
[CONFIRM 355ec2] cancel a booking runs only after your approval.

Twelve lines of code, and nothing about consent is in them. cancelBooking sits under destructive; that single fact installs the hold, the wording, the one-time code, and the rule that only a later message carrying that code releases that one call.


The whole authoring surface

 the WORLD CARD what exists, and what a tool DOES to it
records · reads · writes · destructive
the AGENT SPEC how ONE desk behaves
name · persona · tools · teammates · guards · llmParams · limits
the DOMAIN what the BUSINESS is — every desk answers to it
CONTRACT voice · facts · guards · disclosure · secrets · rewrites ·
wording · limits

There is no fourth thing. No hooks, no loop, no return protocol, no tool plumbing.

What the engine does that you did not write

Consentevery destructive call is held; the approval arrives in a later message, carries a code the engine minted, and licenses exactly that call
Disclosurethe consent question states what THIS call would do, with figures the engine read itself: "Cancelling Blue Room on Friday is permanent, and 240 stays owed."
The floorfabricated identifiers and dates, duplicate calls, half-filled arguments, a reply that claims what the acts do not show — all refused without you declaring anything
The recordone row per act, in a closed vocabulary — done · held · refused · unknown · not-done — chosen by the engine from what the world answered, never from the model's prose
Honest closurea turn that ran out of room closes with what verifiably happened, never with a fabrication

A guard is one sentence

The sentence is what the model is told, what the person is told when a call is refused, and what agent.guards() prints. One string, three jobs — they cannot drift apart.

{ ...onlyAfter('cancelBooking','getInvoice'),rule: 'Read the booking\'s invoice before cancelling, so the guest hears what stays owed.'}

When the model skips the read, the engine collects it itself in one forced micro-step, and what comes back is that same sentence:

cancelBooking(bk_1) — not-done (Read the booking's invoice before cancelling, so the guest
hears what stays owed. getInvoice did not succeed this conversation)

Or declare it, and emit

The three cards can be written as data instead of TypeScript: one declaration.yaml — the contract and its desks — beside the world card it is declared against. The emitter writes the TypeScript and invents zero prose; every sentence in the cards is the declaration's own.

npx looprun-emit <subject-dir># writes cards.ts, subject.ts, the gate file, gen/SEAM.md

A declaration that does not fit its world writes nothing: every refusal the emitter can know is printed at once, each naming the exact YAML path to fix. The declaration is always what changes, never the emitter. The shape, and every refusal with its message: packages/emit/README.md.

Framework-agnostic by construction

@looprun-ai/core is the engine and knows about no framework. @looprun-ai/mastra binds it to Mastra, where LoopRunAgentis a genuine @mastra/core Agent: it registers in your Mastra instance and shows up in Studio with the guards enforcing live. Anything else can call a governed agent over HTTP — @looprun-ai/server puts it behind an OpenAI-compatible /v1/chat/completions endpoint.

packagewhat it is
looprunthe umbrella: the cards, the world, and the Mastra-hosted agent under one name
@looprun-ai/corethe engine — framework-free
@looprun-ai/mastraLoopRunAgent and its ungoverned twin
@looprun-ai/servergoverned agents behind an OpenAI-compatible endpoint
@looprun-ai/emitthe declaration reader and the emitter — the cards, written from data
@looprun-ai/evalverbs over a run directory: run, watch, judge, fold, certify, seal
@looprun-ai/modelsthe validated local tiers on llama.cpp

Measuring it

@looprun-ai/eval runs an exam of authored cases against your model and certifies the result against a bar. UngovernedAgent is the same cards, the same world, the same prompt with every guard hook empty — the only honest way to say what the governance is worth. On a hundred-case exam of a rental-operations domain:

 governed ungoverned
judged score 95/100 54/100
invariant failures 1 29
consent questions 47 0
writes executed 46 79
writes stopped 104 26

The judge is the person and the agent doing the work. No file in this repository calls a third-party model API — not to judge a run, not to score a transcript. buildJudgeInputs writes blind rows to disk and a human-and-agent pair writes the verdicts back. The only model a run reaches is the subject under test.

Learn it

 docs/tutorial/01-concepts.md the three things you write
docs/tutorial/02-hello-world.md consent, for free
docs/tutorial/03-disclosure.md what agreeing would do
docs/tutorial/04-guards.md the catalog and the three strengths
docs/tutorial/05-the-domain-card.md secrets, limits, wording, a second desk
docs/tutorial/06-running-and-measuring.md the exam, the verbs, the twin

Every code block in that tutorial is a compiled file under docs/tutorial/snippets/, run by the test suite — a lesson that drifts from the engine fails the build.

What proves it

governance/GOVERNANCE.md — 12 engine proofs, 4 structural lints, 7 facade gates, the eval verb proofs and 3 repository gates, all of them running under pnpm test.


Apache-2.0 · looprun.ai · Node ≥ 22

About

A governance layer for LLM agents, on top of the framework you already use — AgentSpec (the map) + deterministic guards (the safety kit) + measured certification. Mastra-first. npm i looprun

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages