Most of your agent's bill is scaffolding.
It answers before the call is made.

Limen sits between a terminal coding agent and its model API and decides, before the request leaves the machine, which tool schemas and which stale turns the call does not need. 43.6% fewer input tokens on measured traffic, task pass-rate held, running locally at $0.

what the client sent 11,935 tok
what Limen let through 6,356 tok

Request 3 from the demo corpus, replayed twice — the same pair the printout below carries. −46.7% of the prompt, and the request still passed. The line is the threshold; everything right of it never reaches the model.

−43.6%
input tokens per task
measured on the mock
100%
task pass-rate held
off versus on, by family
0
runtime dependencies
stdlib only, Python 3.9+
$0.00
API spend to run this
mock upstream, no key
subproto show 3 — captured from this build
the request the client sent11,935tok input
what Limen let through6,356tok input −46.7%
spend, at the mock's tariff$0.0413 → $0.02465,579 tok and $0.0167 went away
subproto show — request 3, anthropic /v1/messages, claude-sonnet-4-5

                     client   claude-code
                   recorded  2026-09-28 21:57:50
                     stream  yes
                     status           200
                       ttfb          0 ms
                    latency          3 ms
               billed input         6,356  (2,464 cached)
                     output           320
                      spend       $0.0246
                       body  present  /tmp/limen-on/bodies/anthropic_bcf7f61fcfca.json.gz

  decisions  3, and it could save 5,345 tok
                  tool_gate  delivered  12 kept  12 cut  4,145 tok  1.4 ms  compiled
    tool             exitplanmode                 339 tok value/token 0.000000 < kept floor 0.003003
                             Present the current plan for user approval. parameter 0 accepts…
    tool             killshell                    334 tok value/token 0.000000 < kept floor 0.003003
                             Terminate a running background shell session. parameter 0…
    tool             mcp__browser__click          353 tok value/token 0.000000 < kept floor 0.003003
                             Click an element in the browser by selector. parameter 0 accepts…
    tool             mcp__browser__fill           348 tok value/token 0.000000 < kept floor 0.003003
                             Fill a form field with a value. parameter 0 accepts a JSON…
    tool             mcp__browser__navigate       355 tok value/token 0.000000 < kept floor 0.003003
                             Navigate the browser to a URL. parameter 0 accepts a JSON object…
    tool             mcp__browser__screenshot     362 tok value/token 0.000000 < kept floor 0.003003
                             Capture a screenshot of the current page. parameter 0 accepts a…
    tool             mcp__db__query               344 tok value/token 0.000000 < kept floor 0.003003
                             Run a SQL query against the project database. parameter 0…
    tool             mcp__db__schema              343 tok value/token 0.000000 < kept floor 0.003003
                             Print the schema of a database table. parameter 0 accepts a JSON…
      4 more in --json
                    compact  delivered  10 kept  3 cut  1,200 tok  0.0 ms  compiled
    message          tool_result#5                381 tok error, tool_result, names-task-file
                             raise RetryExhausted(last_error) from err INFO connecting to…
    message          tool_result#7                410 tok error, tool_result, names-task-file
                             DEBUG serialising payload {'amount': 4210, 'currency': 'usd',…
    message          tool_result#3                409 tok error, tool_result, names-task-file
                             raise RetryExhausted(last_error) from err DEBUG serialising…
                     effort  potential  0 tok  0.0 ms  heuristic
                             tier medium, advisory-only: multi-file
subproto demo --slots --home /tmp/limen-off; SUBPROTO_APPLY=tool_gate,compact SUBPROTO_COMPILE=on subproto demo --slots --home /tmp/limen-on; subproto show 3 --home /tmp/limen-on

The printout is a hundred columns wide — slide it sideways to read the cut.

python 3.9+, zero dependencies

Three routes. All of them run offline.

No key, no account, no GPU. The proxy answers from a heuristic the first second you start it; the local checkpoint is an upgrade you opt into later.

pip install subproto

From the index

The route the README's first code block uses. 0.1.1 is published, by CI: one console script, zero runtime dependencies, Python ≥ 3.9.

$pip install subproto
install.sh

From source

Builds a wheel from the clone and puts a subproto shim on your PATH. This is what you want if you mean to read the tests.

$git clone https://github.com/aashish254/limen && cd limen && ./install.sh
subproto demo

See it decide

Seventeen synthetic agent requests through the proxy, no upstream, no spend. Start here. --home on both lines, or the report reads a different directory than the one the demo wrote.

$subproto demo --slots --home /tmp/subproto-try && subproto report --home /tmp/subproto-try

Then subproto doctor — it reads this machine and tells you what is present, what is optional, and what to install. It names key variables and never a key. Gated on 3.9 and 3.11; this run was 3.11.15 on a Darwin 27.0.0, 10 cores at load 6.2.

bench/launch_stats.py

The arms, side by side.

20 tasks × 5 repeats against a seeded mock upstream, run on Darwin 27.0.0, 10 cores. Every row is a number the bench printed, not one this page typed.

metricslots observingper-slot budgets one compiled budgetchange
input tokens / task19,74613,82211,133−43.6%
task pass-rate100%100%100%held
recall of the files the task needs100%100%100%held
precision of what survives1.9%2.6%19.6%+17.7 pp
p50 request latency (ms)6.86.36.5-0.2
hard set: pass-rate / recall100% / 100%0% / 2.7%100% / 100%per-slot lost it
subproto/config.py

What each slot answers, and what it would save.

None of them is on until you name it in SUBPROTO_APPLY, and each is reversible on its own. The token column is the offline projection.

slotwhat it decidestokens it would cut% of input
tool_gatewhich tool schemas this turn cannot use, out of the ~24 that ride along in every request101,56228.3%
compactwhich old turns have gone stale — a superseded tool result, an error the agent already worked around14,8214.1%
contextwhich file notes the code graph says this task will not touch00.0%
efforthow much reasoning this step deserves — advisory: it names a tier and never rewrites the request00.0%

A projection from measured prompt shapes — tokens that would not be sent, priced by the estimator the proxy itself uses. It is not a provider invoice, and the page does not present it as one.

docs/assets/figures/

Eight plates from the run.

Each figure carries the command that produced it, the host it ran on and the commit it came out of. Order is the argument: what a request carries, what comes off, whether the task still passes, and where the naive version breaks.

what the average request carries before anything is decided mean of 20 tasks, 19,746 est input tokens, weighed part by part with the estimator the proxy itself uses mean request11,7574,2323,56819,746tool specs (24) 11,757stale reads 4,232system prompt 3,568the files the task needs 134task instruction 55total 19,746 tok parts are weighed separately and scaled to the measured total (x1.446). Only the two rightmost categories are what the slots may touch.

The average request before anything is decided: a fifth of it is tool schemas the turn cannot use, and the task itself is a rounding error.

python3 bench/launch_stats.py --iters 5 on Darwin 27.0.0, 20 tasks × 5 repeats, commit 0f7a546.

input tokens per task, by what the task is about 20 tasks, 5 repeats each, same mock upstream. The compiler spends one budget over the whole request. 06,25012,50018,75025,00020,55914,34211,500-44.1%tests20,34014,81611,464-43.6%payments19,60713,60811,012-43.8%security19,58613,25411,045-43.6%data19,02313,34110,814-43.2%servicesthe layer offper-slot budgetsone compiled budgettok measured on the mock at $0; a request-shape proxy, not a billed invoice. Families are author labels.

Input tokens per task with the layer off, per-slot, and compiled, split by what the task is about. The reduction is flat across families, which is the point — it is not one kind of task paying for the others.

python3 bench/launch_stats.py --iters 5 on Darwin 27.0.0, 20 tasks × 5 repeats, commit 0f7a546.

the cut by request size, quartiles of the unmanaged request buckets are set by what the request costs before the system sees it, so a task cannot be moved into a flattering bucket 06,25012,50018,75025,00018,41310,570-42.6%Q1 smallest19,59511,035-43.7%Q219,61011,021-43.8%Q321,36711,905-44.3%Q4 largestthe layer offthe layer ontok mean input tokens per task in each quartile; Q1 smallest 16666-19583, Q2 19588-19604, Q3 19605-19615, Q4 largest 19627-22581

The same three arms grouped by the size of the unmanaged request. Bigger requests do not get a proportionally bigger cut, so the saving does not live only in the outliers.

python3 bench/launch_stats.py --iters 5 on Darwin 27.0.0, 20 tasks × 5 repeats, commit 0f7a546.

what each slot would cut, in estimated input tokens a projection per slot over the deterministic synthetic corpus (30 requests, 359,259 est input tokens) the synthetic corpus101,56214,821116,383tool_gate 101,562compact 14,821total 116,383 tok a projection from measured prompt shapes (I6): these are tokens that would not be sent, not a provider invoice.

The ablation's own corpus: what each slot would take off a request shaped like the ones it measures, priced in estimated input tokens. That is why it is not the number the arms above delivered.

python3 bench/ab.py --mock 24 --json, run by python3 bench/launch_stats.py --iters 5 — 30 requests, 359,259 est input tokens.

task pass-rate, by family: the floor the cut has to clear a task passes when the files it needed are still in the request. The gate is -1 pp; the observed delta is 0.0 pp. 0255075100100100tests100100payments100100security100100data100100servicesthe layer offthe layer on% of tasks passing same 20 tasks, 5 repeats, mock upstream. Held at 100%, family by family, is the claim; a pooled average could hide one broken family.

Pass-rate by family with the cut enforced. A saving that broke the task would be visible here as a missing bar, not as a footnote.

python3 bench/launch_stats.py --iters 5 on Darwin 27.0.0, 20 tasks × 5 repeats, commit 0f7a546.

what survives the cut: recall and precision of the evidence recall = required files still present. precision = the share of what survived that was required. 0255075100100.0100.0recall of required files1.919.6precision of what survivedthe layer offthe layer on% precision starts low because an unmanaged request carries every tool spec and every stale turn; the cut raises it only if recall holds.

Of the files a task is about, how many survive (recall), and of what survives, how many are the ones that matter (precision). Per-slot keeps recall and spends precision; the compiler moves both.

python3 bench/launch_stats.py --iters 5 on Darwin 27.0.0, 20 tasks × 5 repeats, commit 0f7a546.

the set built to break the naive version of this idea 8 long requests where the file the task needs sits behind a run of large files it does not need. A per-slot budget spends itself on the newest and drops the one that matters; one joint budget over the whole request does not. 02550751000.0100.0task pass-rate2.7100.0recall of required files0.525.1precision of what survivedthree slots, three budgetsone compiled budget% this is why the shipping arm is the compiler. A slot that saves more tokens than it is allowed to is not a saving, it is a failure.

Eight tasks built so the file the grader wants sits behind a run of large files it does not need — which is what a per-slot recency budget is worst at. Per-slot loses every one of them. This panel is the reason the compiler is the default story rather than a bonus arm.

python3 bench/launch_stats.py --iters 5 on Darwin 27.0.0, 20 tasks × 5 repeats, commit 0f7a546.

the real checkpoint's cost per decision, against the shipped budget convaiinnovations/laya 0.3.20 on cpu, 30 decisions per shape, measured through the server that would use it 07501500budget 350 ms123choice800keep_drop1097noul read from bench/laya_latency.json, measured earlier; not re-run here. Only the shape under the red line is usable in the engine.

The real small model's measured cost per decision against the shape it has to answer in. One shape fits the budget; the one that would name every option does not, so the shipped path scores a single choice.

convaiinnovations/laya 0.3.20 on cpu, read from bench/laya_latency.json — measured against the local server, not re-run by the arms.

regret_leg() in bench/launch_stats.py

Enforcing a wrong call bills the token back.

The saving has a price and it sits on this page beside it. A cut that drops a file the agent then reads again costs more than it saved.

what enforcing costs when it is wrong tokens paid back by re-reads of drops the traffic asked for again, against the tokens the same corpus saves 037,50075,000112,500150,000116,383tokens saved4,088tokens paid backthis corpustok 10 regrettable drops on replayed demo traffic with the slots enforcing; the payback is 3.5% of the saving, and it is the price of enforcing rather than of observing.

17 of the 17 requests that carried a decision still had their body on disk when the harvest ran, which is what lets the payback be counted rather than estimated.

python3 bench/launch_stats.py --iters 5 on Darwin 27.0.0 — 17 demo requests replayed with the slots enforcing, bodies stored, commit 0f7a546.

The demo corpus is 17 requests and every one of them carries a decision. The regret harvest looks for the case where the wire disagrees with a cut — a file the slot dropped that the agent then read again — and it saw 14 evicted reads, of which 10 were wrong: the file came back on the next turn, so the cut cost 4,088 tokens of refetch instead of saving them. All 10 were enforced and none was shadow, so that is the price of a call made, not of a call observed — which is why Limen starts in observation, why each slot is reversible on its own, and why this bill sits on the page beside the saving. It works out at 588.2 regrettable drops per 1,000 requests. The mechanism still nets out at 43.6% fewer input tokens per task with the pass-rate held; the honest number is the pair, not the one that flatters it.

Six claims, none of them made

What this page refuses to claim.

Six claims a proxy like this is expected to make. Limen does not make them, because nothing in the repo has earned them yet. The list lives in tools/make_site.py as GATES: a row is deleted by hand, when the number that replaces it is real.

the claimwhat it would take
−X% on your own traffica real agent session across ≥2 vendors, run by you. Nothing here has seen a human conversation; every number on this page comes from a seeded synthetic corpus.
the billed hero numberan approved API budget and a public 20-task SWE-bench-shaped set with a grader. The $ figures on this page are the in-repo mock's own tariff.
that a fine-tune beats the heuristiclabels. The published ablation says the real checkpoint loses by −0.083 precision on the corpus we carry, and 7 of its 10 cases have gold sets equal to the heuristic's own answer.
a shipped routing modelthe same labels, plus a training run and a weights release.
version 1.0the launch post, and the traffic that would earn the number. `0.1.1` is on PyPI and CI published it, so `pip install subproto` is a route today; 1.0 is a claim about real sessions, not about a version string.
that it works on Windows`os.getloadavg` is POSIX and ANSI handling differs, so CI runs Windows and reports it without gating on it.
tools/make_site.py

17 subcommands. Every number re-derived.

Nothing on this page was typed by hand. The build replays the demo, reads the bench output, counts the tests and the mutants out of the source, and stops rather than print a figure it cannot find in the run.

python3 bench/launch_stats.py --iters 5 runs both arms, the regret harvest and the checkpoint curve, then writes the JSON and the eight plates beside it.
python3 tools/make_site.py replays the demo twice, renders this file, and fails on any token it has no value for.
bash run_tests.sh 444 tests and 157 mutants on 3.9 and 3.11 before any of it is committed.