Repository navigation
Home
agent-runtime is a small Go library for running tool-using agents under deterministic control. A consumer supplies an agent, a set of tools, and a policy. The runtime supplies the loop and every control around it, or, since v0.4.0, offers the same controls to a loop the consumer keeps (see The Gate): schema validation of each request, a policy the agent cannot influence, durable hash-bound approvals, hard limits, an audit log written in the same transaction as the state, and recovery after interruption. Since v0.5.0 the audit log is also a hash chain, and a finished run can be re-checked under a policy.
The model loop itself is not the point; any SDK provides one in fifty lines. The project's identity, since v0.4.0, is to own the effect, not the loop: any loop, the runtime's own or a caller's, proposes an action, and the runtime decides whether it runs, runs it, and records it. This library exists for what happens at the moment a tool is called and something outside changes, when the agent reads untrusted input and takes real actions. Its first consumer is repo-steward, which performs sandboxed dependency upgrades and supplies most of the requirements for the public API; the second is casework. What both of them had to write twice is now in the repository: the provider adapters, the renderer that turns recorded steps into model messages, the trace and read models, and the operator command.
v0.5.0 of the core, published 2026-10-08, with nested modules proxy at v0.2.0 and export/otel at v0.1.2, both requiring core v0.5.0. mcp stays at v0.2.1, providers/ollama, providers/anthropic, and providers/openai at v0.2.0, and bench at v0.1.1: their code did not change, so they have no new tag. v0.5.0 is roadmap item 10, re-checkable decisions. Each decision now records what it can be checked against (the tool's spec by hash, the policy's identity, and what the policy saw of the run), the events form a hash chain that agentrt verify checks, and agentrt.Recheck replays a finished run through a policy and reports where it decides differently. See Re-checking a Run. agentrt-proxy recheck does the same for the proxy's recorded calls (MCP Proxy), and testkit.Recheck in a consumer's tests (Testing a Consumer).
An upgrade needs an operator step. v0.5.0 adds a schema migration, so every older process and operator binary must be stopped before the first open: an older binary's events would carry no hash and break the chain, a v0.4.0 binary refuses a migrated database, and the migration hashes every stored event once. There is also one behaviour change: a policy is now handed a request's arguments and its tool's schema in one form, PolicyJSON, on every path. A policy that unmarshals them is unaffected. Upgrading to v0.5 says what a consumer could notice. A run recorded before v0.5.0 re-checks as "not re-checkable". The reasoning, and what the chain and a re-check do not show, is in decision 9. The Roadmap has item 10 released; items 11 and 12 are intentions.
The release before it, v0.4.0 (2026-10-05, with mcp/v0.2.1, export/otel/v0.1.1, and proxy/v0.1.0, its first tag), added three things. The Gate offers the runtime's controls to a loop the runtime does not own, and the Driver now runs on the same code. The MCP Proxy, agentrt-proxy, puts the gate in front of an MCP host in any language, as a small experiment whose scope is stated first. And agentrt.CanonicalJSON exports the encoder the runtime hashes with. It was additive, except that a number past a new bound is refused where consumer JSON enters, and it fixed a crash a model-chosen number could cause in every release through v0.3.2; Upgrading to v0.4 has both. The reasoning behind its identity is in decision 7 and decision 8.
Before it, v0.3.2 was a patch over v0.3.1 with two small additions the consumers asked for while adopting it (approver.RunReader and testkit.StandIn). v0.3.1 added four things around the loop, each with its own page: measuring an agent, testing a consumer without a model, exporting events to a tracing tool, and approval channels for deciding from chat or a browser. The ideas behind the design are written out in the Essays.
v0.3.0 followed an audit and a security review of v0.2.1: a run is now leased to the process executing it, the database is owner-only, an interrupted side effect waits for an operator, the operator's decision is bound to the approval they were shown, and the adapters refuse redirects and keys over plain http. Coming from v0.2, read Upgrading to v0.3 before anything opens your database; v0.3.1 to v0.4.0 add no step to it, and v0.5.0 adds its own.
This is the Driver, the runtime's own loop. A gate runs the same sequence for a loop the caller owns: the caller's code stands where agent.Decide does and calls Propose and Execute.
Start(goal, limits)
loop until terminal or paused
check limits (steps, failures, loops, time)
agent.Decide(StepInput) the agent sees the run, every prior step, approvals, tool specs, a model caller
record the decision verbatim even an invalid one is in the log
validate args against the schema invalid becomes an observation the agent sees next
policy.Evaluate(request, view) allow, deny, abort, or require_approval; arguments and schema in PolicyJSON form
execute with timeout only under a live lease; a panic or timeout becomes an observation
record the observation
terminal tool succeeded run COMPLETED with its result
| Import path | What it is |
|---|---|
github.com/joeylking/agent-runtime |
the core, v0.5.0: the loop, the gate, the store, approvals, leases, limits, the model interface, the event chain, and Recheck. Two dependencies, a JSON Schema validator and SQLite |
.../replay |
scripted, recording, and replaying models, so tests never call a provider |
.../scripted |
the scripted agent |
.../providers |
the dependency-free helpers a provider adapter shares: error classification with Retry-After, an HTTP client that follows no redirect, bounded body reads, the endpoint and usage checks, price lookup, dollar parsing |
.../render |
recorded steps to model messages, and a model response back to a decision |
.../trace |
aligned and JSON Lines event observers, and Sanitize for text bound for a terminal |
.../view |
run, step, and approval read models for an operator surface, read in bounded pages |
.../cmd/agentrt |
the operator command over a runs database, including verify, which checks the events' hash chain |
.../testkit |
what a consumer's tests use without a model: a crash and resume harness, a policy conformance table, schema fuzzing of tool arguments, a render equivalence check, and re-check helpers. See Testing a Consumer |
.../export |
a read-only follower of the events table that hands each event, in commit order and from a cursor, to a sink, with a JSON Lines sink, and Head, the chain's head as far as it delivered. See Exporting Events
|
.../approver |
the interface an operator surface decides approvals through, bound to the hash it showed, and its store-backed implementation. See Approval Channels |
.../approver/webhook |
the reference signed HTTP approval channel over approver
|
.../providers/ollama |
a local Ollama server. Nested module at v0.2.0, standard library only |
.../providers/openai |
any OpenAI-compatible endpoint. Nested module at v0.2.0, standard library only |
.../providers/anthropic |
the Claude Messages API through the official SDK. Nested module at v0.2.0 |
.../mcp |
an MCP server's tools as runtime tools, and the readers for the operator's manifest and rules files. Nested module at v0.2.1. See MCP Tools |
.../bench |
the outcome vocabulary, result file, summarizer, and comparison for scoring an agent that acts. Nested module at v0.1.1, no dependencies at all. See Measuring Agents |
.../export/otel |
an exporter that turns the events export delivers into OpenTelemetry spans, and the provider an OTEL_* environment configures. Nested module at v0.1.2, requiring core v0.5.0. It carries a decision's origin, and the policy's identity and the spec hash, as span attributes |
.../proxy |
agentrt-proxy, a local MCP proxy over stdio that routes an MCP host's tool calls through a gate, one run per call. Nested module at v0.2.0 (first tagged v0.1.0 with core v0.4.0), requiring core v0.5.0 and mcp v0.2.1; v0.2.0 adds recheck. See MCP Proxy
|
The packages inside the core module add no dependency beyond the two the core already has, testkit, export, approver, and approver/webhook among them. Each nested module has its own go.mod and its own directory-prefixed tag (mcp/v0.2.1, providers/ollama/v0.2.0, bench/v0.1.1, export/otel/v0.1.2, proxy/v0.2.0), so a provider SDK or OpenTelemetry never reaches the core and a consumer imports only what it uses. Every module requires Go 1.26 or later. See Provider Adapters, MCP Tools, MCP Proxy, Exporting Events, and Releasing.
The repository's docs/ folder holds the architecture, status, performance, roadmap, and decision documents that ship with the code; CHANGELOG.md, SECURITY.md, and CONTRIBUTING.md sit beside the README. This wiki explains the same runtime at more length. Where they differ, the repository documents win.
Using it
- Getting Started
- Upgrading to v0.5
- Upgrading to v0.4
- Upgrading to v0.3
- Concepts
- Writing an Agent
- The Gate
- Writing Tools
- MCP Tools
- MCP Proxy
- Writing a Policy
- Models and Replay
- Provider Adapters
- Rendering Model Context
- Approvals and Resume
- Interruption and Reconciliation
- Run Lease
- Limits
- Testing a Consumer
- Re-checking a Run
- Measuring Agents
Inside it
- Internals
- Persistence and Inspection
- Performance
- The Operator Command
- Approval Channels
- Exporting Events
- Testing
Deciding on it
- Safety Properties
- Security Model
- Comparison with Frameworks
- Status and Roadmap
- Roadmap
- Design Decisions
- Essays
- Contributing
- Releasing
Reference