Concepts
~1.1k tokens

Token Economy

Maki's whole design falls out of one fact about agent loops: the conversation is re-sent to the model on every turn.

turn 1   [system + prompt]                      ─► model ─► tool call
turn 2   [system + prompt + result 1]           ─► model ─► tool call
turn 3   [system + prompt + result 1 + 2]       ─► model ─► ...

A tool result does not cost its tokens once. It costs them again on every turn until the session ends or history is compacted. cat a 2000-line file on turn 2 of a 40-turn session and you pay for it 38 more times. Prompt caching softens the price, not the principle: cache reads still cost, and a bloated context also makes models measurably dumber.

So Maki attacks the two multipliers: how much each step adds to context, and how many steps there are.

Smaller results

index instead of read. The index tool returns a tree-sitter skeleton of a source file: imports, types, signatures, line numbers. Usually 70-90% smaller than the file itself. The agent indexes first, then reads only the ranges it needs.

read main.rs                 index main.rs
────────────                 ─────────────────────────────
1400 lines in context        60 lines of signatures
                             + read offset=812 limit=40

Subagents as garbage collectors. A task subagent gets its own throwaway context. It can grep, read, and hit dead ends as much as it wants; only its final summary returns to your conversation. The mess is collected when it exits. Model tiers make this cheap too: delegate a search to a weak model at a fraction of the cost, keep the strong model for judgment.

main context                subagent context (discarded)
────────────                ────────────────────────────
task("find auth") ───────►  glob, grep ×6, read ×9, ...
                  ◄───────  "JWT middleware, auth.rs:120"
one line stays              ~20k tokens never seen

Deferred MCP tools. An MCP server with 100 tools would ship 100 definitions in every request. Maki loads a single tool_search tool instead; the model searches when it actually needs something and only the matches load. See MCP.

Truncation everywhere. Tool output is capped (agent.max_output_bytes, agent.max_output_lines), overlong grep lines are skipped, and every builtin tool description nags the model to read only what it needs. The nagging works.

Interrupted work is not wasted. Press Esc on a long tool, or let its deadline hit, and whatever it printed so far still reaches the model, tagged as partial: bash keeps its streamed lines, code_execution the script output, a task subagent its half transcript. Otherwise the next turn starts from nothing and you pay to run it all again.

Fewer round-trips

Every round-trip re-sends the context, so round-trips are the other half of the bill.

batch runs independent tool calls in one turn: one request, N results.

code_execution goes further: a Python sandbox where tools are async functions. Chained calls, loops, and filtering happen inside the sandbox; only what the script prints enters context.

without                          with code_execution
─────────────────────            ─────────────────────────────
glob        → 300 paths          results = gather(read × 300)
read × 300  → 300 files          filter in python
300 turns, every file            print("3 files call foo_v1")
in context forever               1 turn, 1 line in context

Compaction resets the multiplier when a session runs long: older turns are summarized and dropped. Context has the details.

Watching it work

/usage shows the token breakdown of the current session, and --output-format json in Headless Mode reports total_cost_usd per run. Cheap is a feature you can measure.