Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The dashboard, and watching it work

Otōto logs every call on your machine, and three things read that log: a dashboard, a live tail, and a report to send when something goes wrong. None of it leaves the machine unless you send it.

The dashboard

ototo ui

serves a page at http://127.0.0.1:7777 (--port for another), and prints the link to open. It reads the log file, not a running server, so it covers every session and every repository that logged to that file, and needs nothing running but itself.

For a time range, a repository, a tool or a model, it shows:

  • Totals: delegated and direct calls, the small model’s tokens in and out, what went back to your agent, and the model’s time (median and 90th percentile).
  • Reliability: answers that finished and were not cut off, with what cut the others off (out of turns, out of tokens, repeating its calls, answered without finishing; more budget helps only the first two), citations dropped as unverifiable, build checks passed, and errors. No grade is given under twenty answers.
  • Activity over time, delegated against direct, and latency of each delegated call over time, which shows when the model server was busy.
  • By tool, by repository and by model: calls, errors, times, turns, tokens and reply size. Point Otōto at another model and it gets a row of its own to compare.
  • What the small model did: its steps across all turns: its own tools, tools it made up (it has seen other agents’ Grep, Read and Bash in training), turns it answered in prose, and calls it garbled, with the share of turns that went on those. A row opens to recent examples.
  • Recent calls, each opening to the question, the small model’s steps turn by turn, and the exact reply your agent got. Running now shows the calls in progress.
  • Model servers: a probe of each (latency, the models it serves), in the order they are tried.
  • Settings: the settings file’s path and what it says now, read again each time the page opens.
  • Plugins: each one’s version, the files it reads, who signed it, and whether it loads. One can be switched off or on, removed, or added from its .wasm and signature, and only if a key you already trust signed it; trusting another key is never done from the page.

Light or dark, or following the system.

Who can open it

The log holds questions, code and replies, so the dashboard keeps to this machine:

  • It listens on 127.0.0.1 only, and answers only requests addressed to 127.0.0.1 or localhost, so a web page whose name is pointed at 127.0.0.1 cannot read it through your browser.
  • Its data needs a key, kept in ~/.ototo/ui.key where only you can read it, so another user of a shared machine gets nothing from the port. The link ototo ui prints carries the key after a #, which browsers never send to a server; ototo ui --link prints it again, for a dashboard a service started.
  • Changes to plugins are accepted only from the page itself.

From another machine, tunnel to it, and open the printed link:

ssh -L 7777:127.0.0.1:7777 <host>

A live tail

ototo tail

follows every delegated call as it runs, from every session on the machine: its question, each turn of the small model (the tool it called, tokens so far, time), the claim check, and how it ended.

12:39:17  21572.1    ask      start   Which class validates a new pet, and what does it check?
12:39:22  21572.1    ask      turn 1  find({"query":"validate new pet"}) · 1.6k tok · 4.7s
12:39:24  21572.1    ask      turn 2  read({"addresses":["src/main/java/…/PetValidator.java"]}) · 3.8k tok · 7.1s
12:39:40  21572.1    ask      turn 3  finish({"answer":"The class is PetValidator …"}) · 7.3k tok · 22.9s
12:39:40  21572.1    ask      end     23.0s · 3 turns · 7.3k tok · ok

Your agent is told the same while a delegated call runs, when it asks for progress (Claude Code does): a notice when it starts, then every few seconds the turns so far and the latest step (turn 5 · read src/lib.rs). When no turn ends for 30 seconds, as when the model server is busy, it says so (waiting for the model's reply to turn 6, 4 min). That also keeps Claude Code from ending a long call that is still working.

From a shell, ototo ask, locate, callers and edit show the call on the terminal’s last line while it runs, and clear it before the answer is printed:

(•) ask · gpu · 4 s · turn 2 · read src/cache/Store.kt#evict

The run log

~/.ototo/runs.jsonl: one line a call, from your agent’s sessions and from the command line alike. A line has the kind of call, the repository, the session, the input, the small model’s steps, the reply (its first 6,000 characters), turns, tool calls, tokens, timings, citations kept and dropped, a check’s verdict, and whether an edit was applied. It also names the Otōto that made the call and, for a delegated one, a short hash of the prompts and tool descriptions the small model was given, so that two calls with the same hash had the same instructions.

The log is rotated at 20 MB, three old files kept, and the dashboard reads those too. run_log = "off" turns it off, run_log moves it, and trace = "off" stops the live trace beside it: Settings.

A report to send

ototo report

writes one file for when something goes wrong: the version and the machine, what ototo doctor finds, the settings with their credentials taken out (keys, headers, passwords in URLs), the log’s counts per tool for the last week (--days), and its latest errors’ messages (--errors, 20 unless set). Not the questions, the code read or the answers: --with-inputs adds each error’s input. The file is readable by you only, and nothing is sent: it is yours to read and to send.

Across a team

The dashboard is one machine’s. For usage across many, Otōto sends counts, never questions, code or answers, to an OpenTelemetry collector of yours: Metrics and For organisations.