Repository navigation
Releases
v0.2.18
Routing
Auto-routing no longer falls through to default.backend when a task carries no category signal. A new overall-score stage (profile_overall) picks the backend with the best mean across its scored categories, so measured capability steers conversational turns too — not just the ones that happen to match a keyword.
Measured before this change: 15 of 17 auto-routed turns diagnosed at neutral confidence (0.10) and never reached the capability stages at all, so every one of them landed on the static default no matter what the profiles said.
Learning loop
[learning] auto_fold_every_runs(default 10,0disables) folds runtime telemetry intoprofiles.tomlon its own. The fold was manual-only (agentpit profile learn) and in practice had never run.TurnStartednow carries the run id and the route reason. The TUI shows which stage picked the backend, and/outcome good|badrecords the verdict in place — no screen switch, and it labels the turn on screen rather than whatever run happens to be newest in the log.
Ensemble / review
The aggregator prompt is bounded by a 24k total budget. Oversized member responses are spilled to runs/<run_id>/<backend>.answer.md and cited by path, so the aggregator reads the rest only when it needs it. The existing per-member cap (48k) never fired — the real pressure is the total across members.
v0.2.17
What's changed
- Live tool rows in the TUI — tool and command execution now animates in place with elapsed time, then settles to a green success or red failure result without adding a duplicate completion row. Redacted execution details are preserved safely.
- TypeScript cells from the TUI —
/cell <code>evaluates a TypeScript orchestration cell in the session's persistent Deno sidecar, includingdispatch()support, inline results, type/runtime error distinction, and Esc/Ctrl-C cancellation. - Markdown tables — GFM-style tables render as aligned terminal columns with header rules, alignment markers, inline formatting, escaped/code-span pipes, ragged rows, and CJK-aware widths.
- Classifier-reviewed Claude permissions — supported Claude models use
--permission-mode auto; older pinned models retainacceptEdits. Managed environments that disable auto mode can setAGENTPIT_CLAUDE_PERMISSION_MODE=acceptEdits.
Validation
- Rust workspace: 905 passed, 0 failed, 1 ignored
- Dashboard frontend: 50 passed; production build succeeded
cargo clippy --workspace --all-targets -- -D warningscargo fmt --all -- --check
v0.2.16
Highlights
- Add multiline TUI prompts with Shift/Alt+Enter and Ctrl-J, plus a growing editor.
- Fix multiline bracketed paste so pasted newlines no longer submit separate turns.
- Allow Esc as well as Ctrl-C to cancel an in-flight turn.
- Add Ctrl-R and
/rewind [id], with durable branch restoration across restarts. - Show credential-redacted tool/command previews and explicit succeeded/failed status for Claude, Codex, and Prime Agent.
- Refresh the visible transcript after rewinding to an earlier session point.
v0.2.15
Highlights
- TUI markdown rendering — backend answers now render headings, fenced code blocks, inline
code/bold/italic, lists, quotes, and rules (streaming-safe; also applied when re-attaching to a session) - Mouse-wheel scrolling — the wheel scrolls the transcript (and overlay lists); PageUp/PageDown/End unchanged
@file mentions —@opens project-file completion rooted at the session cwd!backendone-turn routing — the inline backend override moved from@backendto!backend(REPL and TUI), freeing@for file mentions- /config from the TUI — runs the same interactive config as the CLI via the suspend path
Internals
- CI/release workflows now cache with Swatinem/rust-cache
v0.2.14
Slash commands, everywhere they should have been.
Bare agentpit on a TTY opens the TUI, and the TUI had no slash-command system: three literal string comparisons in its Enter handler, everything else forwarded as a conversational turn. Typing /help there cost a real backend call. This release gives both interactive surfaces one registry, the completion UI each can support, and two extension sources on top.
One registry, two surfaces
Name, aliases, usage forms, category, per-surface availability and whether a command can reach a backend now live in one table. The TUI gets a dropdown: / on an empty line opens a filtered popup above the input, ↑↓ select, Enter/Tab accept, Esc dismisses. rustyline cannot render an arrow-selectable popup, so the line REPL gets Tab completion and inline hints over the same registry.
13 more commands
/rescue /refactor /explain /security-review /adversarial-review /learning /arena /profile /similarity /outcome /sessions /doctor /diagnose — each wired to its real CLI signature and offered only where it can honestly run. /outcome is reachable from the TUI, since it attaches the verdict the learned router weights above every other evidence source.
Extensions: SKILL.md and MCP
SKILL.md files under .claude/skills — both the flat and the directory layout, project scope before user — become slash commands. A skill runs as one composed conversational turn, not a subprocess, which is what makes it work on backends that have no skill mechanism of their own.
MCP servers contribute their prompts as /<server>:<prompt> (prompts are the user-initiated primitive; tools are for models). Server definitions come from agentpit's own [mcp.servers] and the project's .mcp.json; agentpit mcp import pulls them out of ~/.claude.json once. Nothing is spawned at startup — /mcp list reads a cache, and only /mcp refresh starts a server.
Two typo bugs fixed
Neither surface honored the rule its own comment claimed. The TUI turned an unknown /word into a backend turn, and the REPL fell through to free-text dispatch, so /frobnicate and a bare /branch were billable calls. Both now refuse with a suggestion.
Rules the extension layer holds to
- An extension can never shadow a built-in —
/reviewstays agentpit's. - Text read off disk is stripped of ANSI/control sequences before it reaches a popup.
- Every composed turn announces its provenance — name, size, source — before it is sent.
v0.2.13
Highlights
Prime Agent backend
- New
prime-agentbackend: dispatched over its JSON event stream (--mode json), with model/thinking flags, canonicalprovider/modelselectors in the dashboard picker, and subscription logins (ChatGPT / GitHub Copilot) recognized by auth preflight. - Provider failures inside an exit-0 stream are now real dispatch errors, never recorded answers; cancellation interrupts before killing so the daemon-side worker shuts down in order.
Session persistence
- Durable conversations: an append-only JSONL session log per session with branching, forking, compaction and crash-evidence journaling — torn tails (even mid-multibyte) are tolerated and sealed on the next append.
- Daemon + workers:
agentpit daemon, per-session detached workers over unix sockets, attach/detach from the CLI and TUI, lazy crash recovery, idle eviction, and strict kill-identity checks (a reused pid never eats a stray SIGKILL). - Native continuation: claude/codex sessions resume with their own ids; composed context windows cover the rest. The newest result decides the resume ref, so a composed fallback is never silently dropped from the backend's view.
/cwdis journaled and resume returns to the session's directory.
Orchestration REPL
agentpit orchestrate: TypeScript cells in a sandboxed Deno sidecar (no net / run / env,--no-remote), withdispatch/store/session host calls, typecheck gating and a runaway-cell idle timeout.
TUI
- Fullscreen ratatui front end with live streaming, session tree/agents overlays, CJK-correct wrapping, and same-connection Ctrl-C that actually cancels the running turn.
Learning
- Relative labels: aggregator grades are compared within a run and only meaningful margins become routing evidence; the dashboard learning view shows the new source mix.
CI
- The similarity (ONNX) suite moved to its own lint+build job after runner-starvation incidents; execution remains a local release gate.
v0.2.12
The arena now checks its submissions, and can land the winner
Both of these came out of using the arena for real work — implementing this release's other two changes — rather than from reasoning about it. Both had to be worked around by hand in the same sitting.
[arena].verify
The arena judges a diff, and a diff does not say whether it builds. A real round produced a submission that failed cargo fmt --check: invisible in the patch, and enough to turn CI red had the tidier-looking change won on looks.
[arena]
verify = "cargo test"Runs inside each contender's own worktree once its work is captured. The verdict appears beside the diff, in the round summary, and again when the patch is applied.
A failed check is reported, never disqualifying. Turning a red check into an automatic loss would quietly replace your verdict with a proxy — which is what the gold bench already is. Here it is one more fact to weigh, and sometimes a one-line formatting slip sits on the better design.
agentpit arena apply
A round's worktrees are gone by the time you judge it; the patch is what survives. Landing a winner used to mean digging it out of the round JSON.
agentpit arena apply # the round's vote winner
agentpit arena apply --label A # or pick one yourselfIt refuses to guess when the votes do not name a winner — a tie, or an unjudged round, is not a decision — refuses on a dirty tree unless forced, and tells you the check verdict as it lands, so a red patch cannot be applied without your knowing.
Fan-out labels land on the member's own variant
Capability rows are keyed by (backend, model, effort). A fan-out run's RouteDecided only carries the run-wide --model/--effort; without those, each member still ran at its own [backends.<id>] default and the log did not say which — so every member's grade landed on the unpinned row. MemberStarted now records the resolved, already-clamped values, and a grade label uses that member's variant. Fallback is per field, so an old events.jsonl folds exactly as before.
Arena rounds run in parallel
Bounded by --concurrency, defaulting to 2 rather than "all of them" — every slot is a full agentic run and multiplies both token spend and local CPU. Results are re-sorted into contender order, so the stored round, and therefore the blind labels, never depend on who finished first.
The two changes above were implemented by backend agents in blind arena rounds and chosen by vote, then reviewed and verified before landing.
v0.2.11
The arena moves into the app
agentpit arena was terminal-only, which left it half-built: a round takes minutes and its votes are the scarcest signal agentpit collects, so the judging has to be somewhere you will actually go.
Settings → アリーナ now owns the whole loop — pick a probe, start a round, judge it blind, read the standings. Two patches side by side, no names, one vote each, and the interval shown next to every rating.
Identity is withheld by the CLI rather than by the UI: arena show omits which backend produced which submission unless --reveal is passed, so the webview never holds the names while a comparison is on screen. Votes are cast by blind label — there is no way to express "vote for codex" through the API, from either front-end. The reveal happens once the last pair is judged, never between comparisons where it would steer the remaining picks.
Ten built-in probes, one per capability cell
Writing a fresh arena task every time does not happen, and an ad-hoc one lands in whatever cell the keyword heuristic picks. The set is now indexed by task category — one probe each, all ten covered — and every rendered task declares its own CATEGORY:, read above every routing gate. Running the ten fills the matrix's category axis by construction; the contenders you enter fill the other.
agentpit arena templates
agentpit arena run --template review/find-defects --target src/dispatch.rs| probe | cell | what it tests |
|---|---|---|
coding/new-unit |
coding | new code to a fixed spec, in the project's own conventions |
refactor/untangle |
refactor | structure improved with behaviour held fixed |
review/find-defects |
review | finding real defects — and not inventing ones |
adversarialreview/trust-nothing |
adversarialreview | reading against the code's own claims |
securityreview/owasp-pass |
securityreview | issues traceable to an attack, by CWE |
debug/root-cause |
debug | the actual cause, not the first silencing change |
explain/how-it-works |
explain | why the code is shaped this way |
docs/user-facing |
docs | written from the outside in |
planning/implementation-plan |
planning | sequencing real work, named files and risks |
longcontext/subtree-inventory |
longcontext | findings that need the whole subtree |
Seven of those categories naturally end in prose, and an empty patch is not a weak submission but a missing one — it would hand the opponent a free win. Each names an output file, so a review arrives as a diff like any other and the blind view stays uniform.
Scriptable arena CLI
arena rounds, arena show [--reveal], arena templates --json, arena leaderboard --json, and a non-interactive arena vote --winner A --loser B / --tie A,B. The desktop app is a client of exactly these.
Fixed
- The BUNDLED CLI card's two absolute paths ran past the edge of their card.
v0.2.10
The terminal and the app stop drifting apart
Installing both the desktop app and the standalone CLI leaves two copies that fall out of sync in silence — the app updates itself while ~/.local/bin/agentpit stays at whatever version it was. That is how a 0.2.5 CLI ends up sitting next to a 0.2.9 app with nothing reporting the mismatch.
Settings → アプリと更新 → BUNDLED CLI now removes the second copy instead. One click puts this bundle's CLI on your PATH, so the terminal runs the same build as the app and updating either one updates both.
Why a shim script and not a symlink
std::env::current_exe() does not resolve symlinks on macOS — measured, not assumed. So agentpit update run through a symlinked CLI hands self_replace the link's own path: the link is overwritten with a plain file, the arrangement silently undoes itself, and the bundle is never re-signed because no .app appears in that path.
exec replaces the process image, so through a shim current_exe() reports the real binary inside the bundle. The updater then replaces the sidecar in place and re-signs — the path it already knew how to handle. Update from either side and both stay correct.
The card will not overwrite a file the app did not write until you ask twice: that file is almost certainly your own standalone install. A hand-made symlink is reported as foreign for the same reason — it is exactly the arrangement that breaks.
agentpit update gained the matching guard: run through a symlink that resolves into an .app, it now refuses with the reason and points you at the desktop app, before spending a download.
Upgrading from a drifted install
If you already have a standalone CLI on PATH, update the desktop app first, then open the BUNDLED CLI card and choose 置き換えて設置. After that the standalone copy is gone and there is only one agentpit.
v0.2.9
Reasoning effort is now a dial you can turn
One canonical ladder — low < medium < high < xhigh < max — mapped to whatever each backend's CLI calls it, in one auditable place. A rung a backend cannot express is clamped down, never up, and the clamped value is what gets recorded, so telemetry says what actually ran rather than what was asked for.
agentpit rescue --backend claude --effort xhigh "…"
agentpit ensemble --effort high "…"
agentpit profile run --backend codex --effort xhighAlso configurable as [backends.<id>].effort and [workflow.roles.<name>].effort, and exposed on the MCP dispatch_task / run_ensemble tools. Precedence mirrors model exactly: flag > role > backend default.
| canonical | claude | codex | agy | opencode |
|---|---|---|---|---|
| low / medium / high | same | same | same | same |
| xhigh | xhigh |
xhigh |
clamped to high |
xhigh |
| max | max |
clamped to xhigh |
clamped to high |
max |
agy binds effort to the model id (gemini-3.6-flash-high) and rejects a separate --effort, so a pinned model wins and says so instead of failing the dispatch.
Capability rows are keyed by what produced them
A capability score was never a property of a CLI's name — it belongs to (backend, model, effort). profiles.toml now keys on the triple, so a high-effort measurement and a low-effort one coexist instead of one silently overwriting the other. An unmeasured variant falls back to the unpinned row and never borrows a sibling rung's number.
Existing files load unchanged: a bare [profile.codex] is the unpinned row it always was.
agentpit arena — blind head-to-head, judged by a human
agentpit arena run --contenders claude,codex "<what to build>"
agentpit arena vote
agentpit arena leaderboardEvery contender builds the same thing in its own git worktree; its work is reduced to a patch; you pick the better one blind and pairwise, with the names withheld until every vote in the round is in. Standings come from a Bradley–Terry fit with a bootstrap interval — a unanimous handful of votes is marked provisional rather than presented as a finding. Votes flow into the ordinary learning fold as MemberGraded, not as a new source that outranks a measured benchmark.
Two long-standing bugs, found by running the real CLIs
Both had passing unit tests the whole time; the tests asserted argument construction, which is not the same as the flag working.
--modelnever reached antigravity.agy --printis an alias for--promptand eats the next token as its value, so any flag after it was swallowed into the prompt.- codex could not write files.
codex execsandboxes to read-only by default and the adapter set none, so codex dispatches described the edit they would have made and changed nothing — while declaring full autonomy. Now--sandbox workspace-write;danger-full-accessstays reserved for the workflow manager.