A classifier inside the coding agent
TypeSafe is a separate service. Its model, jev, answers multiple-choice questions: send it a situation and a list of questions, and it returns an answer to each with a probability.
What kind of error is this?
Input exceeds the maximum length of 1048576 characters
One question, a fixed list of answers, a probability.
In short
We tried jev wherever a cheap, fast decision might save an expensive model call, then measured it against our own past sessions. One use clearly beat the code it would replace: deciding what to do when a model call fails. Two others, re-ranking grep results and reading review verdicts, were measured and removed. Behind every number here: 290 jev calls, $0.011 in total.
- What we tested
- Re-ranking 220 replayed searches, reading 70 real review reports, and sorting 24 real failed-call incidents.
- What helped
- Error triage on failed model calls: 24 of 24, one more than the patterns it backs up. It ships, off by default.
- What we removed
- Grep re-ranking (right file in the top 3 fell from 78.6% to 74.1%) and review verdicts (2 false passes in 20).
- What we found on the way
- A search bug: 55.6% of capped searches came back empty. Fixed, no classifier needed.
What it costs, next to what it replaces
One turn, step by step
Steps marked jev use jev. Steps marked model use the model you pay real money for. The rest runs locally.
you send a messagelocal
From the terminal, the desktop app or headless mode.
the Genome is already builtlocal
Empryo's map of your code, built in the background rather than on every turn.
the request is put togetherlocal
Instructions, tools, a summary of the map, and your message.
jev picks a skill, or nonejev
All 136 installed skills ranked in one call; at most one is suggested. About 1s, $0.0004.
the model reads everythingmodel
Tens of thousands of tokens at $1 to $10 per million. This is where the money goes.
it searcheslocal
Find and grep return candidate files, ranked by name match, centrality and git history.
jev re-ranks the find resultsjev
One call, 244ms median, $0.00005. In shadow mode it shows what it would have done; the model still gets the original order.
it reads and editsmodel
Every read of the wrong file costs a full round trip at model prices.
repeatlocal
Steps E to H repeat, and each round costs more than the last.
the itemized billlocal
Every jev call is added to the tab's cost and to /usage, at $0.042 per million tokens.
Two uses we measured and removed
Re-ranking grep results
Live side-by-side runs told us nothing: the same prompt on the same model took 14 steps, then 6, then 9. So we replayed past sessions instead. We took every grep in 19,828 recorded tool calls, in Empryo’s own order, and used the files the turn went on to read as the answer key. We only scored searches where the answer was in the list, because the 32KB output limit decides that before any ranking happens.
right file first (n=220)
right file in the top 3 (n=220)
top 3, only files the turn edited (n=97)
21 searches fixed, 31 broken. The bar, set before the first call, was +15 points and three fixes for every break. This went the other way, so the code was written, measured and deleted. The useful finding was elsewhere: in 32.4% of searches the file the turn ended up reading was never in the output at all, which led to the bug fix below.
Reading review verdicts
A review agent’s report ends with VERDICT: PASS | FAIL | PARTIAL. When that line is missing, a model call works out the verdict, and jev looked like a $0.0001 replacement. We tested it on 70 real reports with the line removed, using the reviewer’s own verdict as the answer key.
A search bug we found along the way
Search output arrives in chunks, and each chunk was decoded as text on its own, so a character split across two chunks turned into garbage. Worse, the chunk that crossed the 32KB limit was thrown away whole instead of being trimmed.
55.6%1,742 of 3,134 capped searches, across 258,802 recorded greps, returned no matches at all. The model saw only “output capped” and treated that as the answer.
Both bugs are fixed: output is collected whole, trimmed to the limit and decoded once. The decoding bug never triggered in our data (0 garbled paths in 258,802 outputs, all ASCII), so the replay numbers above were not skewed by it.
Where it helped: failed model calls
When a model call fails, Empryo decides whether to retry, shrink the conversation or end the turn by matching the provider’s error message against patterns. Those messages change without notice, so the patterns need frequent fixes.
The one case jev catches and the patterns miss is a Codex error that reads Input exceeds the maximum length of 1048576 characters. It never says “context” or “token”, so recovery never started. jev labels it a context overflow with 0.96 confidence.
This shipped as error triage, off by default. The patterns still decide; jev is asked only when they have called an error permanent and the turn is about to end. It is never asked about sign-in or key errors, answers below 0.7 confidence are ignored, and a session with no failures makes no calls. Median time is 263ms.
The code map rarely lists the files you edit
Empryo gives the model a short, ranked map of your repository, most central files first. Across 87 local sessions, the top 120 entries included only 8.5% of the files those sessions went on to edit; the top 200 reached 13.1%, and only 2 sessions were fully covered.
the map as it was, 87 sessions
pool test, 30 sessions, 278 edited files
the same 120 entries, 43 sessions
In the pool test each session had 120 candidate files, the ones it edited plus random decoys, and jev saw only the first message. It found 80 of 278 against 59 for centrality, with one 348ms call per session. Better, but under our bar of +15 points and three fixes per break, so it is not shipped.
What did ship needs no classifier. Ranking the map by how often files change as well as how central they are raised coverage from 10.7% to 40.2% in the same 120 entries. Change frequency alone reaches 42.4% but leaves out the core types you need to find your way around. The history comes from git, which the Genome already reads.
Three ideas we dropped before building
Each was ruled out by existing data before we spent a single call.
- which lines of a big file to read no headroom
- Across 431,003 partial reads, only 1.3% of re-reads went outside lines already read, and 85.2% of 52,565 edits landed inside them. A perfect predictor would gain about one point.
- which memories fit this turn not measurable
- Session logs record the memories that were used, not the candidates that were passed over, so there is nothing to re-rank until we record them.
- which subagent findings reach the parent not attempted
- A wrongly dropped finding cannot be recovered and nobody notices. The same reason rules out choosing what to keep when a conversation is summarized.
What ships today
| where | state | evidence |
|---|---|---|
| find / symbol search | shipped, off by default, shadow mode | right file in the top 5 went from 59% to 73% on 80 prompts, one point under our bar |
| skill suggestion | shipped, off by default | no measured gain yet |
| error triage on a failed call | shipped, off by default | 24/24 against the patterns' 23/24 on real incidents |
| grep | not shipped | right file in the top 3 went from 78.6% to 74.1% on 220 replays |
| review verdicts | not shipped | 60% agreement, 2 false passes in 20 |
| prefetching files for the model | not shipped | right file first 35% vs 32%, but two of every three prefetched files were wrong |
| which lines to read | no room to improve | 85.2% of edits already land in lines the turn read |
| memory recall | not measurable yet | logs record the memories used, not the candidates |
| ranking the map in the prompt | measured, not shipped | 28.8% vs 21.2% for centrality ranking: a real gain, but under our bar |
| the 32KB search limit | bug fixed | 55.6% of capped searches returned nothing. No classifier involved |
| summarizing long conversations | ruled out | a wrongly dropped detail cannot be recovered, and there is nothing to compare against |
| choosing tools ahead of time | ruled out | changing the tool list between turns would make every turn cost more |
Try it yourself
TypeSafe is off until you add a TypeSafe API key and turn it on. In the terminal app, run /typesafe; in the desktop app, open the TypeSafe settings. Each call appears in the chat with what it decided and what it cost, and later in /usage. Setup is in the docs.
Behind this page: 290 jev calls, $0.011 total, 244ms median and 283ms at p90. Live runs used Claude Haiku 4.5; the replayed reviews were written by Opus 5.