Look inside your agent's context window.
A real session from mado's log: mado writing its own landing page, 25 calls on DeepSeek v4.1 Flash.
or go install github.com/Mediacom99/mado/cmd/mado@latest · macOS, Linux, WSL2 · your key, no account · from the first release, when the repository opens
The session, call by call, as mado printed it.
These lines are mado's profiler run on its own log, verbatim. The first number is what entered the window on that call: In(N+1) − In(N) − Out(N), exact, from reported numbers only. Two ends, like +4,263…+5,078, mean the reasoning of the call before may have stayed in the prompt or left it; net means more left than entered. Cost reads $n/a because no price was logged for this route that day, and mado doesn't invent one.
| call | time | entered | mado's line |
|---|---|---|---|
| run 1 | |||
| 1·1 | 12:20 | your message | +7,616 · cache 0/7,616 · out 935 (815 reasoning) · 0.6s · $n/a |
| 1·2 | 12:20 | 2 bash results | +4,263…+5,078 · cache 7,167/12,814 · out 647 (528 reasoning) · 0.8s · $n/a |
| 1·3 | 12:20 | 2 bash results | +4,889…+5,417 · cache 12,799/18,350 · out 690 (561 reasoning) · 0.7s · $n/a |
| 1·4 | 12:20 | 2 bash results | +498…+1,059 · cache 17,919/19,538 · out 397 (344 reasoning) · 1.4s · $n/a |
| 1·5 | 12:21 | bash result | +449…+793 · cache 19,455/20,384 · out 595 (469 reasoning) · 0.9s · $n/a |
| 1·6 | 12:21 | 2 bash results | +527…+996 · cache 19,967/21,506 · out 1,156 (1,064 reasoning) · 0.8s · $n/a |
| 1·7 | 12:21 | bash result | +954…+2,018 · cache 21,503/23,616 · out 486 (405 reasoning) · 0.6s · $n/a |
| 1·8 | 12:22 | bash result | -129 net · cache 23,551/23,973 · out 407 (366 reasoning) · 0.5s · $n/a |
| 1·9 | 12:22 | bash result | +36 · cache 23,551/24,416 · out 1,106 (1,062 reasoning) · 1.5s · $n/a |
| 1·10 | 12:22 | read result, a steer | +3,819 · cache 24,063/29,341 · out 12,690 (2,440 reasoning) · 1.3s · $n/a |
| 1·11 | 12:30 | write result | -210 net · cache 21,719/41,821 · out 305 (39 reasoning) · 10.0s · $n/amiss: after 0s idle, cause unknown · 7.6k rewritten |
| 1·12 | 12:31 | bash result | +339 · cache 41,736/42,465 · out 595 (396 reasoning) · 1.0s · $n/a |
| 1·13 | 12:31 | bash result | -85 net · cache 42,277/42,975 · out 621 (183 reasoning) · 0.7s · $n/a |
| run 2 | |||
| 2·1 | 12:37 | your message | -13 net · cache 22,283/43,583 · out 284 (104 reasoning) · 0.8s · $n/amiss: tools changed (~bash ~grep) · 20.7k rewritten |
| run 3 | |||
| 3·1 | 12:37 | your message | +0 net · cache 22,283/43,867 · out 329 (288 reasoning) · 0.6s · $n/amiss: after 11s idle, cause unknown · 21.3k rewritten |
| 3·2 | 12:37 | read result | +11,793 · cache 36,500/55,989 · out 780 (707 reasoning) · 0.6s · $n/amiss: after 0s idle, cause unknown · 7.4k rewritten |
| 3·3 | 12:38 | read result | +5,654 · cache 36,558/62,423 · out 14,909 (5,653 reasoning) · 0.6s · $n/amiss: after 0s idle, cause unknown · 19.4k rewritten |
| 3·4 | 17:16 | write result | -203 net · cache 0/77,129 · out 219 (146 reasoning) · 3.6s · $n/amiss: served by DigitalOcean, not OpenInference · 62.4k rewritten |
| 3·5 | 17:16 | read result | +2,771 · cache 77,312/80,119 · out 10,983 (1,526 reasoning) · 1.2s · $n/a |
| 3·6 | 17:17 | write result | +37 · cache 91,008/91,139 · out 432 (391 reasoning) · 1.3s · $n/a |
| 3·7 | 17:17 | read result | +11,781 · cache 91,520/103,352 · out 9,607 (239 reasoning) · 1.3s · $n/a |
| 3·8 | 17:18 | write result | +25 · cache 112,896/112,984 · out 836 (502 reasoning) · 1.1s · $n/a |
| 3·9 | 17:18 | bash result | +34 · cache 113,792/113,854 · out 954 (570 reasoning) · 1.1s · $n/a |
| run 4 | |||
| 4·1 | 17:33 | your message | +40 · cache 114,688/114,848 · out 4,400 (3,713 reasoning) · 1.4s · $n/a |
| run 5 | |||
| 5·1 | 17:34 | your message | +19 · cache 119,168/119,267 · out 4,095 (2,953 reasoning) · 1.1s · $n/a |
Every number carries its state. Unknown is never zero.
A provider that doesn't report a number gets n/a, not 0; a number mado computed says how. The examples are from the session above.
The provider's usage, untouched.
Arithmetic over the reported numbers of each call, with no estimate and no extra request.
When the reasoning before may have stayed or left, both ends, until the session settles which.
Not reported, or not priced. Said so, never drawn as an empty bar.
The fixed prefix, system prompt and tools, measured once per hash with count_tokens.
The share of a file or a paste inside one increment, by bytes, marked with ~.
$0.00 spent and 0% used with no data behind them are the two numbers other harnesses print most often. mado prints what it knows.
We logged what actually crossed the wire.
Single observations from development sessions in September 2026, read back from mado's log. They are not benchmarks; they are why mado exists.
The same 3.7 KB Go file, read by two models. Haiku 4.5 and Opus 5.5 count it differently: the tokenizers differ.
One Anthropic web search. The encrypted result stays in the history and is paid again on every later turn.
No cache breakpoints on Anthropic. cached=0 on every turn: one read of our own project guide entered at full price.
gpt-5-mini output was hidden reasoning, 73%. On Chat Completions it never comes back.
gpt-oss-20b on llama-server. First token with a cold cache, then with a 5k-token prefix cached.
A negative increment. The chat template dropped earlier reasoning, and nothing in the payload said so.
Reads failed on guessed file names by a model exploring a repo with only read: it needed a tool that lists files.
mado is the agent, so what you find, you can change.
The profiler isn't watching from outside: it reads the request mado is about to send and the usage that came back. Profiler on or off, the bytes on the wire are identical.
- 1
Measure
Every call, what entered the window and what the cache gave back, from reported numbers only.
- 2
Discover
Where it went: this file, that tool, a router that served the call from another upstream.
- 3
Change
In the harness itself: the system prompt, the tools, the breakpoints. It's yours, in Go.
- 4
Re-measure
Same task, profiler on: the difference is the answer, not an impression.
Of thirteen harnesses surveyed, none showed what entered the window on a turn.
What each one tells you about the context window, as of September 2026. The most-voted cost complaints are caches that break with no stated reason, like claude-code#46829. The cause is in the request, and the harness owns the request.
| Tokens entered per turn | Why the cache missed | Unknown shown as unknown | |
|---|---|---|---|
| Claude Code | — | a likely cause, last miss only | partly |
| Codex CLI | — | — | no: "100% context left" with no data |
| OpenCode | — | — | no: $0.00 spent, 0% used |
| Pi | — | idle time or a model switch, off by default | no: an unknown price shown as 0 |
| mado | every call | every miss: the cause from the request, or unknown | yes, n/a |
A whole agent, not a viewer bolted onto one.
The profiler is the reason to pick mado. It isn't the only thing you get, and this page says what works today and what comes with the first release.
- Any model, your keyAnthropic Messages, OpenAI, and every Chat Completions endpoint: OpenRouter, DeepSeek, Groq, llama.cpp, Ollama, LM Studio, vLLM. No subscription login, ever.
- Seven tools, sized for a window
read,list,grep,bash,write,edit,fetch, behind permission prompts, with a redactor on every result before the model sees it. - The window, managedA system prompt in named sections that never changes mid-session,
AGENTS.md, Agent Skills on demand, and compaction you can see in the transcript. - Everything on disk, nothing in your repoEvery session in one SQLite file;
-cand-rresume;-pfor scripts, with exit codes. - A library firstThe TUI is a client of the same packages you import:
agent,event,tool,provider,store,client.
mado profileThe whole session as one report, and/profilelive.- The prefix, countedSystem prompt and tools, once per hash.
- Claude Code's logsThe same report from another harness's session.
- Signed releasesNotarized binaries, a cask,
mado upgrade.
- MCPWith OAuth, and each server's cost in the report.
- SubagentsChild sessions with their cost on the tree.
- RollbackFiles, conversation, or both.
The benchmark lands with the release. Until then, no numbers.
One task in a real Go repository, the same model, Claude Code, Codex, OpenCode, Pi and mado at their defaults, medians of five runs. The scripts live in bench/, so you can run the same comparison on your own repo.
The questions this page usually gets.
My harness already shows a context bar. What is this?
A bar is the state of the window: how full it is now. mado shows what entered it on a given call, by difference of reported numbers, and why the cache missed, from the hashes of the system prompt and the tools in the request that was actually sent. The bar is the state; the call is what you paid for.
Does the profiler cost extra calls or tokens?
No. It measures bytes already sent: nothing goes into the payload, and profiler on or off produces the same sequence of request hashes. The fixed prefix will be counted once per hash with count_tokens, as a separate request, never one per tool.
Where does my code go?
To the provider you configured, and nowhere else: no telemetry, no account, no proxy of ours. Sessions live in one SQLite file in your data folder, and a redactor turns a secret read from a file into a stable placeholder before it enters the window.
Where do the prices come from?
The models.dev catalog, cached for a day, with an override in the config; on OpenRouter, the cost it reports. The rate is frozen in the log, so an old call keeps the price it was paid at. Without a price there is no cost, and mado says $n/a instead of $0.00.
Why did "served by DigitalOcean, not OpenInference" miss the cache?
OpenRouter routes a model to several upstream providers, and each keeps its own cache. After four hours away, the call landed on a different one: 62.4k tokens written again. A timer would have guessed an expired TTL; the response names the upstream, so mado says which.
Windows?
macOS and Linux at launch, amd64 and arm64; Windows runs it from WSL2.
Why Go?
One binary, no cgo, no runtime to install, and a target of tens of megabytes of RAM, measured with the release and published with the method. And mado is a library first: the profiler is a client of the same packages you import, not a special case in the core.
What doesn't it do?
No sandbox: bash runs as you, and the boundary is the permission prompt, which a shell can walk around. Run it in a container for real isolation. No telemetry, no hosted service, no subscription login.
License?
Planned: AGPL-3.0, plus a commercial license for uses that can't meet its terms, and a contributor agreement.
One binary, your key, and the folder you're already in.
From the first release, when the repository opens: signed and notarized binaries for macOS and Linux, amd64 and arm64. Nothing else to install: no runtime, no daemon.
Local models work: any server that speaks Chat Completions (llama.cpp, Ollama, LM Studio, vLLM) is one entry, /provider add ollama http://localhost:11434/v1 none. When a server doesn't report a number, the profiler says n/a instead of guessing.