An autonomous coding agent built on LangGraph and deepagents. It plans, executes, and verifies real changes against a real project checkout, with human approval required for anything risky. It also runs Planning Chat, a separate conversational assistant for research and design discussion that remembers what it learns across sessions and can hand a finished plan straight to the build pipeline.
It learns from its own work. After a failed attempt it reflects (what failed, why, what to do differently) before trying again; every finished task leaves an episode; once a day those episodes are consolidated into each project's memory; and months of past tasks stay searchable, so "have we hit this before?" has an answer. See Memory and reflection.
The backend is a FastAPI app (agent/server.py); the frontend is React/Vite. Together they cover
submitting and watching build tasks, chatting with Planning Chat, and administering models, users,
and repos.
The agent targets a fixed set of local projects (PROJECTS in agent/config.py /
projects.json). Each task gets its own workspace — a git worktree of the live repo at
/home/agent-workspaces/.tasks/<project>/<task-id>, on its own branch agent/<task-id>, filled
from the project's workspace (/home/agent-workspaces/<project>) with dependencies hardlinked and
build output copied (agent/workspaces.py). So tasks on the same project can run side by side:
Settings → Tasks at once per project sets how many (default 10, at most 16; each task has its own 2 GB sandbox, so size it to the machine). They code in parallel and take
turns only for checks, review and merge; a task that merges second is rebased onto the first and
reviewed again. The limit is a set of Postgres advisory locks (agent/graph.py, project_slot)
rather than anything in the process, because it is a property of the project: a second worker or an
overlapping restart sees the same slots, and Postgres drops a claim when its connection closes.
Community
Questions, help with an install, or an idea for what it should do next: join the Telegram group. Bugs are best as a GitHub issue, and security problems go through a private advisory, never in public.
Screenshots
![]() |
![]() |
| Watch a task work. Every tool call and its output as it happens, which model made it, the agent's own summary, and the review gate's READY or NEEDS_FIXES at the end. You can nudge it mid-task from the box at the bottom. | What it cost and whether it's getting better. Spend against your balance, outcomes, fix cycles, a benchmarks row comparing the last two weeks with the two before, and the review gate's own bill, from this box's router ledger. No tracing service. |
![]() |
![]() |
| GitHub alerts become tasks. Security alerts, code scanning and Dependabot pull requests in one inbox. Approve one and it's a task; alerts on the same package arrive as one item, and one the fix didn't close comes back. | Benchmarks you can rerun. The golden suite and SWE-bench Verified run from the dashboard. Each run keeps its score, cost, models, notes and what the host was doing. |
![]() |
![]() |
| Every role is a named alias you can repin. Planner, coder, frontend coder, investigator and test-writer each say what the role needs, with live pricing and an optional pinned provider. A swap is one dropdown. | Hands-off, with lines it won't cross. Auto mode lets a task finish without you, per project. It still asks before a delete that would lose work, and when it has a question for you. |
More: model probes, support roles, tool reliability, planning
Models are probed, not assumed. A constrained role says which models passed its probes and which failed, and when they were last probed.
Cheap jobs get cheap models. The classifier needs structured output and nothing else; the summarizer is a plain completion, so the cheapest tier is fine.
Where the tokens went, and which tools fail. Per-role calls, tokens, latency, cost and cache rate, then tool-call errors by tool and by day.
What the machine was doing. Memory, model calls in flight, latency and containers across a benchmark run, then every task's result.
Plan first. A planning session starts from a repo and a model route. It remembers what it learns about the project between sessions, and Build Now hands the finished plan to a build task.
Contents
Operating it, rather than reading about it: docs/architecture.md (the four processes, where each secret lives, the three checkouts, the two-node graph), docs/runbooks/ (symptom → check → action), and docs/backup.md (backup, and proving a restore works). Agreed-but-unbuilt work is in docs/todo.md.
- How a build task runs
- Planning Chat
- Frontend routing
- Model profiles
- IDE
- Dashboard
- Telegram alerts
- GitHub inbox
- Memory and reflection
- Model routing
- Auth
- Attachments
- Repo layout
- Running it
- Configuration
- Adding a project
- Operating it
- Testing
- Tracing and secret redaction
- Connection resilience
How a build task runs
agent/outer_graph.py wires a small, two-node graph around a deepagents agent
(agent/deep_agent.py):
START → work → verify_and_ship ──(findings / unfinished plan)──→ work (loop, same inner thread)
│
├──(escalated / awaiting approval / awaiting merge)──→ END (resting; resume continues)
└──(READY + merge allowed)──→ merge+deploy (inside this node) → END
work(agent/nodes/work.py) drives the deep agent's own tool-calling loop against a sandboxed checkout (docker/agent-sandbox/— only the target repo is mounted, so a shell command can't reach other projects or host secrets). It plans viawrite_todosand can delegate to three subagents: investigator (read-only research — no write/edit/shell tools at all), test-writer (writes tests, required to run the checks itself before reporting done) and verifier (agent-verifier, its own seat and ledger line since 2026-09-25: after a bug fix it runs the reported case and its neighbours and reports what still fails; it edits nothing). The verifier and the test-writer are bounded — 30 and 60 tool calls — and counted down in their own results, so the last allowed call carries no tools and has to be the report (WrapUpMiddleware) rather than "Tool call limit reached" and nothing. Arun_checkstool lets it run the project's real typecheck/lint/test suite itself mid-task — the same commands the gate will run — so it finds its own breakage rather than learning about it a full round-trip later. On a frontend-routed task the seats run under the Frontend profile overlay (see Frontend routing and Model profiles). If a model gets stuck repeating a call the loop guard has refused, the rest of that pass moves toagent-coder-fallback, a different model, on the same conversation. The task is handed back only if that model gets stuck too. An empty reply — reasoning that ran to the output cap and left nothing to say — is retried once on that same fallback seat at low reasoning effort before the conversation sees it (EmptyReplyRetryMiddleware); every seat's output is capped at 16k tokens. With a GitHub token it can also read a pull request host-side (github_pull_request) — the sandbox never sees the token.verify_and_ship(agent/nodes/verify_and_ship.py) is the actual gate. It always re-runs the real typecheck/lint/test suite itself — the agent's own "done" status carries no authority here. A check that fails on the branch is re-run at the merge-base; one that fails there too is marked pre-existing, does not forceNEEDS_FIXES, and is listed for the agent with an instruction not to chase it. A pass with a real diff produces one commit on the task's own branchagent/<task-id>, handed to an independent review service. The review unit is that branch plus its merge-base, fixed at the fork point — not "whatever the sandbox HEAD is now". The old model compared two HEADs and inferred the rest, which produced inverted diffs whenever live moved ahead: a branch's additions read as deletions of everything live had gained since, and that manufactured twoblockingfindings against a commit that had in fact added the settings it was accused of removing. Those checks are agent-authored code, so on a host install they run inside a container rather than on the machine — in the image their toolchain needs, with no network, and with a refusal rather than a fall-back if the sandbox is unavailable (SECURITY.md). From there:NEEDS_FIXESloops back toworkwith the findings;READYmerges and deploys (after the operator's merge approval, unless that switch is off). Merge and deploy live inside this node, not as a third graph node — a deploy preflight can fail as its own stage (a live URL the build depends on is down) and escalate to a human rather than being handed to the agent as a compile error.- A review round is an argument, and both sides are heard. The agent's closing message of a
round rides in the follow-up commit under "Response to review round N"; the reviewer reads every
answer on the branch before repeating a finding, withdraws a blocking finding that a reported run
has disproved unless the diff itself shows otherwise, and must name the evidence it disputes when
it does repeat one. The reviewer's own breaker still ends the argument — three
NEEDS_FIXESin a row, or the same file in its findings across three rounds — and the gate escalates the task to a person. On a benchmark project ("benchmark": trueinprojects.json) there is no person waiting and the prediction is the tree, so the fix ships as it stands, recorded as disputed. The same projects get three diff-pattern gates (agent/nodes/diff_patterns.py) that send a fix back once, at the decision point: an invented error message where the existing template was expected, a grammar loosened without a must-still-reject test, a fix applied to one of two same-named functions. - The plan has to be finished before anything is committed. If the agent's own
write_todoslist still has open items, the gate holds the commit and sends it back to finish, naming what's left. Committing mid-plan means the review service reviews a deliberately-incomplete change and correctly reports the not-yet-written pieces as defects — bouncing the task on findings that describe scheduled work. It's a nudge budget, not a hard gate: a model that stops maintaining its own list can't strand working code as an uncommittable diff forever. - Two limits the model can't override: a budget ceiling (
BudgetGuardMiddleware, checked after every model call, on both the coordinator and every subagent) and an iteration/retry ceiling on the work/verify loop. The ceiling is enforced against what the router billed, not a token × rate-table estimate: each call is carried at its estimate only until the router's line for that call id lands (agent/tools/router_ledger.py), then the billed figure replaces it. A planning turn was once ended at "$8.09 spent" when OpenRouter had billed $1.72. - Loops end. A third identical tool call whose two predecessors returned the same result is
answered from cache; the fourth and later are refused; after eight refusals in a row the pass
escalates naming the looping tool (
RepeatCallGuardMiddleware). A call whose result changes (a poll, a flaky test) is never blocked — results are compared with addresses, durations and temp paths normalised away — but eight identical calls in a row are refused whatever they returned. Malformed tool calls are stripped from every model request (SanitizeToolCallsMiddleware) so a truncatedwrite_todoscannot poison every later turn of the thread. ask_userlets the agent pause and ask the operator a clarifying question mid-task instead of guessing, using the same human-in-the-loop interrupt that gates approval-required actions.- A task is never a dead end. Escalated, stopped, budget-exhausted, or orphaned by a backend restart mid-run — each has a way back in. The full graph state lives in the checkpointer, so resuming adds budget and continues the same inner thread rather than starting over. The dashboard detects an orphaned task (the store says "running" but nothing is driving it) and offers a resume instead of leaving it stuck looking busy forever.
Planning Chat
A separate agent (agent/planning_chat.py) for research, design discussion, and scoping a project
before anything gets built. No write/bash access to the real repo — just research tools (web
search, a headless-browser browse_page with screenshots, describe_image, read-only access to
your other projects, search_project / find_files against the real tree, optional GitHub PR
and inbox tools) and save_brief / save_plan. "Build Now" hands a finished plan to the real build
pipeline above, as if it had been typed in directly. The app lands here first: Planning Chat is
the front door, not the raw task composer.
One seat, set by the session's model profile. agent-planning-chat answers every turn. The
profile chosen when the session starts sets its model, and a frontend session's Frontend overlay
goes ahead of that, so a frontend plan is written by the model that will build it (see
Model profiles). Until 2026-10-28 a classifier picked an Easy or Hard seat on
every turn and frontend sessions had a third seat; profiles replaced all three. Whatever the model,
the tool list, memory and permissions are the same.
The brief comes first and stays pinned. save_brief is the only tool a new session can call
until a brief exists (BriefFirstMiddleware); the brief rides in the system message on every call
after that (PinnedBriefMiddleware), so compaction cannot touch it, and it persists with the
session so follow-up turns do not re-force it. Saving the brief also matches the request against
the project's skills and names the architecture skills to read before any file.
Search, then read the window a hit points to. search_project (ripgrep, capped per file and
overall) and find_files (the .gitignore-aware file list) against the real repo. Loop-proofed:
an identical search is answered from cache and refused on the third; zero hits come back with what
was scanned and what to change; a per-turn search budget ends searching with "write the plan from
what you have". Both are dials under Settings → Runtime limits.
The draft gate. After N repo reads without a saved plan (default 50) read_project_file closes
with "save a draft now" and reopens once a plan is saved. A gate-forced save is a checkpoint, never
the end of the turn. Paged reads return at least 500 lines whatever limit asks.
Deliberate constraints, each earned by a real incident:
- No subagents.
create_deep_agentauto-adds a general-purpose subagent (and itstasktool) even when none are declared; planning hides it at both the schema and execution layer (agent/middleware/hidden_tools.py). A hidden delegation primitive once burned a 30-minute turn timeout on nested agent loops. Built-inglob/grepare hidden for the same reason — they search the agent's own memory/skills space, never the repo, and a model will loop on the misleading "No matches found" forever. The repo search issearch_project/find_files. - Codebase-map first. The cartographer's per-project map (
/skills/codebase-map/SKILL.md) is advertised in the prompt and read before any directory walking — one read replaces a dozen exploratory listings. The map refreshes once a day, scheduled by the agent itself at the first quiet moment (agent/jobs.py; hash-gated: an unchanged repo costs a tree walk, no model call), and immediately after every merge+deploy. A companionrecent-changesskill lists the newest commits with their files. - Large files page.
read_project_filesupportsoffset/limit; a truncated read says outright that re-requesting returns identical text and names the exact next call to make. - A per-turn dollar ceiling (Settings → Runtime limits,
planning_turn_budget_usd, default $15; also seedable fromPLANNING_TURN_BUDGET_USD) on top of whatever the session already spent. Planning previously ran uncapped — the one agent with no budget was the one that once spent $7 on a single 157-call turn. On breach the draft plan and real cost are banked and the operator decides whether another turn's allowance is worth it. - A plan written as chat text is not lost. A turn that ends with a plan-shaped final message
and no
save_plancall has that text adopted as the draft (narrow heuristic; an explicit save always wins).
The agent can propose a project that does not exist yet. When a request turns out to be a new application rather than a change to the current project, the plannin






