The coding agent you can hold accountable.
Arcturn is an open-source terminal coding agent and the TypeScript harness underneath it.
- Every tool call clears a permission engine before it runs.
- Every edit is snapshotted before it lands.
- Every session is a file on disk you can replay, bisect and blame.
Node 20 or newer. See Getting started.
Allowed. The edit landed, and a checkpoint was written first.
An arcturn session. The header names the model and the working directory; a prompt asks for input validation on the /signup handler; Arcturn reads the route and greps for existing validation, then stops and asks permission before editing src/routes/signup.ts, offering allow, deny, or always-allow for src/**.ts. The edit is allowed: it lands as 24 lines added and 3 removed, a checkpoint is recorded for /rewind, LSP reports no errors, and the session's spend so far is shown against its budget.
The problem
Capability raced ahead of accountability.
The models write the code — that question is settled. The one I actually have is whether I can let an agent run, and, twenty minutes later, what exactly it did.
git diff is the whole forensic story.
Forty tool calls leave the residue of maybe twelve, in a flat pile, with no indication of which turn produced which hunk.
The shell commands leave no trace at all.
Fetches, background tasks, and the sub-agent that ran for ninety seconds and cost more than the rest of the session.
“Undo” means git checkout plus remembering.
The conversation that produced the change isn’t in version control, so going back means losing it.
What’s different
Four things Arcturn does that a capable agent still doesn’t.
Control
Decide once, at the choke point.
The runtime’s tool dispatcher checks the permission engine and returns a denial before a tool’s execute is ever reached — there is no second path. Rules are allow, deny or ask; scopes resolve session over project over user. Read-only tools pass. Anything that reaches the ask step with no permission requester configured resolves to deny, not “assume it’s fine.”
- Four modes —
default,acceptEdits,plan,yolo --dry-runsends file mutations to a shadow tree for/diffbefore/apply- Lifecycle hooks can veto a
preToolUsecall - An opt-in OS sandbox confines
bashwrites to the workspace
A permission prompt in an arcturn session: editing src/routes/signup.ts requires approval, with allow, deny, and always-allow for src/**.ts offered.
Accountability
The session is the artifact.
Every session is a .jsonl file — a header line, then one JSON line per entry, appended in order. The structure is a tree: each entry carries a parentId, so resuming from three turns ago and trying a different approach starts a branch instead of overwriting what came after. Both branches stay walkable.
Underneath, every write and edit snapshots the file’s prior content first, so /rewind can restore it.
arcturn replay <session>- Re-runs the original prompts against the same model or another one, emitting NDJSON per turn, so you can diff two runs mechanically.
arcturn bisect <session>- Binary-searches those prompts for the turn where behaviour diverged, replaying a recorded cassette hermetically: no provider, no network.
arcturn blame <file>- Per line, which turn wrote it and what evidence that turn had: files read, pages fetched, commands run, with anything from a fetch or an MCP server marked untrusted.
Extend
Add a capability without recompiling anything.
Models
Bring your own provider.
One interface across Anthropic, OpenAI and every OpenAI-compatible endpoint, Google Gemini, Bedrock, Vertex and Azure — streaming, tool calls, thinking, prompt caching and cost tracking included. Point --model at <provider>/<model>, route different roles to different models, or set a failover chain.
arcturn --model anthropic/claude-sonnet-4-5
arcturn --model openai/gpt-5.1
arcturn --model google/gemini-3-pro-previewReceipts
What has actually run, and what hasn’t.
6 of the 9 provider paths have completed real multi-turn tool-calling sessions against a live endpoint. Another 3 have never reached their endpoints at all. Which is which is in the table, not in a footnote.
openai-compatible
Any OpenAI-shaped endpoint, credentials per endpoint. Has completed real multi-turn tool-calling sessions.
Provenanthropic
Claude, direct — ANTHROPIC_API_KEY, or an OAuth subscription sign-in. Verified live on Claude Haiku 4.5.
Provenopenai
GPT via Chat Completions — OPENAI_API_KEY. Verified live on GPT-5 nano.
Provenopenai-responses
GPT via the Responses API — OPENAI_API_KEY. Verified live on GPT-5 nano.
Provengoogle
Gemini, direct — GOOGLE_API_KEY (GEMINI_API_KEY also works). Verified live on Gemini 3.5 Flash Lite.
Provenanthropic-compatible
Any Anthropic-Messages endpoint, credentials per endpoint. Verified live against a canonical Messages API; no third-party implementation exercised yet.
Provenbedrock
Claude, Nova, Llama, Mistral and Titan on AWS — the standard AWS provider chain.
Not reachedvertex
Gemini and Claude on Google Cloud — application-default credentials.
Not reachedazure
GPT on Azure OpenAI, addressed by deployment — AZURE_OPENAI_API_KEY or Entra ID.
Not reached
Four waves of adversarial review went at the seams. They found /apply writing outside the workspace through an in-workspace symlink, served sessions and sub-agents escaping the audit trail entirely, a WebSocket upgrade with no Origin check, and two features that were present but unreachable. All four are written up on the security page rather than quietly patched out.
Every fix landed with a regression test verified to fail against the previous behaviour first.
Embed it
The same runtime, without a terminal in front of it.
@arcturn/core is what the CLI is built on. One Agent per session, one AgentEvent stream out — the same events the CLI emits with --output-format json.
import { createAgent } from "@arcturn/core";
import { createClient, requireModel } from "@arcturn/ai";
import { createDefaultTools } from "@arcturn/tools";
const llm = createClient(); // resolves API keys from the environment
const { tools } = createDefaultTools({ cwd: process.cwd() });
const agent = createAgent({
llm,
model: requireModel("anthropic/claude-sonnet-4-5"),
systemPrompt: "You are a focused, careful coding agent.",
tools,
cwd: process.cwd(),
sessionDir: ".arcturn/sessions", // omit for an unpersisted, in-memory agent
permissions: { mode: "default" },
});
agent.subscribe((event) => {
if (event.type === "toolStart") console.log("→", event.toolName);
});
await agent.prompt("Add input validation to the signup handler");
console.log(agent.finalText());Open source
Apache-2.0, and checkable.
No commercial-use restriction, no source-available licence with a catch in clause four. One repository holds the CLI, the runtime, the harness and the regression tests behind the findings above — and the commands that check them are written down rather than described.
Every turn counts.
Start a session, watch every tool call ask first, then go back and read exactly what happened.