Talk to a bot. It runs the board.
A team of bots that organize your work as a kanban, from the web app, the terminal or a WhatsApp group.
Workers carry the cards out. A card closes only when the evidence is on it.
Landing page · Install guide · Changelog · Latest release
mini-tui started as a terminal UI for the mini-swe-agent coding agent. It is now a different thing: a team of bots that plan and organize, with workers that do. The bot does not write all the code itself. It turns what you ask into cards on a durable task graph, hands them to workers, checks the evidence, and reports back in the thread you started.
| Before | Now | |
|---|---|---|
| Who works | One agent that types code | Bots that write a board, and workers that take cards |
| Where you talk | A terminal prompt | The web app, the terminal, or a WhatsApp group per bot |
| What "done" means | The agent says it is done | A closure check reads the goal against the diff and the logs |
| How it looks | Panes of transcripts | Silver-metal web app: bots in the sidebar, a kanban, a team map |
| Can it look at a page | No | Yes, through cu: a real browser, with screenshots in the chat |
- Every request becomes cards with dependencies on a durable task graph. Workers take a lease on
a card, a card in flight is not lost when a lane stops, and an expired lease freezes its project
instead of dropping work. See
docs/task-graph.md. - Cards from other bots are visible read-only, through a shared graph layer
(
docs/bot-graph-rag.md). - The sidebar is a tree: window, bots, and the delegates each bot started. The team map draws the same tree as a live graph.
- Each bot can have its own WhatsApp group. It answers there without a mention, and its avatar becomes the group photo.
- The thread is mirrored both ways between WhatsApp and the web app, with no echo and an origin marker on each bubble. Images and attachments travel too.
- Voice notes are transcribed on your machine, and the state is honest when a transcript fails.
- The bot reacts with 👀 the moment it hears you, shows a typing indicator, and uses semantic
reactions when a card closes.
/newsessionstarts a fresh session, and/renamerenames the session and the group subject. - Pair with a phone code or a QR from Settings, Channels.
Details:
docs/channel-multichat.md,docs/channel-mirror.md,docs/channel-consumers.md.
The computer_use tool drives a real browser through a reusable pool of cu
daemons and posts screenshots to the chat. A bot can open the page it just changed, look at it, and
file what it found as evidence on the card. Logins live in cu's vault, and the bot never sees a
password.
A card moves to Done only after a closure check reads the goal against the diff and the logs. The default judge is a model. Point it at Laya, a small self-hosted classifier, and a clean run closes in about a tenth of a second; anything unclear goes to the model judge. A run that the judge keeps rejecting for the same reason ends as a stalemate instead of looping.
export MINI_AGENT_GOAL_ORACLE=laya
export MINI_AGENT_GOAL_ORACLE_URL=http://127.0.0.1:8102/v1/systemoneHow it works: docs/goal-closure.md.
One Docker image for the whole runtime (web, agent, whisper, Python), and provider keys that connect for real — including Kimi for Coding, whose temperature and message-part constraints the connection now handles — with failed connects that say what the provider actually answered. Also: model router with three tiers and a persistent fallback breaker, usage metering per model and provider, generative UI in the chat (forms, tables and charts), a voice-note recorder and player, inline images and attachments, AI session titles, a PWA with cookie login, and a long list of hardening passes on the web server (per-IP rate limits, loopback-only CORS, upload sniffing). Everything is in the changelog.
git clone https://github.com/jaivial/mini-tui && cd mini-tui
bun install
(cd agent-rs && cargo build --release) # one binary, no Python
mkdir -p ~/.local/lib/mini-tui && cp agent-rs/target/release/mini-agent-rs ~/.local/lib/mini-tui/
bun run web # http://127.0.0.1:4317Or run the whole runtime in Docker — same web on 4317, state in your mounted home, tools baked into the image: see Run it in Docker below.
Then open Settings, Providers, and paste a model key. It is tested with one real request before it
is saved, and a refusal names what the provider actually answered. The install guide has the rest,
and the terminal UI is still there: run bin/mini-tui.
The whole runtime also ships as one image — tools in the image, state in your mounted home:
docker compose up -d # the web app on 4317
docker compose --profile channels up -d # + the WhatsApp consumer daemon
docker compose --profile rag up -d # + RAG server + graph projectionThe container is not a sandbox: gh and ssh work inside exactly like in the terminal
outside it (GitHub auth and your ssh keys/config ride the home mount, gh and openssh-client
have the image). Agent sessions can open PRs and reach your ssh hosts from the container. See
docs/docker.md for the whole tour.
Historical closure-check result, 19 October 2026. Ten tasks, three repetitions, MiniMax-M3.1-Flash-Preview on both
sides, pi and mini interleaved task by task so that API drift hits both alike, mini on the deployed
binary and config with Laya as the judge.
| pi | mini-tui | |
|---|---|---|
| Tasks passed | 30/30 | 30/30 |
| Sum of per-task medians | 145.6 s | 112.9 s (0.78x) |
| Tasks where mini is at least as fast | 1/10 | 9/10 |
| Timeouts | 0 | 0 |
Limits: ten tasks, three repetitions, one model, one machine. Recovered from the original completed-run readback; its temporary raw directory is no longer available here. This is not a measurement of the 20 October parity changes. The earlier analysis attributed the gain to a judge that
closes a clean run in a tenth of a second instead of a model call, with a cap on judge
rejections. Before that, 7 of 27 runs with the model judge hit the 900 s timeout. Earlier rounds
were a tie and the report keeps them: docs/pi-vs-mini-benchmark.md.
The 11 October full dev sweep was 62.5% (10/16). A later targeted composite reached 75% (12/16); it was not a fresh full sweep. The single-run 0.69x speed ratio belongs to another experiment and did not hold in the three round-6 repetitions.
The graphics below keep provenance beside every result. Public leaderboard data uses another corpus and is not a like-for-like ranking of mini.
Obsolete, kept for context.
docs/img/time-per-task.png(11 October dev sweep) carries the earlier dev-sweep framing and is referenced by nothing in this repo. It is not a like-for-like view of the corpus above and should not be quoted; it is kept only as a historical artifact until someone decides to archive or drop it.
Everything below is the detailed reference for the terminal UI, the web app, the agents and the configuration.
-
Prompt bar, Claude-Code style. A compact multi-line prompt pinned to the bottom of the screen — long lines wrap to the next row and the box grows with your text (up to 8 rows): type your task (
Alt+Enter/Ctrl+Jfor a hard new line), hit Enter, and keep typing follow-ups while the agent works (or after it submits) — they continue the same conversation. -
Command palette. Typing
/opens real-time completion over the available commands (/model,/settings,/model <id>): filter as you write, pick with↑/↓or a mouse click, fill into the prompt withEnter/Tab(it never sends for you). -
$skill— skills anywhere in the prompt. Type$at any point of the phrase and a panel lists the skills, filtering as you type on any word of the name, in any order ($bodyand$body-prfindpr-body);Enter/Tabinserts$name, painted in the skill color. A prompt likefollow $good-code, then use $better-ui, $pr-body, and finish with $pr-fix-loophands mini every referenced skill's instructions, then your prompt verbatim; the transcript shows just what you typed. Unknown$namesstay plain text. -
Skills inherited from Claude Code. mini-tui keeps its own folder,
~/.config/mini-tui/skills/(override withMINITUI_SKILLS_DIR). At install (bun installruns the sync) and at every startup, each skill in~/.claude/skillsthat folder doesn't have yet is copied in, including symlinked skills and thesynced/<bucket>/layout. Copies are never overwritten, and a skill you delete from mini-tui's folder is not re-imported.bun run sync-skillsruns the sync by hand. -
Bundled skills, carried everywhere. mini-tui ships its own skills in
skills/:$e2e(how agents write and runmini-agent-rs e2etests),$e2e-army(how agents write and run TesterArmye2etests,npx e2ewithe2e.config.ts, whose agent steps run on MiniMaxMiniMax-M3.1-Flash-Previewwith the key from~/.env) and$subagents(how an agent splits work across its own subagents). They are installed into the skills folder at install and at every startup of the TUI,-pand the web server; a newer bundled version replaces an unedited copy, while an edited or deleted one is left as you made it.mini-agent-rs e2e skillprints$e2eon hosts with only the binary. -
The TUI and the web app on the same session. Every UI records the agent it runs for a session. Opening that session elsewhere (the web app, another terminal) follows the same agent live instead of starting a second one: its output streams in as it is written, and a prompt,
/modelor/compactsent from either UI reaches it. Closing one view leaves the agent to the other. Sessions that a terminal continued while the web app held them are reloaded before the next message, so a conversation never forks. -
Subagents (
/subagents). With the Rust agent, a session can split its work across child agents it owns (details)./subagentslists them with their state, steps and cost; each one is saved as a session, so/resumeopens it. -
/compact. Summarizes the conversation now, with or without a run in flight. While it runs, the transcript showsCompacting...and the status linecompacting. Auto-compaction (at 80 % of the context window) shows the same state. -
Only what you sent. The transcript shows your prompt as typed — never the harness' task template wrapping — the final answer is rendered as proper markdown (bold, code, links), and the redundant
exit Submittedecho of it is never shown. -
One quiet card per bash step (command + its output), shadcn-style: zinc neutrals, rounded surfaces, a single subtle accent and semantic colors used sparingly. A single status line under the prompt carries everything: the animated load state while the agent works (
⣾ ▓░░░░ working · 34s), the selected model, the working path and the checked-out git branch. Nothing sits below it. -
Output display modes — collapsed (just a
1 tool callline), trimmed to 2 lines, or fully expanded. The model's thinking follows the same three states (Thinking...while it works,Thought for {n} secondswhen done), and your prompt is echoed into the thread the moment you send it. Pick one in the settings panel (/settings); it persists across runs.estill expands or collapses any block individually. Expanded output is capped at 500 lines with a... N lines hidden ...marker. -
Flat memory, always. The transcript is mounted in a viewport-sized sliding window (two viewports, 24–120 items depending on terminal height;
gpages older ones in,Greturns to the live bottom) and every text block is clipped, so an all-day run keeps a flat footprint instead of growing with the conversation. -
Six themes —
shadcn(zinc, default) plusnord,dracula,gruvbox,tokyo nightandcatppuccin. Also in/settings(Tabswitches group): moving the selection repaints the whole UI live and the choice persists. -
/resume— saved sessions in SQLite. Every conversation is stored (~/.config/mini-tui/sessions.db) with an AI-generated title (falling back to your prompt, trimmed)./resumeopens a modal listing the sessions started in the current folder, paged and searchable by title — pick one to restore its transcript, and your next prompt continues the same conversation with full context (mini --resume, companion patch 3). Not sure which one it was?→(or clicking[→ preview]) shows the selected session's transcript read-only inside the same panel — scroll it with↑/↓,PgUp/PgDn,g/G;←/Escgoes back to the list,Enteropens it. -
/connect— BYOK providers. The whole MiniMax Code catalog — Xiaomi MiMo, DeepSeek, OpenCode Go, Z.AI (GLM coding), MiniMax — plus OpenAI, Anthropic, Moonshot, Zhipu, Groq and OpenRouter. Pick a provider, paste your API key, choose a model and the connection is tested for real (a one-token query through mini's own model layer) before being saved locally. Every catalog model of a connected provider joins the/modelpicker, and its key is injected into your runs, including runs the agent starts itself (subagents, a model switch mid-run). Every provider runs on its own direct base URL (xiaomi/…,deepseek/…,anthropic/…, …) with a dedicated key/base env slot — no litellm. Saved connections on the genericopenai/slot (OPENAI_API_BASE/OPENAI_API_KEY) keep working for any OpenAI-compatible endpoint. -
Select-to-copy. Highlight any text in the UI with the mouse and it lands on your clipboard the moment you release — transcripts, commands, outputs, whatever. It works inside tmux (OSC 52 passthrough + the tmux paste buffer,
prefix+]) and falls back towl-copy/xclip/xsel/pbcopywhen available. -
Modals, not layout shifts.
/model,/settings,/resume,/connectand/helpopen as floating modals centered over the transcript — the prompt bar and the bottom stack never move, and panels clip to the available area on short terminals. -
/helplists every command and key in one panel. -
Tool call cards with syntax-highlighted bash commands and a quiet focus marker.
-
Output cards with
rc=0/rc=Nbadges, exception info, and collapsible bodies (… 220 lines hidden · [e] expand) so huge outputs never blow up the layout.- Live header with model, step count, running cost and a spinner while a step is in flight. -
/model— switch models mid-conversation. Type/modelin the prompt to open the model picker (or/model <id>to jump straight to one). During a live run the agent picks the new model up from its next step — no restart, no lost context. The last model you pick (in any session) is remembered in~/.config/mini-tui/last-model.jsonand becomes the default of new mini-tui terminals; windows that are already open keep their own model.-mand$MINITUI_MODELstill take precedence. -
Agent commits are authored by you. Runs export
GIT_AUTHOR_*/GIT_COMMITTER_*from the account logged intogh(GitHub noreply email), falling back togit config --global user.*— commits never show up as "claude".MINITUI_GIT_IDENTITY=0disables it. -
Plain-text final answers (with the companion patch below): the agent finishes by simply answering instead of shuttling its answer through a temporary markdown file. The answer lands in the green
exitbanner, and you can type a follow-up to keep going. -
viewmode renders any existing trajectory (including~/.config/mini-swe-agent/last_mini_run.traj.json) with the same renderer.
The same agent, in a browser, with every session running at once in panes and windows:
- split the screen like tmux, up to 12 panes in a window;
- group panes into as many windows as you like, each with a working, done or idle dot per pane in the sidebar;
- move any pane to another window from its menu, rearrange the panes of a window (a menu step in each direction, a row / column / grid layout, or drag one onto another to swap them);
- resize the notes of a session freely inside its pane, the width kept per pane;
- keep the same layout on every device (it lives on the server).
Each session streams over its own WebSocket, and a remote mode keeps the agent headless on a server over SSH while the UI stays on your machine.
![]() |
![]() |
| Move a pane to another window, or to a new one. | The same windows on a phone. |
bun run web # build the UI and serve everything on http://127.0.0.1:4317
bun run web:dev # Vite dev server, hot reloadSessions started in the browser land in the same ~/.config/mini-tui/sessions.db the terminal UI's
/resume reads, so both front ends see each other. A session running in a terminal opens live in
the browser: the web app follows that terminal's agent (its output streams in, your prompts go to
it) instead of showing a frozen copy or starting a second agent, and the terminal does the same for
a session the browser runs. Opening a session never fails because a terminal is saving at that
moment: the database runs in WAL mode, so readers never wait for a writer. /resume works in the browser too, even from a new
chat with nothing running: it lists every saved session, searchable, and sending a message continues the
one you pick.
What the web app does:
- History in the sidebar, by recency or grouped by folder.
- A folder picker for new chats that also browses remote hosts.
- Prompt memory with
↑/↓. - A toast when a session finishes, with a View button that jumps to it.
- Panes like tmux (
Ctrl+\), up to 12 in a window, and as many windows as you like: the sidebar lists them with a pane count and a New window button, and any pane can move to another window or to a new one from its pane menu. - A side panel in every pane, with two tabs:
- Notes, saved as you type.
- Terminal, a real shell in the session's folder (over SSH for a remote session) that survives
hiding the panel and reloading the page. Open it with
Ctrl+`or/terminal.
- Settings with an interface size and a separate text size.
- Subagents above the transcript. A session whose agent started subagents shows them in a strip
under its header, each with a status dot, its steps and its cost. Click one to follow it live in
the pane (and message it like any session); its own strip links back to the parent.
/subagents [name]lists them or opens one.
One workspace on every device: the windows, panes and sidebar are kept on the server, so a phone, a laptop and a second tab all show the same thing, and a change on one appears on the others live.
Notes, the terminal, the shared workspace and prompt memory all travel over one hub WebSocket per tab, with no REST polling. A server restart (a deploy) does not lose your panes: sessions reopen from the saved history when the page reconnects.
The terminal is a shell as the user the server runs as, so keep the server behind authentication,
or turn the terminal off with MINITUI_WEB_TERMINAL=0. Remote runs need mini-tui on the server;
the hosts panel has a Test button that checks reachability and agent presence.
To run it as a service, deploy/mini-tui-web.service is a systemd
unit. Agents and the terminal can use sudo there just as in the terminal UI, limited by the account's
own sudoers rules. Do not add NoNewPrivileges=true: it makes every sudo fail.
|
A new chat is a draft. |
Commands and skills. |
|
Model switcher. Searchable and grouped by provider, fully keyboard-driven, and it opens from the prompt bar. A model missing from the list can be typed. |
Settings and providers. Test a key with a real request before it is saved. Keys never come back out of the server: reads carry a masked hint. |
The prompt bar is one line when empty and grows a line at a time up to five, then scrolls. Chips keep the field a plain textarea, so IME, autocorrect and paste all keep working.
![]() |
![]() |
Built for touch as well as a keyboard: 44px targets on a finger, bottom-sheet dialogs, safe-area insets and
dvh layout. Light and dark, six accent palettes. Full details: web/README.md.
site/ is the project's landing page and documentation: SvelteKit prerendered to static HTML (every route
is a real .html file, readable with JavaScript off), Tailwind 4 with shadcn-svelte components, and
scroll-reveal motion that only ever hides content it is about to animate in.
cd site && bun install
bun run dev # http://127.0.0.1:4320
bun run build # prerender every route to site/build
bun run seo # audit the built pages: titles, descriptions, canonicals, JSON-LD, alt text, links, sitemap
bun run browse # real-browser check at desktop / tablet / phone
bun run layout # width audit: no sideways scroll, no squeezed column, at 320 to 1440 pxThe canonical origin comes from VITE_SITE_URL, and the base path is derived from it, so links, canonical
tags and the sitemap cannot disagree. .github/workflows/site.yml runs the audits and publishes to GitHub
Pages on every push to main that touches the site. Docs are markdown in site/src/content/docs/.
Six palettes — pick one in /settings (Tab to the theme group): moving the selection repaints
the whole UI live and the choice persists.
| shadcn (default) | nord | dracula |
|---|---|---|
![]() |
![]() |
![]() |
| gruvbox | tokyo night | catppuccin |
|---|---|---|
![]() |
![]() |
![]() |
-
Bun ≥ 1.3 (OpenTUI ships a native Zig renderer; Node ≥ 26.4 also works)
-
An agent, one of:
- Python ≥ 3.10 with the bundled agent installed (
pip install -e ./agent), or anyminion yourPATH; - the Rust agent: no Python needed, either a
release binary or
cargo build --releaseinagent-rs/.
~/.config/mini-swe-agent/.envis honored either way. - Python ≥ 3.10 with the bundled agent installed (
git clone https://github.com/jaivial/mini-tui && cd mini-tui
bun install
python3 -m pip install -e ./agent # bundled mini-swe-agent → `mini` + `mini-swe-agent-tui`
# optional: make `mini-tui` available everywhere
cp bin/mini-tui ~/.local/bin/mini-tui && chmod +x ~/.local/bin/mini-tuiWithout Python, download the static Rust agent instead of the pip install step. mini-tui finds it
there on its own:
mkdir -p ~/.local/lib/mini-tui
curl -Lo ~/.local/lib/mini-tui/mini-agent-rs https://github.com/jaivial/mini-tui/releases/latest/download/mini-agent-rs-x86_64-linux-musl
chmod +x ~/.local/lib/mini-tui/mini-agent-rs# Just start it: the prompt bar is ready (like `mini -y -m <model>`, but pretty)
mini-tui
mini-tui -m xiaomi/mimo-v2.6-flash
# Or start immediately with a task
mini-tui run "Fix the failing test in test_utils.py" -m xiaomi/mimo-v2.6-flash
# Render an existing trajectory (add --follow to keep watching it)
mini-tui view ~/.config/mini-swe-agent/last_mini_run.traj.json
# Include the system prompt in the transcript
mini-tui run ... --show-system-p / --print runs a session with no UI, like claude -p, opencode run or pi -p: the
same integrated agent (tools, $skills, /connect providers, follow-up context) runs one turn,
the answer goes to stdout, and the process exits with the run's status. Scripts, CI jobs, cron
and other agents can drive mini-tui this way.
# Only the final answer on stdout (quiet by default)
mini-tui -p "Fix the failing test in test_utils.py" -m xiaomi/mimo-v2.6-flash
# Verbose: every step (commands, outputs, notices) streams to stderr, the answer to stdout
mini-tui -p "why is the build red?" -v
# Machine-readable: one JSON result, or JSON lines as the run happens
mini-tui -p "list the TODOs" --json | jq -r .result
mini-tui -p "refactor utils.py" -o stream-json | jq -c 'select(.type=="tool_call")'
# Pipe context in (stdin is appended to the prompt, or is the prompt)
git diff | mini-tui -p "review this diff"
# Sessions continue exactly like /resume in the TUI
mini-tui -p "now add tests" --continue # latest session of this folder
mini-tui -p "and the docs" --resume s-mugx # any session, id or unique prefix
mini-tui -p --compact --continue # /compact from the shell
# Guard rails for unattended runs
mini-tui -p "..." --max-steps 30 --cost-limit 1 --timeout 900 --cwd ~/repo --no-session| Option | Meaning |
|---|---|
-m, --model <id> |
model of this run (default: $MINITUI_MODEL, the resumed session's model, else the last /model pick) |
-o, --output-format |
text (default) · json · stream-json (--json = -o json) |
-v, --verbose |
stream every step: text → stderr; json → adds events; stream-json → thinking and full outputs |
-q, --quiet |
the answer only: no error tail on stderr |
-C, --continue / -r, --resume <id> |
follow up in the latest / a given saved session |
--compact |
compact a --continue/--resume session instead of sending a prompt |
--no-session |
don't save the run to the /resume history |
--cwd <dir> |
working directory of the run |
--max-steps, --cost-limit, --timeout |
agent.step_limit, agent.cost_limit, agent.wall_time_limit_seconds |
-c, --config <spec> |
extra mini config (key=value specs merge into the default config) |
Exit codes: 0 submitted, 1 the run failed or hit a limit, 2 usage error, 130
interrupted (Ctrl+C / SIGINT once interrupts the agent, twice kills it).
The json result (also the last stream-json line) carries result, subtype
(success/error/interrupted), is_error, exit_status, session_id, model, cwd,
num_steps, api_calls, cost_usd, duration_ms, trajectory_path, log_path and, on
failure, error. stream-json starts with an init line (session id, model, runner, paths),
then one line per transcript event (task, assistant, tool_call, observation, notice,
exit, and thinking with -v).
Everything the TUI's slash commands do also works from a shell (add --json to any of them):
mini-tui sessions [--all] [-n 20] [-s text] # /resume history (this folder, or all)
mini-tui sessions show <id> # print a transcript
mini-tui sessions rm <id> # delete a session
mini-tui models # /model catalog + /connect providers (* = default)
mini-tui model [<id>] # print / set the default model (what /model saves)
mini-tui skills # $skills
mini-tui settings [output-mode|theme <value>] # /settings
mini-tui --versionRun artifacts live under ~/.config/mini-tui/runs/<timestamp>-<slug>/
(traj.json + its append-only traj.jsonl journal, mini.log, pid, control). mini-tui never
touches ~/.config/mini-swe-agent/last_mini_run.traj.json — it always passes its own -o.
| Key | Action |
|---|---|
Enter |
send the prompt (starts a task, or continues the running conversation) |
Alt+Enter / Ctrl+J |
insert a newline in the prompt (Shift+Enter too where the terminal reports it) |
/ |
opens the command palette: ↑/↓ or click to select · Enter/Tab fills the prompt (never sends) |
$ |
anywhere in the prompt: opens the skills panel (filters as you type) · Enter/Tab inserts $name |
/compact |
summarize the conversation now (frees context) |
/subagents |
list the subagents this session started (each is a session: /resume opens it) |
↑ / ↓ (in the prompt) |
browse the prompts sent in this session · ↓ past the newest restores your draft |
Esc |
close the palette / leave the prompt · double Esc interrupts the run |
ctrl+c |
clear the prompt · press it twice (within 1.5 s) to close |
ctrl+\ |
show / hide the error console (Esc closes it) |
/quit · /exit |
close mini-tui |
/help |
commands and keys |
/model |
open the model picker (or /model <id> for a direct switch) |
/settings |
output display (collapsed / trimmed / expanded) |
/new |
start a fresh session in place (stops the current run; model and settings stay) |
/resume |
browse sessions saved in this folder (search + pages, → previews a transcript read-only) |
/connect |
connect a BYOK provider (key → model → tested connection) |
j / k (or ↓ / ↑) |
(navigation mode) move between tool call blocks |
e |
expand / collapse the focused output block |
PgUp / PgDn |
scroll half a screen · mouse wheel scrolls 3–5× (burst-accelerated) |
g / G |
load older steps (viewport-sized pages) / back to the live bottom |
mini-tui run
├─ src/mini/spawn.ts starts `mini-swe-agent-tui` (or falls back to `mini`)
│ raw stdout/stderr → <session>/mini.log
├─ src/traj/watch.ts polls <session>/traj.json every 200 ms (rewritten by the harness per step)
├─ src/traj/parse.ts pure parser: messages → RunEvent[] (tools, outputs, notices, exit)
└─ src/ui/ OpenTUI + React renderer (cards, badges, banners, prompt, /model picker)
The parser tolerates mid-write reads (the harness rewrites the file non-atomically after every step), unknown message shapes, and multimodal content in any of the message formats mini produces.
mini-tui appends MESSAGE <text> / MODEL <id> lines to the run's control file
(<session>/control, exported to mini as MSWEA_CONTROL_FILE). The companion harness patch:
- injects
MESSAGElines asUserNewTaskprompts — from the agent's next step mid-run, and after a submission via an exit hold (the run stays open so one conversation can span many turns); - applies
MODELswitches from the next step.
Each UI that starts an agent records it in the live_runs table of sessions.db (session,
trajectory, control file, pid). Another UI opening that session checks that the pid still runs that
trajectory, then follows it the same way: it reads the same journal and writes to the same control
file. So the web app, the terminal and a subagent's parent all drive one process, and whichever UI
started it decides when it ends.
agent-rs/ is a Rust port of the bundled agent's runner. It uses the same command line, configs,
trajectory, journal and control file, packaged as one self-contained binary.
| Python agent | Rust agent | |
|---|---|---|
| Start a run | ~190 ms | ~2 ms |
| A session waiting for your next message | ~36 MB | ~4 MB |
| Needs | Python ≥ 3.10 + the agent's packages | nothing (static build: any x86-64 Linux) |
cd agent-rs && cargo build --release # or: --target x86_64-unknown-linux-musl (static)
mini-tui # uses it: Rust is the default once the binary is there
MINITUI_AGENT=python mini-tui # the Python agent insteadPrebuilt binaries (mini-agent-rs-x86_64-linux-musl, …-linux-gnu, SHA256SUMS) are attached to
every release.
Does the Rust agent use more tool calls, or take longer? No. Both agents send the model
byte-identical prompts, so the model batches commands into replies exactly the same way and neither
one makes it do more work. Two parity scenarios lock that in: turns (a model that batches several
commands per reply must produce the same turns, tool calls and trajectory on both) and
batch-calls (the same over the wire, with every request body compared byte for byte). The Rust
agent also gets to its first model request sooner (~7 ms vs ~173 ms). If two runs of the same task
really do differ, the difference is on the model side — sampling and context pressure decide how
much a model batches per reply — and not in the runner.
Rust is the default for the terminal UI, mini-tui -p and the web app, whenever the binary is found:
MINITUI_AGENT_BIN, else agent-rs/target/release/mini-agent-rs, else ~/.local/lib/mini-tui/mini-agent-rs,
else mini-agent-rs on PATH. The binary also generates session titles and runs the providers panel's
connection tests, so nothing runs through Python. Without a binary, mini-tui uses the Python agent as
before; MINITUI_AGENT=python chooses it on purpose, and MINITUI_AGENT=rust says so when the binary is
missing. mini-tui points the binary at the bundled YAML configs (MINI_AGENT_CONFIG_DIR), so a copy
installed outside the checkout works too.
It covers everything mini-tui uses:
- The loop: limits, follow-ups,
/model,/compactand automatic compaction,--resume, interrupts. - Models: cli-proxy, Rosetta, DeepSeek, Xiaomi, OpenAI, Anthropic, the provider registry and OpenCode Go.
- Environments: local and docker.
agent-rs/tests/parity/run_all.sh runs both agents on the same scripted tasks and scripted HTTP
servers: 31 scenarios plus 8 helper cases. It checks that they write identical trajectories, exit the
same way and send identical requests. See agent-rs/README.md for what is and
is not ported.
A session on the Rust agent can split its work across subagents it owns. The model drives them
from its bash tool, and the bundled $subagents skill teaches it how:
mini-agent-rs agent spawn api "write the API tests" --cwd ~/repo --max-steps 60
mini-agent-rs agent send api "also cover the 404 path" # mid-run, or continue a finished one
mini-agent-rs agent resources # free memory, avg child cost, how many more may start
mini-agent-rs agent ls | wait | result | tail | model | stop
mini-agent-rs agent ask "which branch?" # inside a subagent: ask the parent- One process per subagent. It keeps its whole context between turns, so a follow-up is a single message, not a new run replaying the history. A message sent mid-run lands before its next step. One that exited continues from its saved conversation under the same name.
- It reports back by itself. When a subagent finishes, fails, stalls or asks something, the
parent reads a
[subagent <name>] …message before its next step. A parent that already ended its turn is woken by it, so the orchestrator never spends steps polling. - Owned by the session. Subagents stop with it, count toward its cost limit, and run on its
current model unless told otherwise. Up to 100 run at once per hub, with resource admission guards. Recursive depth has no fixed default cap; set
MINI_AGENT_MAX_DEPTH=Nfor an explicit cap (0means uncapped). Depth alone does not bypass memory, concurrency, cancellation or provider limits. - It never OOMs the box. Before you fan out,
agent resourcesreports the free memory, the measured average memory of the running subagents, andmax fan-out now:min((free - 10 GiB reserve) / average child, CPU headroom, 100 - live). Everyspawnruns the same calculation at the gate, so a child that would not fit is refused — the session, and every other child, keeps running. - Visible everywhere. Each subagent is saved as a session under its parent: the web app shows
them above the transcript, the TUI lists them with
/subagents, and any of them opens live. - Your connected models, no extra key. A subagent on another model (
-m minimax/...) gets that model's key from your/connectproviders (~/.config/mini-tui/providers.json), like a run you start yourself. Nothing has to be exported first. A variable you did set still wins. - The parent reviews before it accepts. A subagent's summary is a claim. The
$subagentsskill has the parent check the diff, the build and the tests itself. When something is missing or wrong, the parent sends the review back to that same subagent (agent send <name> "<review>"): each finding with its file and line, and the check to rerun. The subagent fixes the work with all its context, and the parent reviews again until nothing is left. After 3 rounds stuck on one finding it moves that subagent to a stronger model (agent model).
A run that starts no subagent behaves exactly like the Python agent: the parity suite still
compares every scenario byte for byte. $orchestration's orch forwards its commands to the
session's subagents when it runs inside a Rust session. Its detached headless runs remain for your
own terminal and the Python agent. Full reference: agent-rs/README.md.
With the Rust agent, mini-tui gives the model five native tools on top of bash:
agent_define (a named specialist: description, when to use it, its system prompt, its model),
agent_route (which agent fits a task), agent_delegate (one task, or tasks to fan out),
agent_message (to the parent, a sibling or a subagent) and agent_handoff (finish your part and
pass the work to the next agent). A general-purpose agent, general, is built in; definitions the
model creates live in ~/.config/mini-tui/agents/.
- Every agent is a session. It starts as a byte-for-byte copy of the session that started it
(the delegating one, or for a handoff the agent handing over) with the model switched to its
own, plus its task appended after the copy. The copy is the prefix the provider has cached: on
the same model the source's last call already wrote it; on another model the hub first warms
that model's cache with the copy (one call,
max_tokens1) and starts the agents once it is written. Each agent's first-call cached tokens are measured and shown. - Background, never blocking. Delegating returns at once; the session keeps working and is told when an agent finishes. Messages land before the recipient's next step, or wake it.
- Chains between peers.
translatorhands off toreviewer; the reviewer messages the translator its corrections and waits; the translator's answer goes back to the reviewer (not to the parent); the reviewer hands off todeployer. The parent is woken when the chain ends. - Recursive fan-out. An agent can delegate to subagents of its own (
login->backend+frontend); its turn end while they work is a wait, not a report. - Agents panel (web). The side panel's Agents tab draws the tree (parent -> children -> grandchildren) with live state, model, steps, cost and cache reuse, and the timeline of delegations, handoffs, corrections, replies and fan-outs. Any agent opens as its own session.
A subagent that re-reads what the orchestrator already read is the single biggest cost in a fan-out. Measured on a real orchestration (6 subagents, 2 repos, 2026-10-07): 122,039 of 184,342 tokens (66%) were discovery, 28 files were re-read that the parent had already read, and 3 integration failures were found by the parent reading diffs, not by any subagent.
So the exploration is paid for once and handed over, in the run tree's context/ folder,
with a schema instead of a social contract:
# The orchestrator fills it, after exploring and before delegating:
mini-agent-rs agent state put findings.md --prompt-file f.md # symbols, files, lines found
mini-agent-rs agent state put contracts.md --prompt-file c.md # the contract + producers/consumers
mini-agent-rs agent state put decisions.md --prompt-file d.md # what was decided and why
mini-agent-rs agent state put surface.backend.md --prompt-file s.md # callers, types, payloads
mini-agent-rs agent spawn api-worker --cwd ~/repo/backend --repo backend "add the field"-
Handed over by default. A child receives the documents that name the repos it works in, scoped: a backend child gets
surface.backend.mdand the## backendsections offindings.md/contracts.md, and is not billed for the frontend's. It happens whether or not the parent remembered--context-file—--repo Rnames the repos explicitly, otherwise they are read from the child's own--cwd.MINI_AGENT_CONTEXT=0turns it off. -
Cheap to consult.
mini-agent-rs agent surface <symbol>answers "who calls this and which contract does it carry" out of what is already indexed, in one call, instead of grepping the tree again.agent surface lslists what is indexed. -
The handshake goes back. When a child's turn ends the hub parses its own final answer into three lines — what it touched and who calls it, what contract field it emitted or read, and anything that contradicted the brief — and stores it as
<child>.mdfor the next child. In a live run, thatsurpriseline is how a subagent reported that the backend emittedgroup_menu_enabledwhile the frontend struct expectedgroupMenuEnabled, with no mapping layer: a real integration failure, caught at the child's own turn instead of by the parent reading diffs. -
Checked mechanically, not by reminder.
mini-agent-rs agent contract-check --repo Rruns by itself when a child's turn ends: every file it claims to touch exists and is under the repo, every field the contract names for that repo appears where it should, and ano <invariant>clause is checked literally against the touched files. No model in the loop. A claim that does not hold fails loudly instead of reaching the parent as a summary. There is a fourth check,coverage, for the failure mode that matters most here: a check that examined nothing must never be able to say pass. If no file was claimed and no clause of the contract names the repo, it fails and says "This is NOT a pass". Five separate ways to get a green check over an empty corpus were found by running real orchestrations — a child's prose without asurface:tag, a comma inside a sentence inventing a file name, the wordThereread as a field name,--repoparsed into a key the arm never read, and a child's worktree directory mistaken for its repo. All five are fixed; the shape they share is that a mechanical check was parsing free text with a rule free text does not obey.Two more defects in the same arm came out of the same probing (fixed in 0.38.2):
--prompt-filewas ignored (so a control was answered about a different child's text), and the positional handshake was dropped whenever--repowas given. All three invocation forms are now verified to examine the text they are handed.The best proof that it works came from an agent, not from a test: an orchestrator received
4/4 pass, did not trust it, copied the tree to/tmp, deleted the wire field, re-ran the check, got4/4 passagain — and reported its own integration check as toothless. That negative test is how the last defect was found.
Measured on a real orchestration driven in the web UI with subagents (same task, 2 repos, orchestrator + 2 children), against the run without the store:
| without | with | |
|---|---|---|
| child discovery tokens | ~3,197 | ~1,456 (-54%) |
| orchestrator tokens | ~26,699 | ~12,093 (-55%) |
| integration bugs caught by a subagent | 0 | 1 (a snake_case/camelCase mismatch) |
Duplicate reads do not go down, and that is expected: a child still reads the file it has to edit. What the store removes is the orientation work around it.
The clearest measurement of the fix, same 6-file two-repo task, orchestrator exploring once:
| before | after | |
|---|---|---|
| discovery tokens per subagent | ~1,962 | ~0 |
| subagent steps | 40 | 28 |
Fewer steps for more work. The honest caveat: the orchestrator pays for the exploration up front, so on a repo this small its own cost still roughly matches what a child would have spent, and total tokens went up. It amortises with more children or a bigger repo — which is exactly the shape the baseline measurement (66% of tokens spent rediscovering) came from.
Two delivery paths, one message. The store is not the only context a child gets at launch: the
local graph RAG (MINI_AGENT_GRAPH_RAG_URL, docs/semantic-delegation.md) adds retrieved
historical evidence in a <graph-context> block right after the <context> blocks. They are
independent switches — MINI_AGENT_CONTEXT=0 turns off only the store, unsetting the URL turns
off only the retrieval — and since 2026-10-21 they are deduplicated at the launch point: a hit
whose text is already in the delivered blocks/brief/task is dropped instead of delivered twice
(dedup_skipped in the graph_retrieval event audit, and no empty <graph-context> block when
every hit was already there).
The Rust binary also carries a small RLM (Recursive Language Model) harness in pure Rust —
experimental, behind its own opt-in subcommand (nothing else in the binary couples to it): an
execution framework that treats long-horizon context as program variables and sub-agents as
function calls, inside a persistent REPL. A script (or the rlm> prompt) binds what matters with
let, and call runs a whole sub-agent to completion and hands its answer back as a value —
recursively, up to a depth cap:
mini-agent-rs rlm # interactive; variables and functions auto-persist
mini-agent-rs rlm script.rlm # or run a script (exit 1 on the first error)let repo = mini-tui # context is variables; {{repo}} interpolates (strict Jinja)
def worker(q) # a sub-agent is a function call
let r = ask {{q}} # one turn: model + bash tool until it answers
return {{r}} # the answer is the call's value
end
let out = call worker(check the tool)
ask is one model turn on the current conversation, run is shell, set writes globals from
inside a function, and the state (rlm.json) survives restarts. No Python, no framework: the
same model layer the agent uses. Full reference:
agent-rs/README.md.
agent/ vendors mini-swe-agent (v2.4.6 base via
git subtree --squash) together with everything mini-tui's nicest behaviors need, so
pip install -e ./agent is one install that carries:
- Integrated runner —
mini-swe-agent-tuishares the public CLI's config merge, model loop, journal, resume format, andMSWEA_CONTROL_FILEprotocol, but skips Typer, Rich, prompt interaction, and the interactive-agent module on the normal yolo path.mini-tuiprobes for it once;MINITUI_EMBEDDED_AGENT=0forces the compatibleminiCLI fallback. - Plain-text final answer — a response with text and no tool calls is the submission
(no more
cat > /tmp/final_answer.mdround-trips). - Control file —
MODEL <id>switches the running agent's model from its next step;MESSAGE <text>continues the conversation mid-run or at the exit hold (both no-ops whenMSWEA_CONTROL_FILEis unset). --resume— reloads a previous conversation's message history as context and treats the new task as a follow-up (what/resume+ prompt uses).- Custom model providers — xiaomi (MiMo), rosetta, cliproxy, deepseek, opencode_go and
openai model classes, with
mini extra <provider>-modelsto list their catalogs. - Append-only trajectory journal —
<traj>.jsonlgets one line per message (O(1) per step, never torn) and the fulltraj.jsonexport is compact, atomic and throttled; mini-tui reads the journal and parses only the new bytes per tick. - Slim default install — the direct HTTP clients do not need the
openaiSDK; benchmark datasets live behind thebenchmarksextra (mini-swe-agent[full]includes it).
Upstream sync: git subtree pull --prefix=agent --squash <upstream> <ref>. The original patch
descriptions (kept for upstreaming) live in
docs/mini-swe-agent-patches.md.
Trajectory persistence, measured on a synthetic 300-step run (the agent saves after every step):
| full rewrite per step (old) | append-only journal + throttled export (now) | |
|---|---|---|
| Bytes written | 235.4 MB | 4.3 MB (55× less) |
| Persistence time | 0.99 s | 0.12 s (8× faster) |
The TUI itself is bounded and lean: the transcript mounts a viewport-sized sliding window
(two viewports, 24–120 items depending on terminal height), so memory stays flat no matter how long
a run gets; g pages older items in and G returns to the live bottom. The 0.3.0 render pass then
cut the measured footprint by ~30 % across the board — on the standard 400-step repro: 18.6 s →
13.1 s CPU and peak RSS 323 → 228 MB. In 0.5.0 blocks got 3.6× leaner (plain text unless the
text really has markdown — measured 0.33 → 0.09 MB per block): the same repro now settles at
205 MB, and a plain-prose run at 152 MB. Agent-side, prompt_toolkit loads lazily (35 → 20 MB
import). In 0.7.0 the local/subscription gateways (cliproxy/, rosetta/, xiaomi/) talk to
their OpenAI-compatible endpoint directly instead of through litellm: a real claude-opus-5-5 run
peaks at 40 MB instead of 214 MB and answers ~2.3 s sooner. Since 0.9.0 every provider
(DeepSeek, OpenAI, Anthropic, OpenCode Go included) talks to its base URL directly and litellm is
an optional extra (docs/PLAN-litellm-detach.md).
pi-parity speed work (docs/pi-speed-analysis.md): the loop's
own overhead is 0.027 s/step (median, measured on 28 headless runs) while 83 % of wall time is
the model. Three of the remaining losses are now closed: retries honour Retry-After /
retry-after-ms first and use pi's min(0.5 × 2^i, 8) s shape for throttles (measured corpus:
392 s of waiting → ~136 s); a step's independent commands run overlapped (up to
MINI_AGENT_PARALLEL_ACTIONS, default 2) instead of strictly serially, results kept in input
order; and the journal no longer clones the whole conversation per step (serialize_info +
the unjournaled tail, max(20, len/10) messages or 60 s between full exports).
0.13.0 integrated-runner benchmark (scripts/benchmark-runtime.py, seven fresh Python 3.10
processes, deterministic one-step run, no network): public mini ~205–212 ms / ~39 MiB peak RSS;
bundled mini-swe-agent-tui ~160–170 ms / ~34 MiB — about 20% faster and roughly 5 MiB less resident
memory. The measurement excludes the TUI process itself; it covers the agent startup/run path
that the TUI launches. Run python3 scripts/benchmark-runtime.py --runs 7 to reproduce.
The TUI ingestion path has its own terminal-free benchmark: bun run benchmark:tui -- 5000
appends 10,002 realistic trajectory messages and measures only the incremental parser (about
11–12 ms on the current machine). The randomized item-index tests cover tool/observation pairing
fallbacks and prefix replacement, so long-run CPU growth is measurable without opening a TTY.
See docs/PLAN-ram-reduction.md for the plan and numbers.
bun test # parser fixtures + in-memory OpenTUI render tests (no TTY, no network)
bun run typecheck
bun run screenshots # regenerates docs/screenshots/*.png from scripted scenes
bun run benchmark:tui -- 5000 # terminal-free long-run parser + item-index benchmark
MINITUI_AGENT=python bun test # the same suite on the Python agent (Rust when its binary is built)
cd web && bun run e2e # the web app in a real browser (see web/README.md for the others)
cd web && bun run build && bun scripts/bundle-report.mjs # chunk sizes, and what a cold visit downloads
cd agent-rs && cargo test --release && sh tests/parity/run_all.sh # Rust agent: unit + parity- Yolo only: runs use the integrated agent entry (or the compatible
mini -y --exit-immediatelyfallback) — the UI visualizes and forwards prompts, it never confirms or rejects commands. - No token streaming in the terminal UI: the transcript updates once per step, so the spinner covers model calls and command execution. The web app shows the reply as it streams. Follow-ups sent mid-step are picked up at the next step.
- After a submission the run stays open ("type to continue") while the TUI is attached; quitting the TUI ends it.
- The code highlighter degrades to plain text when a tree-sitter grammar is unavailable.
- If the TUI is killed hard, the agent child may survive: check
<session>/pidandpgrep -af "minisweagent.run.tui|mini-agent-rs|mini -y --exit-immediately".
With MINITUI_AGENT_TOOLS=1, read recognizes PNG, JPEG, GIF and WebP by their byte signatures. It attaches the original image as a bounded (8 MB) data URL, with a separate user visual block after the tool result. Chat, Responses and Messages wire shapes preserve the image; an empty multimodal_regex does not disable this route. Images do not accept text pagination. Code-mode text reads reject raster bytes rather than returning broken pixel text.
The provider and chosen model must still support images. Vendor support and wire preservation do not prove that a specific endpoint has interpreted an image correctly. This does not change a running benchmark's binary or tool gate.
Host-native read, write, edit and Rhai code are available only for the local environment. Other environments fail closed with a tool error and a bash fallback, so container paths cannot accidentally read or change host files. Docker still executes bash in the container working directory. A native container bridge is not implemented by this guard. STOP is checked before each inline native action in a concurrent batch.
Queue edits remain limited to pending tasks; each put increments goal_version and clears old results. Claims carry the version. Both managed workers and direct finish calls require positive closure evidence bound to task ID, current fence and goal version, with a trajectory reference. Plain done is refused. This is recorded evidence, not an independent proof that a judge is correct.
queue events --db PATH --after SEQ --limit 100 returns a durable sequence cursor for created, goal_updated and state_changed events. Consumers deduplicate by seq and resume after their last persisted cursor. Heartbeats do not emit state events. SQLite triggers write events in the same transaction as task changes. Existing tasks are not given invented historical events. Running-goal amendment and live worker notifications remain a separate integration step.
GET /api/task-events?after=SEQ returns up to 100 durable queue events and a resume cursor. /api/task-events/stream uses SSE event task, with id set to seq. Resume with Last-Event-ID or after; store/deduplicate seq on the client. The server reads without schema migration or worker/model calls, at most once per 5 seconds per stream, limits streams to16 and closes with queue-error on unavailable data. Same-origin protection, 5-second child timeout and cursor validation apply. This is an API, not yet a task-board UI or a verified main-chat delivery path.
Contract checks now read indexed files from the current contained tree instead of counting index labels as code. Index reads are capped at256 files,512KB per file and4MB corpus, with missing coverage reported. All named identifiers in a positive clause must be present as code tokens; one matching word is insufficient. Coverage requires actual file contents. Prose invariants are flagged for semantic review, never declared safe by an absent/present token. Mechanical checks do not certify types, caller/producer-consumer mappings or complete repository coverage.
The existing Rhai code tool is a batching alternative, not JavaScript/QuickJS parity with current Pi. It now respects STOP during interpreter progress, caps a single print and the entire returned observation at60KB without splitting UTF-8, and limits reads to8 concurrent file-reader threads. Pure-script time/operation bounds remain; registered host tools still perform real actions. This does not add async JS, non-LLM image classifiers or a result store.
queue amend --id ID --goal-version N --goal-file FILE appends a follow-up only to the explicitly named task at the expected version. It clears stale vector/results. Running tasks are fenced and interrupted; managed workers check their lease/version once a second and stop on revocation. Reconcile the stopped process before retrying with the updated goal. This avoids an old goal closing a new request; it is a safe restart route, not live in-place delivery. Ambiguous similarity never edits a task.
queue cancel --id ID --goal-version N cancels pending/blocked tasks. A running task stays interrupted (blocking conflicting claims) until its worker is stopped and reconciled. Cancellation-request status is not a claim that all external effects were undone.
After verifying the previous worker is stopped, queue cancel-reconcile --id ID --goal-version N --reconciled yes converts interrupted to cancelled and releases ownership. The operator assertion is explicit; the CLI does not pretend to inspect an arbitrary external worker.
Transport-neutral ChannelInbox stores explicitly bound Gmail/WhatsApp/Apple-Messages routes with session/project and approved sender handles. Ingestion is transactional, bounded, deduplicated by account/thread/message ID and ordered by arrival cursor while retaining provider time/reply-to. A conflicting duplicate is refused. Evo normalizes messages.upsert data only after transport authentication, using the account supplied by trusted configuration rather than the webhook claim; self echoes and ambiguous sender direction are refused. Group messages require participant identity. Media references are not fetched automatically. Audio transcription attaches to the original audio with pending/complete/uncertain/failed state, never a second task.
This is internal adapter/storage preparation. No HTTP receiver, account connection, webhook configuration, Gmail/Apple transport, external send, task launcher or bot identity is enabled by importing it. Bindings are operator configuration, not grants from message content. Transport authentication and owner permission must be checked before calling accept; incoming words cannot authorize arbitrary external effects. Real channel end-to-end and multi-chat work remain open.
mini-tui queue <action> forwards to the selected Rust task-graph binary without taking over the TUI. It preserves queue arguments and stdout/stderr, defaults to MINITUI_TASK_GRAPH_DB, and passes SIGINT/SIGTERM to the queue lifecycle. Non-worker calls have a10-second timeout; output is bounded to4MB per stream. Mutations are attempted once. A timeout/interruption may follow a committed write, so inspect current state before retrying. This shared bridge is ready for main/web clients, but importing it does not launch a worker or prove that main conversation routing is integrated.
Every contract-check records a coverage..json manifest with contained paths and SHA1 content digests of the exact bounded files examined, unread exclusions, scope/root and check time. SHA1 is a freshness fingerprint, not a security guarantee. Changes since the previous check fail the snapshot check and request refreshed findings/contracts. Manifests explicitly state partial repository coverage and unverified semantic contracts. A new snapshot does not automatically repair an index, prove full coverage or check a Git revision.
QueueEventsClient shares a read-only SSE subscription among watchers, deduplicates durable seq, refreshes the graph after events, reconnects after5 seconds and retries failed snapshots without losing the pending event. It guards stale fetches after all watchers leave and removes timers/streams. No mutations or model polling. This is a client building block; the task panel has not yet been wired to it or visually verified.






























