Hey everyone,
Cursor Ambassador here. I built veredicto for the agent loops I run in Cursor every day: stop paying full tsc startup on every retry, stop burning tokens scraping compiler prose meant for a human at a terminal. The patches looked fine. The loop still cost too much and returned the wrong shape of answer. So I started returning structured verdicts from a warm TypeScript session that never writes the candidate to disk: pass or fail against the project baseline, what broke, what got fixed, and how to repair it.
The core idea
Keep the language agents already write. Change only the interface.
Vercel Labs' Zero (May 2026) got the shape right: structured JSON, stable codes, typed repairs. Almost nobody writes production code in Zero, so the adoption path is steep. arXiv 2604.13927 lands in the same place from the other direction: compiler feedback bottlenecks coding agents, and the bottleneck is the interface, not the model.
veredicto takes that compiler interface and points it at the TypeScript you already have. One warm LanguageService, N candidate overlays in memory, structured JSON out. No new language. No editor lock-in.
The problem
Most agent loops look the same: write a patch, run the checker, interpret the error, retry. Cost and format both hurt.
| Break | What happens today | What veredicto does |
|---|---|---|
| Cost | Every attempt pays process startup. Write disk → spawn tsc --noEmit → restore. |
Session init once. Warm overlays. Disk never touched. |
| Format | Prose for humans. Agent scrapes, burns tokens, guesses. | JSON verdicts: delta errors, repair actions, optional semantic impact. |
Why now
A few things stacked up in about six months.
- Cursor. This is where I write TypeScript with agents. Shadow workspace already keeps language servers warm for editor UX. When the agent needs a project-scale verdict on a candidate patch, it still often shells out to
tscand scrapes stderr. That gap is what I built for. - December 2025. Claude Code ships native LSP; OpenCode bundles 30+ language servers. Agents can consume diagnostics, but those diagnostics were built for human IDEs.
- April 2026. The paper argues for structured, actionable compiler feedback.
- May 2026. Zero ships and shows demand. The same interface for existing TypeScript is still open.
Built for Cursor agent loops
This is a Cursor showcase from my side of the house. Shadow workspace already owns warm language servers for editor UX. veredicto sits under the agent retry loop: batch candidates, baseline-relative pass/fail, structured repairs, no disk writes. Same product, different layer. Wire format stays open over loopback so CI and other agents can call it too, but I built and dogfood it in Cursor.
| Layer | Cursor today | veredicto |
|---|---|---|
| Editor | Shadow workspace / stock language servers for buffers and project UX | Stays out of the chrome |
| Agent retry loop | Often: write files → run tsc → parse prose → guess |
Warm session → N in-memory candidates → structured JSON verdict |
| How you call it | Agent tool, custom command, or shell in the workspace | POST /v1/check on loopback, or the CLI |
Wire it up in Cursor (no special extension):
- Keep a daemon warm for the workspace:
veredicto serve --project ./tsconfig.json --port 4117 - Give the agent a tool / custom command that
POSTs tohttp://127.0.0.1:4117/v1/checkwith{ candidates, fixes, impact } - Or one-shot from the terminal / agent shell:
veredicto check --project ./tsconfig.json --candidates candidates.json --fixes --impact --compact
Exit codes are easy for an agent to branch on (0 all pass, 2 any fail) without scraping. The JSON body is the contract; see INTEGRATION.md. Wire format is frozen at /v1/.
What it is
A daemon + CLI over your real tsconfig. Candidates are full-file overlays (replace, create, or null to delete). Each candidate is judged against the baseline captured at session start:
newErrors/fixedErrors/totalErrors- diagnostics with stable
TSxxxxcodes and exact 1-based spans - TypeScript code fixes as repair actions (
file,position,length,newText) withconfidenceandpreconditions - optional semantic impact: export signature delta + reference (def–use) fan-out
- optional parallel candidates via
worker_threads(one Session per worker)
Cross-file damage shows up in the verdict: patch math.ts, and a break in report.ts is reported. HTTP (POST /v1/check, GET /v1/health) and CLI. Exit codes an agent can read without parsing: 0 pass, 2 fail, 1 usage/crash. Use --compact when tokens are tight.
Before/after agent loop: disk + cold tsc vs warm LanguageService
Design decisions
| # | Decision | Why |
|---|---|---|
| 1 | Delta verdicts | pass means zero new errors. A legacy repo with 400 errors does not drown an agent fixing one function, and the agent does not get credit for debt it did not pay. fixedErrors tracks real cleanup. |
| 2 | One warm session across candidates | You stop paying process startup and a full re-parse on every attempt. Incremental analysis is the point. |
| 3 | Neutrality | JSON over loopback, no vendor SDK. I dogfood it in Cursor; Claude Code, OpenCode, CI, and evals can hit the same face. The protocol stays useful if more than one client can call it. |
Numbers
Compare the full agent loop, not a micro-bench that appends a comment:
BEFORE: write patch to disk → spawn tsc --noEmit → restore
AFTER: Session init once → warm in-memory overlays (+ fixes + impact)
Measured on Fedora, Node 22 (2026-07-08). Layered app = 40 core + 30 services + 20 routes (~92 files), graph-shaped, not a linear toy chain.
| Project | Candidates | BEFORE total | AFTER init+batch | Full-loop | Warm / cand |
|---|---|---|---|---|---|
| Fixture (2 files) | 10 | ~10.0 s | ~0.45 s | ~22× | ~9.6 ms |
| Layered app (92 files) | 10 | ~3.7 s | ~0.85 s | ~4.4× | ~45 ms |
| Layered app (92 files) | 20 | ~7.5 s | ~1.3 s | ~5.9× | ~43.5 ms |
If you quote one number, use the layered full-loop ratio (~5.9×). The fixture's 104× per-candidate figure is real and also toy-scale. On the layered run, a core signature break reported newErrors=3 in dependents, so the cross-file path is doing work.
npm run bench:compare:large
Version timeline
v0.1.0 — the core loop
Warm LanguageService session. Overlays. Baseline-relative pass/fail. Repair actions. HTTP + CLI. Loopback-only serve. Published to npm. CI green.
v0.2.0 — agent-first post-checker phases (current)
Keep TypeScript's parser + checker. Add phases after typechecking instead of rewriting the frontend.
| Phase | What shipped |
|---|---|
| A — Contract | Formal JSON Schema + protocolVersion: 1 |
| B — Impact | Export signature diff + findReferences def–use fan-out (--impact) |
| C — Repairs | confidence + machine-checkable preconditions on every fix |
| D — Speculation | Parallel candidates via worker_threads (--parallel) |
One shared LanguageService is not free-threaded. Parallel mode uses one Session per worker for isolation, which raises init cost. Sequential warm stays the default for interactive loops.
Related work
- Cursor shadow workspace — editor-layer warm LSPs. Complementary. I want Cursor agents calling veredicto under the retry loop, not replacing shadow workspace.
- LSP-inside-agents — human-format diagnostics for IDE UX, not batch candidate verdicts.
- ai-typescript-check — snippet-scale, ChatGPT-plugin era.
- Zero — right interface for a new language.
I have not found a project-scale, agent-native checker for existing TypeScript that owns this slot. That is the gap I am filling for Cursor agent loops first.
Limits
- Candidates are full-file contents, not unified diffs (yet)
- Diagnostic delta keyed by
file|code|message(span anchoring is next) - Impact is export-signature + references, not a full reaching-definitions solver
- No auth — binds loopback only and refuses anything else
- Parallel workers re-init Session each batch (warm pool is debt)
Install
npm install -g veredicto
# or: npx veredicto …
veredicto serve --project ./tsconfig.json --port 4117
curl -s localhost:4117/v1/check -d '{
"fixes": true,
"impact": true,
"candidates": [
{ "id": "patch-a", "files": { "src/report.ts": "…" } }
]
}'
veredicto check --project ./tsconfig.json --candidates candidates.json --fixes --impact --compact
npm: [email protected]
GitHub: github.com/kleosr/veredicto
Pitch: PITCH.md ·
Protocol: PROTOCOL.md ·
Bench: BENCH.md
The ask
If you are on Cursor: wire POST /v1/check (or the CLI) into your agent retry loop as a custom tool/command and tell me what happened to tokens-per-fix and retries-to-green. That is the feedback I want most.
If you own a big TypeScript repo: run npm run bench:compare -- --project path/to/tsconfig.json and open an issue with the numbers.
If the protocol shape is wrong for your loop: open an issue shaped like your loop.
Warm session, delta verdicts, structured repairs, TypeScript you already have. Built for Cursor agent loops. Thanks for reading.
Best,
kleosr
Cursor Ambassador
