agent-ultramode 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +113 -13
  2. package/cli.mjs +203 -14
  3. package/package.json +5 -7
  4. package/ultra.ts +167 -15
package/README.md CHANGED
@@ -6,6 +6,18 @@
6
6
 
7
7
  Took the [LLM-as-a-Verifier](https://github.com/llm-as-a-verifier/llm-as-a-verifier) paper (Kwok et al., 2026) and turned it into a simple `/ultra` command for coding agents.
8
8
 
9
+ > ### New in v2: repair climbs past the best-of-N ceiling, putting a mid-range flash model in the frontier's coding tier
10
+ >
11
+ > Picking the best of N attempts can never beat **oracle@N**. If none of the attempts solved the task, selection cannot invent a fix. That ceiling is where every best-of-N method stops.
12
+ >
13
+ > v2 adds a **verifier-guided repair pass**: the verifier critiques the winner, a repair runs against that critique, and the result is kept **only if it verifies better** (your tests when they exist, the verifier otherwise). Because repair can synthesize a fix no attempt produced, it goes past the ceiling. On a 24-task SWE-bench Lite slice it reaches **91.7%, above the 87.5% oracle@5** of its own candidate pool.
14
+ >
15
+ > Plus **adaptive early-exit**: N is a budget, not a quota. The moment an attempt passes your tests, ultra takes it and abandons the rest.
16
+ >
17
+ > Stack that on the verify step and a small, non-vision flash model reaches **90.4%** on Terminal-Bench 2.1's coding subset, inside the band of GPT-5.6 Sol (89.5%), Claude Opus 5 (89.1%) and Grok 4.6 (88.4%), at a fraction of the per-token cost. Ours is best-of-5 against their pass@1, so read it as reaching the tier, not a like-for-like beat.
18
+ >
19
+ > [How it works](#repair-and-early-exit-v2)
20
+
9
21
  Instead of shipping the agent's first attempt, `ultra` runs your task **N times in isolated git worktrees**, in parallel, then uses the **same model** as a verifier to pick the best result. When it is confident (or when most attempts succeeded) it applies the winning diff to your working tree; when the attempts diverge and it is not sure, it hands you the top candidates. No cross-model dependency, no extra services, no first-attempt lottery.
10
22
 
11
23
  And unlike most "add a verifier" posts, I benchmarked it before believing it. The receipts are below, good and bad.
@@ -18,14 +30,52 @@ And unlike most "add a verifier" posts, I benchmarked it before believing it. Th
18
30
 
19
31
  ## The receipts
20
32
 
21
- Benchmarked on **Terminal-Bench** with **DeepSeek V4 Flash 0731** as both the agent and the verifier, running inside **opencode** (N=5 attempts, same-model verifier). Verified end to end with **OpenCode**, **Claude Code**, and **cline** as the agent (the numbers above are the OpenCode run; a Claude Code number will be added once benchmarked). Works with any agent, not just opencode.
33
+ Benchmarked with **a small open model (DeepSeek V4 Flash 0731)** as both the agent and the verifier, best-of-5. Every number below is from a real run, good and bad. Full method, per-experiment setup and the honest scope notes are in the technical report: **[PAPER.md](./PAPER.md)**.
34
+
35
+ ![On Terminal-Bench 2.1, ultra ties the paper's verifier; on SWE-bench Lite, repair beats the oracle ceiling](viz/chart.png)
36
+
37
+ ![Cost versus score on Terminal-Bench 2.1: a small non-vision flash model reaches 90.4% on the coding subset, inside the frontier's band, at a fraction of the per-token cost. Inset: verifier-guided repair reaches 91.7% on SWE-bench Lite, above the 87.5% oracle@5 ceiling.](viz/chart2.png)
38
+
39
+ ### Terminal-Bench 2.1: reasoned verify ties the paper, with far fewer calls
40
+
41
+ | Stage | Terminal-Bench 2.1 (89 tasks) |
42
+ |---|:---:|
43
+ | base@1 (single shot) | 78.7% |
44
+ | **ultra (reasoned verify)** | **87.6%** (78/89) |
45
+ | oracle@5 (selection ceiling) | 96.6% |
46
+
47
+ The reasoned verifier **ties the LLM-as-a-Verifier paper's logprob PPT** (88.0% +/- 0.6%) while using **7.5x fewer verifier calls** (576 vs 4,320) and **no logprobs** at all. Same accuracy, a fraction of the verifier budget, and it runs on any OpenAI-compatible endpoint.
48
+
49
+ ### The non-vision handicap (and the fair arena)
50
+
51
+ DeepSeek V4 Flash has **no vision**. Terminal-Bench 2.1 mixes image tasks in with the coding ones, so the blended score carries a penalty a text-only model can never pay down:
52
+
53
+ | Terminal-Bench 2.1 split | tasks | ultra |
54
+ |---|:---:|:---:|
55
+ | Vision tasks | 12 | 58% |
56
+ | **Coding tasks** | **77** | **90.4%** |
57
+ | Blended (headline) | 89 | 87.6% |
58
+
59
+ On the **coding subset**, the fair arena for a small, non-vision flash model, ultra reaches **90.4%**.
22
60
 
23
- | Slice | base@1 (single shot) | ultra (best-of-N + verify) | oracle@5 (ceiling) |
24
- |---|:---:|:---:|:---:|
25
- | All 15 tasks | 24% | **33%** | 40% |
26
- | 4 recoverable tasks | 40% | **75%** | rescued 3 of 4 |
61
+ For context on the full 89-task set, as reported by Artificial Analysis (pass@1, avg of 3): GPT-5.6 Sol 89.5%, Claude Opus 5 89.1%, Grok 4.6 88.4%. Their coding-subset numbers are unpublished. So this is not a beat: ours is best-of-5 and theirs is pass@1. The honest framing is that a small, non-vision flash model reaches the **frontier's coding tier at a fraction of the cost**.
27
62
 
28
- Overall the verifier captured about **56% of the pass@1 to pass@5 headroom**. On the tasks with real variance, where best-of-N can actually help, it took the passing attempt on `chess-best-move`, `new-encrypt-command`, and `decommissioning-service` (missing only `jupyter-notebook-server`). A 40 to 75 jump on recoverable tasks is not a rounding error.
63
+ ### SWE-bench Lite: repair climbs past the selection ceiling
64
+
65
+ Pure code repair, no vision, 24 django/pytest tasks, N=5:
66
+
67
+ | Stage | SWE-bench Lite (24 tasks) |
68
+ |---|:---:|
69
+ | base@1 (single shot) | 70.8% |
70
+ | ultra (reasoned verify) | 75.0% |
71
+ | oracle@5 (selection ceiling) | 87.5% |
72
+ | **ultra + repair** | **91.7%** (22/24) |
73
+
74
+ This is the clean "past the ceiling" result. Selection alone can never beat the best attempt it was handed (oracle@5 = 87.5%). Best-of-N **repair** rescued 2 tasks none of the five attempts solved, landing **above** the oracle ceiling at 91.7%.
75
+
76
+ ### The trace-reading ceiling
77
+
78
+ The verifier is strong on code diffs and weak on **self-reported terminal outcomes**. It saturates on convincing-but-wrong self-reports: on `extract-elf`, a failing run reported "4102 entries, zero mismatches" when 698 was the correct answer, and the verifier believed it. Repair plus a real test overcomes this; a verifier reading a trace alone cannot. This is why SWE-bench (pure code diffs) is the clean signal and Terminal-Bench is muddied by both vision and self-report.
29
79
 
30
80
  ## How the competition works
31
81
 
@@ -60,13 +110,38 @@ Overall the verifier captured about **56% of the pass@1 to pass@5 headroom**. On
60
110
 
61
111
  Progress streams live: phase titles in the web UI tool card, and toasts in the terminal.
62
112
 
113
+ ## Repair and early-exit (v2)
114
+
115
+ Two additions in v2, both designed so they can only help.
116
+
117
+ **Verifier-guided best-of-N repair (beyond the best-of-N ceiling).** Selection alone can never do better than the best attempt it was handed (its ceiling is oracle@N). After the tournament picks a winner, `ultra` critiques it and runs up to `--repair-n` guided repair passes (default 2), keeping the **first pass that passes your tests**; if none pass, it keeps the tournament winner unchanged. The keep-check uses your repo's own tests if there are any, otherwise the reasoned verifier. It is regression-safe and **never worse than the winner**: a repair is adopted only when it clears the bar, so repair can solve tasks no single attempt did while a bad repair is simply discarded. In a SWE-bench Lite run this reached 91.7%, above the 87.5% oracle@5 ceiling, by rescuing two tasks none of the five attempts solved.
118
+
119
+ **Adaptive early-exit (N as an upper bound).** N is a budget, not a quota. The moment an attempt's diff **passes your tests**, `ultra` takes it as the verified winner, abandons the still-running attempts, and skips the tournament and repair. Only this hard signal stops early; low verifier confidence never does, so you never trade quality for speed. With no detectable test command it runs all N, exactly as before. Turn repair off with `--no-repair` (or `ULTRA_REPAIR=0`); turn early-exit off with `--no-early-exit` (or `ULTRA_NO_EARLY_EXIT=1`).
120
+
63
121
  ## Install
64
122
 
123
+ Pick your agent. Each is one command, and each gives you the same native `/ultra <task>`.
124
+
125
+ **Claude Code**
126
+
127
+ ```sh
128
+ mkdir -p ~/.claude/commands
129
+ curl -fsSL https://raw.githubusercontent.com/maverick-tr/agent-ultramode/main/install/ultra.md -o ~/.claude/commands/ultra.md
130
+ ```
131
+
132
+ **Grok**
133
+
134
+ ```sh
135
+ grok plugin install maverick-tr/agent-ultramode --trust
136
+ ```
137
+
138
+ **opencode**
139
+
65
140
  ```sh
66
141
  opencode plugin agent-ultramode
67
142
  ```
68
143
 
69
- That is the whole setup. With no options it drafts and verifies with your **current session model**, so `/ultra <task>` just works:
144
+ Then run it in any session:
70
145
 
71
146
  ```text
72
147
  /ultra fix the failing test in foo/bar
@@ -74,7 +149,17 @@ That is the whole setup. With no options it drafts and verifies with your **curr
74
149
 
75
150
  `ultra` branches attempts off `HEAD`, so run it inside a git repo with at least one commit.
76
151
 
77
- **Pin a specific model (optional).** To draft and verify with a model other than the session one:
152
+ On **Claude Code** and **Grok**, attempts run as parallel subagents in the host itself (Grok shows them live in the Tasks pane, Ctrl+G). On **opencode** they run as isolated git worktrees and the winning diff is applied to your working tree. Same loop either way: fan out N, verify, repair the winner, keep it only if it is better.
153
+
154
+ ### More install options
155
+
156
+ **Claude Code, as a full plugin** (instead of the single file above):
157
+
158
+ ```sh
159
+ /plugin marketplace add maverick-tr/agent-ultramode
160
+ ```
161
+
162
+ **opencode, pinning a specific model (optional).** To draft and verify with a model other than the session one:
78
163
 
79
164
  ```jsonc
80
165
  { "plugin": [ ["agent-ultramode", { "model": "myprovider/my-model" }] ] }
@@ -105,6 +190,10 @@ Every option also reads from an `ULTRA_*` env var.
105
190
  | `effort` | `"none"` | `reasoning_effort` for the verifier calls (kept low so judging stays fast) |
106
191
  | `concurrency` | `6` | how many attempts run at once |
107
192
  | `agentTimeout` | `600000` | per-attempt timeout in ms |
193
+ | `repair` | `true` | run verifier-guided repair on the winner, kept only if it verifies better |
194
+ | `repairN` | `2` | repair passes to run (1 to 5); keeps the first that passes your tests, else the winner |
195
+ | `test` | auto-detected | test command for the repair keep-check and early-exit (npm / pytest / cargo / go if unset) |
196
+ | `earlyExit` | `true` | stop as soon as an attempt passes the test command, and apply it |
108
197
 
109
198
  ## Use without opencode (the CLI)
110
199
 
@@ -140,9 +229,17 @@ The 6 attempts round-robin across the three agents, and the verifier picks the b
140
229
 
141
230
  The verifier needs an OpenAI-compatible endpoint: by default it reads `OPENAI_API_KEY` and `OPENAI_BASE_URL`, or pass `--api-key` / `--base-url` / `--verify-model`. Run `agent-ultramode --help` for all options.
142
231
 
232
+ ## How the hosts differ
233
+
234
+ One plugin serves all three, each using its host's own subagent primitive: Grok's `spawn_subagent` with worktree isolation, Claude Code's `Agent` tool, and opencode's own worktree runner. The best-of-N, verify and repair loop is identical; only the fan-out mechanism and the progress UI change. Both the Claude Code and Grok paths are verified end to end.
235
+
236
+ ## DeepSeek Harness (experimental)
237
+
238
+ A native DeepSeek Harness `/ultra` plugin exists but is not recommended yet: the harness surfaces each attempt as a separate child session, which is noisy in the web UI. Use the Grok or Claude Code plugins above for a clean native `/ultra` today.
239
+
143
240
  ## Why I built this, and the honest story
144
241
 
145
- I wanted to know if best-of-N verification could squeeze real quality out of a cheap-but-capable model without reaching for a bigger, pricier one. So, ran it on real Terminal-Bench tasks and let the numbers decide. What I found:
242
+ I wanted to know if best-of-N verification could squeeze real quality out of a small-but-capable model without reaching for a bigger, pricier one. So, ran it on real Terminal-Bench tasks and let the numbers decide. What I found:
146
243
 
147
244
  - **Planning-first best-of-N does nothing for execution tasks.** Drafting N plans, picking the best, then executing once: **0 out of 5** outcomes changed on Terminal-Bench, at 3 to 10 times the cost. It amplifies effort, not capability. I dropped it.
148
245
  - **Best-of-N over full trajectories plus a same-model verifier does work.** This is the version that ships.
@@ -153,19 +250,22 @@ It significantly helps on the tasks that matter, and I can point at the per-task
153
250
 
154
251
  ## The honest caveats (because benchmarks lie by omission)
155
252
 
156
- 1. **Confidence is a noisy signal at this scale.** The one miss (`jupyter`) had confidence 0.25, higher than two tasks it got right (0.17, 0.21). Some beyond-capability tasks even show high confidence while being pure failures. "Trust when confident" is directionally real, not a clean gate.
157
- 2. **It cannot help beyond-capability tasks.** 9 of the 15 never passed in 5 tries, and no verifier manufactures a solution that was never there.
158
- 3. **n=15 (4 with real variance) is a credible but modest sample.** Enough to headline honestly, not enough to over-claim precision.
253
+ 1. **The model has no vision, and image tasks are out of fair reach.** DeepSeek V4 Flash is text-only, so the 12 Terminal-Bench 2.1 vision tasks (58%) drag the blended score down in a way no amount of verifying can fix. The **coding subset (90.4%)** is the fair arena for a non-vision model; read the blended 87.6% with that in mind.
254
+ 2. **The frontier comparison is not apples to apples.** Our headline numbers are **best-of-5**; the GPT-5.6 / Opus 5 / Grok 4.6 numbers are **pass@1**. So "reaches the frontier's coding tier" is a cost-and-capability framing, not a claim that this small model beats them head to head.
255
+ 3. **The verifier hits a trace-reading ceiling.** It reasons well over code diffs but is fooled by convincing-but-wrong self-reports (the `extract-elf` run that claimed "4102 entries, zero mismatches" when 698 was correct). Selection over self-reported terminal outcomes saturates; only repair plus a real test breaks past it.
256
+ 4. **SWE-bench is the clean signal; Terminal-Bench is muddied.** SWE-bench Lite is pure code repair with real tests, which is why repair cleanly beats the oracle@5 ceiling there (91.7% > 87.5%). Terminal-Bench mixes in vision and self-reported outcomes, so its blended number carries penalties that have nothing to do with the verifier.
159
257
 
160
258
  ## What this likely means at scale
161
259
 
162
- The 15 tasks were deliberately failure-skewed, so the +9 points is not what you would see on the whole benchmark. On the full set the model already passes about 83%, so most tasks have no headroom. My honest estimate for a full run is a **modest +2 to +5 points on average**, because the dramatic gains only land on the minority of tasks the model *sometimes* solves. Average lift small, per-recoverable-task lift large. Same fact, two views.
260
+ On any full benchmark most tasks have no headroom: the model either always solves them or never does, and a verifier changes neither. The lift concentrates on the minority of tasks the model *sometimes* solves, where selection can grab the passing attempt and repair can climb one rung higher. So on Terminal-Bench 2.1 the base-to-verify jump is a real **+8.9 points blended** (78.7% to 87.6%), and on the coding subset it lands at **90.4%**, but the honest reading is that the dramatic per-task gains average out into a moderate headline lift, larger on repair-friendly pure-code sets like SWE-bench than on mixed sets diluted by vision. Average lift moderate, per-recoverable-task lift large. Same fact, two views.
163
261
 
164
262
  ## Roadmap
165
263
 
166
264
  - [x] **Standalone CLI** (`npx agent-ultramode`) so the loop runs anywhere, with any agent, no opencode required.
167
265
  - [x] **Multiple models in one pass** (repeatable `--agent`): spread attempts across different models, one neutral verifier picks the best.
168
266
  - [x] **OpenCode, Claude Code, and cline** verified end to end.
267
+ - [x] **Verifier-guided best-of-N repair** (v2): up to `--repair-n` critique-guided passes on the winner, keeping the first that passes your tests. Beats the oracle@N ceiling on SWE-bench Lite (91.7% vs 87.5%).
268
+ - [x] **Adaptive early-exit** (v2): N becomes an upper bound; a test-passing attempt ends the run early with no quality trade-off.
169
269
  - [ ] **First-class agent integrations** (tuned defaults and a benchmark number) for Grok, Pi, and Codex.
170
270
  - [ ] Native slash-command or MCP packaging per agent. Contributions welcome.
171
271
 
package/cli.mjs CHANGED
@@ -9,7 +9,7 @@
9
9
  // Node 18+ (uses global fetch and node:util parseArgs). Zero dependencies.
10
10
  import { execFile } from "node:child_process"
11
11
  import { promisify } from "node:util"
12
- import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises"
12
+ import { mkdtemp, rm, writeFile, mkdir, readFile } from "node:fs/promises"
13
13
  import { tmpdir } from "node:os"
14
14
  import { join } from "node:path"
15
15
  import { parseArgs } from "node:util"
@@ -39,8 +39,32 @@ OPTIONS
39
39
  --repo <path> repo to run in (default: current directory)
40
40
  --concurrency <int> attempts to run at once (default 6)
41
41
  --effort <level> verifier reasoning_effort (default none)
42
+ --test <cmd> test command for the repair keep-check (else auto-detected:
43
+ npm test / pytest / cargo / go). Passing exit code = ok.
44
+ --repair-agent <cmd> agent for the repair pass ("{task}" substituted; default: --agent)
45
+ --repair-n <int> repair attempts to run, 1 to 5 (default 2; env ULTRA_REPAIR_N).
46
+ Keeps the best that verifies; never worse than the winner.
47
+ --no-repair disable the verifier-guided repair pass (on by default)
48
+ --no-early-exit disable adaptive early-exit (on by default when a test
49
+ command is available)
42
50
  -h, --help show this help
43
51
 
52
+ REPAIR (on by default; ULTRA_REPAIR=0 or --no-repair to disable)
53
+ After the tournament picks a winner, ultra critiques it and runs --repair-n
54
+ guided repair passes (default 2), each in its own worktree seeded with the
55
+ winner's diff and the same critique, then keeps the best that verifies:
56
+ with a test command, the first repair that PASSES the tests; otherwise the
57
+ first repair the reasoned verifier clearly prefers over the winner. If none
58
+ qualify, the winner is kept. So repair can lift the result past the best-of-N
59
+ ceiling, and the applied result is never worse than the tournament winner.
60
+
61
+ ADAPTIVE EARLY-EXIT (on by default; --no-early-exit or ULTRA_NO_EARLY_EXIT=1 to disable)
62
+ N is an upper bound. If an attempt PASSES the repo's tests (--test or an
63
+ auto-detected npm/pytest/cargo/go command), ultra takes it as a verified
64
+ winner, abandons the still-running attempts, and skips the tournament and
65
+ repair. Only this hard signal stops early; low verifier confidence never does,
66
+ so the result can never be worse than running all N.
67
+
44
68
  EXAMPLES
45
69
  # default: opencode as the per-attempt agent, verify with your OpenAI key
46
70
  agent-ultramode "fix the failing test in foo/bar"
@@ -131,6 +155,71 @@ async function tournament(C, task, summaries) {
131
155
  return { ranked, conf: ratio(ranked[0]) - ratio(ranked[1]) }
132
156
  }
133
157
 
158
+ // --- verifier-guided repair (generic: never benchmark-specific) ------------
159
+ // After the tournament picks a winner, critique it and run one guided repair
160
+ // pass, then KEEP the repair only when it verifies as better (repo's own tests
161
+ // if available, else the reasoned verifier). So repair can only help, not hurt.
162
+
163
+ async function critique(C, task, diff, log) {
164
+ const prompt =
165
+ `A coding task and the patch a verifier selected as the best of several attempts. You are a strict ` +
166
+ `reviewer. In 3-5 sentences name the MOST LIKELY remaining problems: missed edge cases, incomplete ` +
167
+ `coverage, wrong root cause, or regressions it could introduce. If it looks fully correct, say so and ` +
168
+ `name the single thing most worth double-checking. Be concrete and actionable.\n\n` +
169
+ `TASK:\n${task}\n\nPATCH:\n${diff.slice(0, 6000)}\n\nAGENT LOG (tail):\n${(log || "").slice(-1200)}\n\nReview:`
170
+ try { return (await chat(C, prompt)).trim().slice(0, 1500) } catch { return "" }
171
+ }
172
+
173
+ const XDG_ENV = (d) => ({
174
+ ...process.env, XDG_DATA_HOME: d, XDG_STATE_HOME: d, XDG_CACHE_HOME: d,
175
+ OPENCODE_DISABLE_DEFAULT_PLUGINS: "1", OPENCODE_DISABLE_AUTOUPDATE: "1", OPENCODE_DISABLE_MODELS_FETCH: "1",
176
+ })
177
+
178
+ // run an agent command inside a fresh worktree (optionally pre-seeded with a
179
+ // base diff), return the full diff off `base` + the log tail.
180
+ async function agentInWorktree(repo, base, work, tag, agentCmd, taskText, seedDiff, timeout) {
181
+ const wt = join(work, tag)
182
+ await git(repo, "worktree", "add", "--detach", wt, base)
183
+ if (seedDiff && seedDiff.trim()) {
184
+ const p = join(work, `${tag}.seed.patch`); await writeFile(p, seedDiff)
185
+ await git(wt, "apply", "--3way", p).catch(() => {})
186
+ }
187
+ const cmd = agentCmd.replace("{task}", taskText.replace(/"/g, '\\"'))
188
+ const d = join(work, `${tag}-xdg`); await mkdir(d, { recursive: true }).catch(() => {})
189
+ let log = ""
190
+ try {
191
+ const { stdout, stderr } = await execFileP("bash", ["-lc", `exec </dev/null; ${cmd}`], { cwd: wt, env: XDG_ENV(d), timeout, maxBuffer: 32 * 1024 * 1024 })
192
+ log = (stdout || "") + (stderr || "")
193
+ } catch (e) { log = `agent error: ${e?.message || e}` }
194
+ await git(wt, "add", "-A").catch(() => {})
195
+ const diff = await git(wt, "diff", "--cached").catch(() => "")
196
+ await git(repo, "worktree", "remove", "--force", wt).catch(() => {})
197
+ return { diff, log }
198
+ }
199
+
200
+ // true iff `testCmd` exits 0 in a fresh worktree with `diff` applied.
201
+ async function runTests(repo, base, work, tag, diff, testCmd, timeout) {
202
+ const wt = join(work, `t-${tag}`)
203
+ await git(repo, "worktree", "add", "--detach", wt, base)
204
+ try {
205
+ if (diff.trim()) { const p = join(work, `t-${tag}.patch`); await writeFile(p, diff); await git(wt, "apply", "--3way", p).catch(() => {}) }
206
+ await execFileP("bash", ["-lc", `exec </dev/null; ${testCmd}`], { cwd: wt, env: process.env, timeout, maxBuffer: 32 * 1024 * 1024 })
207
+ return true
208
+ } catch { return false }
209
+ finally { await git(repo, "worktree", "remove", "--force", wt).catch(() => {}) }
210
+ }
211
+
212
+ // auto-detect a repo test command if the user didn't pass --test.
213
+ async function detectTestCmd(repo) {
214
+ const has = async (f) => !!(await readFile(join(repo, f), "utf8").catch(() => ""))
215
+ const pkg = await readFile(join(repo, "package.json"), "utf8").catch(() => "")
216
+ if (pkg && /"test"\s*:/.test(pkg) && !/no test specified/.test(pkg)) return "npm test --silent"
217
+ if (await has("pytest.ini") || await has("pyproject.toml") || await has("setup.cfg") || await has("tox.ini")) return "python -m pytest -q"
218
+ if (await has("Cargo.toml")) return "cargo test -q"
219
+ if (await has("go.mod")) return "go test ./..."
220
+ return ""
221
+ }
222
+
134
223
  async function main() {
135
224
  const { values, positionals } = parseArgs({
136
225
  allowPositionals: true,
@@ -146,6 +235,11 @@ async function main() {
146
235
  repo: { type: "string" },
147
236
  concurrency: { type: "string" },
148
237
  effort: { type: "string" },
238
+ "no-repair": { type: "boolean" },
239
+ "no-early-exit": { type: "boolean" },
240
+ "repair-agent": { type: "string" },
241
+ "repair-n": { type: "string" },
242
+ test: { type: "string" },
149
243
  help: { type: "boolean", short: "h" },
150
244
  },
151
245
  })
@@ -160,6 +254,11 @@ async function main() {
160
254
  const conf = Number(values.conf ?? 0.34)
161
255
  const cc = int(values.concurrency, 6, 1, 12)
162
256
  const agentTimeout = 600000
257
+ const doRepair = !values["no-repair"] && process.env.ULTRA_REPAIR !== "0"
258
+ const repairN = int(values["repair-n"] ?? process.env.ULTRA_REPAIR_N, 2, 1, 5)
259
+ const earlyExit = !values["no-early-exit"] && process.env.ULTRA_NO_EARLY_EXIT !== "1"
260
+ const repairAgent = values["repair-agent"] || (agents[0] || 'opencode run "{task}"')
261
+ let testCmd = values.test ?? process.env.ULTRA_TEST ?? ""
163
262
  const C = {
164
263
  verifyModel: values["verify-model"] || process.env.ULTRA_VERIFY_MODEL || process.env.OPENAI_MODEL || "gpt-4o-mini",
165
264
  baseURL: values["base-url"] || process.env.OPENAI_BASE_URL || process.env.ULTRA_BASE_URL || "https://api.openai.com/v1",
@@ -204,26 +303,63 @@ async function main() {
204
303
  }
205
304
  const limit = pLimit(Math.max(1, Math.min(cc, n)))
206
305
  const esc = task.replace(/"/g, '\\"')
207
- const outs = await Promise.all(worktrees.map((wt, i) => limit(async () => {
306
+
307
+ // Adaptive early-exit (#141): N is an upper bound. If a completed attempt
308
+ // PASSES the repo's tests (a hard, non-regressible signal), take it as the
309
+ // verified winner, abandon the still-running/queued attempts, and skip the
310
+ // tournament + repair. Low verifier confidence never stops early, so the
311
+ // result can never be worse than running all N.
312
+ if (earlyExit && testCmd === "") testCmd = await detectTestCmd(repo)
313
+ const exitTestCmd = earlyExit ? testCmd : ""
314
+ if (exitTestCmd) err(`>>> early-exit armed: stop as soon as an attempt passes '${exitTestCmd}'`)
315
+ const ac = new AbortController()
316
+ let verified = null
317
+ const outs = new Array(n)
318
+
319
+ const runAgent = (wt, i) => new Promise((resolve) => {
208
320
  const cmd = agents[i % agents.length].replace("{task}", esc)
209
321
  const d = join(work, `xdg-${i}`)
210
- await mkdir(d, { recursive: true }).catch(() => {})
211
- const env = {
212
- ...process.env,
213
- XDG_DATA_HOME: d, XDG_STATE_HOME: d, XDG_CACHE_HOME: d,
214
- OPENCODE_DISABLE_DEFAULT_PLUGINS: "1", OPENCODE_DISABLE_AUTOUPDATE: "1", OPENCODE_DISABLE_MODELS_FETCH: "1",
215
- }
216
- let log = ""
217
- try {
218
- const { stdout, stderr } = await execFileP("bash", ["-lc", `exec </dev/null; ${cmd}`], { cwd: wt, env, timeout: agentTimeout, maxBuffer: 32 * 1024 * 1024 })
219
- log = (stdout || "") + (stderr || "")
220
- } catch (e) { log = `agent error: ${e?.message || e}` }
322
+ mkdir(d, { recursive: true }).catch(() => {}).finally(() => {
323
+ const env = {
324
+ ...process.env,
325
+ XDG_DATA_HOME: d, XDG_STATE_HOME: d, XDG_CACHE_HOME: d,
326
+ OPENCODE_DISABLE_DEFAULT_PLUGINS: "1", OPENCODE_DISABLE_AUTOUPDATE: "1", OPENCODE_DISABLE_MODELS_FETCH: "1",
327
+ }
328
+ const child = execFile("bash", ["-lc", `exec </dev/null; ${cmd}`],
329
+ { cwd: wt, env, timeout: agentTimeout, maxBuffer: 32 * 1024 * 1024, detached: true },
330
+ (e, stdout, stderr) => resolve(((stdout || "") + (stderr || "")) || (e ? `agent error: ${e?.message || e}` : "")))
331
+ // if a verified winner is found elsewhere, kill this attempt's process group
332
+ ac.signal.addEventListener("abort", () => { try { process.kill(-child.pid, "SIGTERM") } catch {} }, { once: true })
333
+ })
334
+ })
335
+
336
+ await Promise.all(worktrees.map((wt, i) => limit(async () => {
337
+ if (ac.signal.aborted) return // verified winner already found; don't start queued attempts
338
+ const log = await runAgent(wt, i)
221
339
  await git(wt, "add", "-A").catch(() => {})
222
340
  const diff = await git(wt, "diff", "--cached").catch(() => "")
341
+ outs[i] = { diff, log, summary: `AGENT LOG (tail):\n${log.slice(-1500)}\n\nDIFF:\n${diff.slice(0, 6000) || "(no changes)"}` }
223
342
  err(` attempt ${i} [${binOf(agents[i % agents.length])}]: ${diff ? diff.length + " diff chars" : "no changes"}`)
224
- return { diff, summary: `AGENT LOG (tail):\n${log.slice(-1500)}\n\nDIFF:\n${diff.slice(0, 6000) || "(no changes)"}` }
343
+ if (exitTestCmd && !verified && diff.trim()) {
344
+ const pass = await runTests(repo, base, work, `ee-${i}`, diff, exitTestCmd, agentTimeout)
345
+ if (pass && !verified) { verified = { i, ...outs[i] }; err(` ✔ attempt ${i} PASSED tests; abandoning the rest`); ac.abort() }
346
+ }
225
347
  })))
226
348
 
349
+ // verified fast path: a test-passing attempt is correct by the repo's own
350
+ // criterion, so apply it directly (no tournament, no repair, no regression).
351
+ if (verified && verified.diff.trim()) {
352
+ const patch = join(work, "winner.patch")
353
+ await writeFile(patch, verified.diff)
354
+ try {
355
+ await git(repo, "apply", "--3way", patch)
356
+ console.log(`\n🏆 attempt ${verified.i} passed the repo's tests (${exitTestCmd}); applied it and abandoned the rest. Review, then commit.`)
357
+ } catch (e) {
358
+ console.log(`\nattempt ${verified.i} passed tests but the patch did not apply cleanly (${e?.message || e}). The diff:\n\n${verified.diff.slice(0, 8000)}`)
359
+ }
360
+ return
361
+ }
362
+
227
363
  const diffs = outs.map((o) => o.diff)
228
364
  const summaries = outs.map((o) => o.summary)
229
365
  if (diffs.every((d) => !d.trim())) {
@@ -234,6 +370,59 @@ async function main() {
234
370
  err(`>>> verify: probabilistic pivot tournament (${C.verifyModel}) ...`)
235
371
  const { ranked, conf: margin } = await tournament(C, task, summaries)
236
372
  const best = ranked[0]
373
+
374
+ // --- repair: critique the winner, run repairN guided passes, keep the best that verifies.
375
+ // Best-of-N repair (regression-safe): fan out repairN attempts off `base`, each seeded with
376
+ // the winner's diff and the SAME critique injected (one critique, reused). Keep-policy:
377
+ // * with tests: keep the FIRST repair that PASSES the repo's tests; else keep the winner.
378
+ // (a passing repair is correct by the repo's own criterion, so it cannot regress.)
379
+ // * without tests: keep the FIRST repair the reasoned verifier CLEARLY prefers over the
380
+ // current winner (same >k/2 threshold as before); else keep the winner.
381
+ // INVARIANT: the final applied result is never worse than the tournament winner.
382
+ if (doRepair && diffs[best].trim()) {
383
+ err(`>>> repair: critique + ${repairN} guided pass(es) on the winner (keep the best that verifies) ...`)
384
+ const crit = await critique(C, task, diffs[best], outs[best].log)
385
+ const rtask =
386
+ `${task}\n\n--- A previous attempt (already applied to your working tree) produced a partial ` +
387
+ `solution. A reviewer flagged the issues below. Improve and COMPLETE it, keeping what is correct; ` +
388
+ `verify before finishing. ---\nREVIEWER NOTES:\n${crit}`
389
+ if (testCmd === "") testCmd = await detectTestCmd(repo)
390
+ // run the repairN attempts in parallel, honoring the concurrency limiter (cc).
391
+ const rlimit = pLimit(C.cc)
392
+ const reps = await Promise.all(Array.from({ length: repairN }, (_, r) => rlimit(async () => {
393
+ const rr = await agentInWorktree(repo, base, work, `repair-${r}`, repairAgent, rtask, diffs[best], agentTimeout)
394
+ const rsum = `AGENT LOG (tail):\n${rr.log.slice(-1500)}\n\nDIFF:\n${rr.diff.slice(0, 6000) || "(no changes)"}`
395
+ err(` repair ${r} [${binOf(repairAgent)}]: ${rr.diff ? rr.diff.length + " diff chars" : "no changes"}`)
396
+ return { diff: rr.diff, summary: rsum }
397
+ })))
398
+ // only real, changed candidates are eligible.
399
+ const cands = reps.filter((x) => x.diff.trim() && x.diff !== diffs[best])
400
+ let kept = false, why = ""
401
+ if (!cands.length) { err(` repair produced no new change; winner kept`) }
402
+ else if (testCmd) {
403
+ // regression-safe: keep the FIRST repair that PASSES the repo's own tests.
404
+ err(` keep-check: running tests (${testCmd}) on ${cands.length} repair candidate(s) ...`)
405
+ for (let r = 0; r < cands.length; r++) {
406
+ const pass = await runTests(repo, base, work, `rep-${r}`, cands[r].diff, testCmd, agentTimeout)
407
+ if (pass) { diffs[best] = cands[r].diff; summaries[best] = cands[r].summary; kept = true; why = `repair ${r} passes the tests`; break }
408
+ }
409
+ if (!kept) why = "no repair passed the tests"
410
+ } else {
411
+ // no tests: keep the FIRST repair the verifier CLEARLY prefers over the current winner.
412
+ const preferRepair = async (rsum) => { // reasoned verifier: keep repair only if it clearly wins
413
+ const limit = pLimit(C.cc)
414
+ const votes = await Promise.all(Array.from({ length: C.k }, () => judge(C, limit, task, summaries[best], rsum)))
415
+ return votes.filter((v) => v === "B").length > C.k / 2 // B = repaired
416
+ }
417
+ for (let r = 0; r < cands.length; r++) {
418
+ if (await preferRepair(cands[r].summary)) { diffs[best] = cands[r].diff; summaries[best] = cands[r].summary; kept = true; why = `verifier prefers repair ${r}`; break }
419
+ }
420
+ if (!kept) why = "verifier keeps winner"
421
+ }
422
+ if (kept) err(` ✔ repair KEPT (${why})`)
423
+ else if (cands.length) err(` all repairs discarded (${why})`)
424
+ }
425
+
237
426
  const nonEmpty = diffs.filter((d) => d.trim()).length
238
427
  const majority = nonEmpty >= Math.max(2, Math.ceil(n / 2))
239
428
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-ultramode",
3
- "version": "0.1.4",
4
- "description": "Best-of-N for coding agents with a same-model verifier: run your task N times in isolated git worktrees and apply the verified winner. A /ultra command for opencode.",
3
+ "version": "0.2.0",
4
+ "description": "Best-of-N for coding agents with a same-model verifier, verifier-guided repair, and adaptive early-exit: run your task N times in isolated git worktrees, verify, repair the winner past the best-of-N ceiling, and apply it. A /ultra command for opencode.",
5
5
  "module": "ultra.ts",
6
6
  "main": "ultra.ts",
7
7
  "exports": {
@@ -28,6 +28,8 @@
28
28
  "verifier",
29
29
  "llm-as-a-verifier",
30
30
  "coding-agent",
31
+ "repair",
32
+ "self-repair",
31
33
  "ultra"
32
34
  ],
33
35
  "license": "MIT",
@@ -37,10 +39,6 @@
37
39
  "url": "git+https://github.com/maverick-tr/agent-ultramode.git"
38
40
  },
39
41
  "devDependencies": {
40
- "@opencode-ai/plugin": "^1.0.153",
41
- "@types/bun": "latest"
42
- },
43
- "peerDependencies": {
44
- "typescript": "^5"
42
+ "@opencode-ai/plugin": "^1.0.153"
45
43
  }
46
44
  }
package/ultra.ts CHANGED
@@ -8,9 +8,11 @@
8
8
  * low confidence -> shows you the top candidates and applies nothing
9
9
  * (a low-confidence pick is a coin flip and should not be applied silently).
10
10
  *
11
- * This is the version validated on Terminal-Bench: plan-first best-of-N gave no edge,
12
- * but best-of-N over full trajectories + this verifier lifted the recoverable tasks
13
- * from 40% to 75% (n=15, same model, no cross-model dependency).
11
+ * Validated on Terminal-Bench 2.1 (89 tasks, same model as the solver, no cross-model
12
+ * dependency): base@1 78.7% -> 87.6% with best-of-5 and this verifier, against an
13
+ * oracle@5 ceiling of 96.6%. Plan-first best-of-N gave no edge; best-of-N over full
14
+ * trajectories is what moved the number. On 24 SWE-bench-style django/pytest tasks,
15
+ * verifier-guided repair reached 91.7%, above the 87.5% oracle@5 of the attempts.
14
16
  *
15
17
  * Install (opencode.json):
16
18
  * { "plugin": [ ["file:///abs/path/ultra.ts", { "model": "provider/model", "n": 5 }] ] }
@@ -22,14 +24,14 @@ import type { Plugin } from "@opencode-ai/plugin"
22
24
  import { tool } from "@opencode-ai/plugin"
23
25
  import { execFile } from "node:child_process"
24
26
  import { promisify } from "node:util"
25
- import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises"
27
+ import { mkdtemp, rm, writeFile, mkdir, readFile } from "node:fs/promises"
26
28
  import { existsSync } from "node:fs"
27
29
  import { tmpdir, homedir } from "node:os"
28
30
  import { join } from "node:path"
29
31
 
30
32
  const execFileP = promisify(execFile)
31
33
  type Opts = Record<string, any>
32
- const VERSION = "0821g"
34
+ const VERSION = "v2-0824r"
33
35
 
34
36
  interface Cfg {
35
37
  url: string
@@ -45,6 +47,10 @@ interface Cfg {
45
47
  judgeTokens: number
46
48
  cc: number
47
49
  agentTimeoutMs: number
50
+ repair: boolean
51
+ repairN: number
52
+ repairAgent: string
53
+ testCmd: string
48
54
  }
49
55
 
50
56
  const num = (v: any, def: number, lo: number, hi: number): number => {
@@ -75,6 +81,12 @@ function buildCfg(opts: Opts): Cfg {
75
81
  judgeTokens: num(opts.judgeTokens ?? process.env.ULTRA_JUDGE_TOKENS, 12000, 500, 200000),
76
82
  cc: num(opts.concurrency ?? process.env.ULTRA_CC, 6, 1, 12),
77
83
  agentTimeoutMs: num(opts.agentTimeout ?? process.env.ULTRA_AGENT_TIMEOUT, 600000, 30000, 1800000),
84
+ // verifier-guided repair: on by default; a repaired winner is kept only when it verifies better.
85
+ repair: String(opts.repair ?? process.env.ULTRA_REPAIR ?? "1") !== "0" && opts.repair !== false,
86
+ // best-of-N repair: run this many repair attempts, keep the best that verifies (never worse than the winner).
87
+ repairN: num(opts.repairN ?? process.env.ULTRA_REPAIR_N, 2, 1, 5),
88
+ repairAgent: String(opts.repairAgent ?? process.env.ULTRA_REPAIR_AGENT ?? opts.agent ?? process.env.ULTRA_AGENT ?? 'opencode run "{task}"'),
89
+ testCmd: String(opts.test ?? process.env.ULTRA_TEST ?? ""),
78
90
  }
79
91
  }
80
92
 
@@ -163,6 +175,44 @@ async function tournament(C: Cfg, task: string, summaries: string[]): Promise<{
163
175
  return { ranked, conf: ratio(ranked[0]) - ratio(ranked[1]) }
164
176
  }
165
177
 
178
+ // ---- verifier-guided repair (generic; never benchmark-specific) -------------------------
179
+ // After the tournament, critique the winner and run ONE guided repair pass, then keep the
180
+ // repair ONLY when it verifies as better (the repo's own tests if available, else the
181
+ // reasoned verifier). Repair can lift past the best-of-N ceiling, and never makes it worse.
182
+
183
+ async function critique(C: Cfg, task: string, diff: string, log: string): Promise<string> {
184
+ const prompt =
185
+ `A coding task and the patch a verifier selected as the best of several attempts. You are a strict ` +
186
+ `reviewer. In 3-5 sentences name the MOST LIKELY remaining problems: missed edge cases, incomplete ` +
187
+ `coverage, wrong root cause, or regressions it could introduce. If it looks fully correct, say so and ` +
188
+ `name the single thing most worth double-checking. Be concrete and actionable.\n\n` +
189
+ `TASK:\n${task}\n\nPATCH:\n${diff.slice(0, 6000)}\n\nAGENT LOG (tail):\n${(log || "").slice(-1200)}\n\nReview:`
190
+ try { return (await chat(C, prompt)).trim().slice(0, 1500) } catch { return "" }
191
+ }
192
+
193
+ // auto-detect a repo test command when the user did not pass one.
194
+ async function detectTestCmd(dir: string): Promise<string> {
195
+ const read = (f: string) => readFile(join(dir, f), "utf8").catch(() => "")
196
+ const pkg = await read("package.json")
197
+ if (pkg && /"test"\s*:/.test(pkg) && !/no test specified/.test(pkg)) return "npm test --silent"
198
+ if (await read("pytest.ini") || await read("pyproject.toml") || await read("setup.cfg") || await read("tox.ini")) return "python -m pytest -q"
199
+ if (await read("Cargo.toml")) return "cargo test -q"
200
+ if (await read("go.mod")) return "go test ./..."
201
+ return ""
202
+ }
203
+
204
+ // true iff `testCmd` exits 0 in a fresh worktree with `diff` applied.
205
+ async function runTests(dir: string, base: string, work: string, tag: string, diff: string, testCmd: string, timeout: number, abort?: AbortSignal): Promise<boolean> {
206
+ const wt = join(work, `t-${tag}`)
207
+ await git(dir, "worktree", "add", "--detach", wt, base)
208
+ try {
209
+ if (diff.trim()) { const p = join(work, `t-${tag}.patch`); await writeFile(p, diff); await git(wt, "apply", "--3way", p).catch(() => {}) }
210
+ await execFileP("bash", ["-lc", `exec </dev/null; ${testCmd}`], { cwd: wt, env: process.env, timeout, maxBuffer: 32 * 1024 * 1024, signal: abort })
211
+ return true
212
+ } catch { return false }
213
+ finally { await git(dir, "worktree", "remove", "--force", wt).catch(() => {}) }
214
+ }
215
+
166
216
  // ---- lean sandbox for sub-agent attempts ------------------------------------------------
167
217
  // Sub-agents run under XDG_CONFIG_HOME -> a provider-only config so they skip the user's MCP
168
218
  // servers and plugins (the slow part). Deps install once into the sandbox and cache forever.
@@ -204,9 +254,10 @@ export const Ultra: Plugin = async (_input, options) => {
204
254
 
205
255
  const ultra = tool({
206
256
  description:
207
- "Best-of-N with a verifier. Runs a coding/terminal task N times in isolated git worktrees, ranks the " +
208
- "attempts with the same model as a verifier, and applies the winning diff when confident (otherwise " +
209
- "reports the top candidates). Use for a task worth spending compute to get right the first time.",
257
+ "v2: Best-of-N with a verifier + repair. Runs a coding/terminal task N times in isolated git worktrees, " +
258
+ "ranks the attempts with the same model as a verifier, then runs one critique-guided REPAIR pass on the " +
259
+ "winner and keeps it only if it verifies better (the repo's tests, else the verifier). Applies the " +
260
+ "result when confident (otherwise reports the top candidates). For a task worth getting right the first time.",
210
261
  args: {
211
262
  task: tool.schema.string().describe("The task to solve N times and verify. Be specific."),
212
263
  },
@@ -277,25 +328,59 @@ export const Ultra: Plugin = async (_input, options) => {
277
328
  worktrees.push(wt)
278
329
  }
279
330
 
280
- // Run the agent in each worktree IN PARALLEL (lean sandbox keeps this cheap).
331
+ // Adaptive early-exit (#141): N is an upper bound. If an attempt PASSES the
332
+ // repo's tests (a hard, non-regressible signal), take it as the verified winner,
333
+ // abandon the still-running/queued attempts, and skip the tournament + repair.
334
+ // Low verifier confidence never stops early, so the result can never be worse
335
+ // than running all N. Off with earlyExit:false or ULTRA_NO_EARLY_EXIT=1.
336
+ const earlyExit = (eff as any).earlyExit !== false && process.env.ULTRA_NO_EARLY_EXIT !== "1"
337
+ let testCmd = eff.testCmd || (await detectTestCmd(dir))
338
+ const exitTestCmd = earlyExit ? testCmd : ""
339
+ // one combined abort: fires on host cancel (ctx.abort) OR our verified-winner signal.
340
+ const ee = new AbortController()
341
+ try { (ctx.abort as AbortSignal | undefined)?.addEventListener?.("abort", () => ee.abort(), { once: true }) } catch {}
342
+ let verified: { i: number; diff: string; log: string; summary: string } | null = null
343
+
344
+ // Run the agent in each worktree IN PARALLEL (lean sandbox keeps this light).
281
345
  const limit = pLimit(Math.max(1, Math.min(eff.cc, eff.n)))
282
346
  let done = 0
283
347
  status(`ultra: running ${eff.n} attempts in parallel...`)
284
- toast(`running ${eff.n} attempts in parallel`)
348
+ toast(`running ${eff.n} attempts in parallel${exitTestCmd ? `, early-exit on '${exitTestCmd}'` : ""}`)
285
349
  const outs = await Promise.all(worktrees.map((wt, i) => limit(async () => {
350
+ if (ee.signal.aborted) return null // verified winner already found; skip queued attempts
286
351
  const env = await isolatedEnv(String(i))
287
352
  let log = ""
288
353
  try {
289
- const { stdout, stderr } = await execFileP("bash", ["-lc", `exec </dev/null; ${cmd}`], { cwd: wt, env, timeout: eff.agentTimeoutMs, maxBuffer: 32 * 1024 * 1024, signal: ctx.abort })
354
+ const { stdout, stderr } = await execFileP("bash", ["-lc", `exec </dev/null; ${cmd}`], { cwd: wt, env, timeout: eff.agentTimeoutMs, maxBuffer: 32 * 1024 * 1024, signal: ee.signal })
290
355
  log = (stdout || "") + (stderr || "")
291
356
  } catch (e: any) { log = `agent error: ${e?.message || e}` }
292
357
  await git(wt, "add", "-A").catch(() => {})
293
358
  const diff = await git(wt, "diff", "--cached").catch(() => "")
294
359
  done++
295
360
  status(`ultra: ${done}/${eff.n} attempts done`, `attempt ${i}: ${diff ? diff.length + " diff chars" : "no changes"}`)
296
- return { diff, summary: `AGENT LOG (tail):\n${log.slice(-1500)}\n\nDIFF:\n${diff.slice(0, 6000) || "(no changes)"}` }
361
+ const out = { diff, log, summary: `AGENT LOG (tail):\n${log.slice(-1500)}\n\nDIFF:\n${diff.slice(0, 6000) || "(no changes)"}` }
362
+ if (exitTestCmd && !verified && diff.trim()) {
363
+ const pass = await runTests(dir, base, work, `ee-${i}`, diff, exitTestCmd, eff.agentTimeoutMs, ee.signal)
364
+ if (pass && !verified) { verified = { i, ...out }; toast(`attempt ${i} passed tests; abandoning the rest`, "success"); ee.abort() }
365
+ }
366
+ return out
297
367
  })))
298
- for (const o of outs) { diffs.push(o.diff); summaries.push(o.summary) }
368
+ const completed = outs.filter((o): o is { diff: string; log: string; summary: string } => !!o)
369
+ for (const o of completed) { diffs.push(o.diff); summaries.push(o.summary) }
370
+
371
+ // verified fast path: a test-passing attempt is correct by the repo's own
372
+ // criterion, so apply it directly (no tournament, no repair, no regression).
373
+ if (verified && verified.diff.trim()) {
374
+ const patch = join(work, "winner.patch")
375
+ await writeFile(patch, verified.diff)
376
+ try {
377
+ await git(dir, "apply", "--3way", patch)
378
+ toast("applied the verified winner", "success")
379
+ return `🏆 ultra: attempt ${verified.i} passed the repo's tests (${exitTestCmd}); applied it and abandoned the rest. Review it before committing.`
380
+ } catch (e: any) {
381
+ return `ultra: attempt ${verified.i} passed tests but the patch did not apply cleanly (${e?.message || e}). The diff:\n\n\`\`\`diff\n${verified.diff.slice(0, 6000)}\n\`\`\``
382
+ }
383
+ }
299
384
 
300
385
  if (diffs.every((d) => !d.trim())) {
301
386
  return `ultra ran ${eff.n} attempts but none made any changes (all diffs empty). The sub-agent could not act: the task may be too ambiguous, or the sandbox model could not reach its endpoint. Try a more specific task, or check the model endpoint.`
@@ -306,6 +391,73 @@ export const Ultra: Plugin = async (_input, options) => {
306
391
  const { ranked, conf } = await tournament(eff, task, summaries)
307
392
  const best = ranked[0]
308
393
 
394
+ // --- repair: critique the winner, run repairN guided passes, keep the best that verifies -----
395
+ // Best-of-N repair (regression-safe): fan out eff.repairN attempts off `base`, each seeded
396
+ // with the winner's diff and the SAME critique injected (one critique, reused). Keep-policy:
397
+ // * with tests: keep the FIRST repair that PASSES the repo's tests; else keep the winner.
398
+ // (a passing repair is correct by the repo's own criterion, so it cannot regress.)
399
+ // * without tests: keep the FIRST repair the reasoned verifier CLEARLY prefers over the
400
+ // current winner (same >k/2 threshold as before); else keep the winner.
401
+ // INVARIANT: the final applied result is never worse than the tournament winner.
402
+ if (eff.repair && diffs[best].trim()) {
403
+ status(`ultra: repair, ${eff.repairN} critique-guided pass(es) on the winner...`)
404
+ toast(`repair: ${eff.repairN} pass(es) on the winner`)
405
+ const crit = await critique(eff, task, diffs[best], completed[best].log)
406
+ const rtask =
407
+ `${task}\n\n--- A previous attempt (already applied to your working tree) produced a partial ` +
408
+ `solution. A reviewer flagged the issues below. Improve and COMPLETE it, keeping what is correct; ` +
409
+ `verify before finishing. ---\nREVIEWER NOTES:\n${crit}`
410
+ const rcmd = eff.repairAgent.replace("{task}", rtask.replace(/"/g, '\\"'))
411
+ // run one seeded repair attempt in its own worktree; returns its diff + summary.
412
+ const runRepair = async (r: number): Promise<{ diff: string; summary: string }> => {
413
+ const rwt = join(work, `repair-${r}`)
414
+ await git(dir, "worktree", "add", "--detach", rwt, base)
415
+ worktrees.push(rwt)
416
+ const wpatch = join(work, `winner-for-repair-${r}.patch`)
417
+ await writeFile(wpatch, diffs[best])
418
+ await git(rwt, "apply", "--3way", wpatch).catch(() => {})
419
+ const renv = await isolatedEnv(`repair-${r}`)
420
+ let rlog = ""
421
+ try {
422
+ const { stdout, stderr } = await execFileP("bash", ["-lc", `exec </dev/null; ${rcmd}`], { cwd: rwt, env: renv, timeout: eff.agentTimeoutMs, maxBuffer: 32 * 1024 * 1024, signal: ctx.abort })
423
+ rlog = (stdout || "") + (stderr || "")
424
+ } catch (e: any) { rlog = `agent error: ${e?.message || e}` }
425
+ await git(rwt, "add", "-A").catch(() => {})
426
+ const rdiff = await git(rwt, "diff", "--cached").catch(() => "")
427
+ const rsum = `AGENT LOG (tail):\n${rlog.slice(-1500)}\n\nDIFF:\n${rdiff.slice(0, 6000) || "(no changes)"}`
428
+ return { diff: rdiff, summary: rsum }
429
+ }
430
+ // run the repairN attempts in parallel, honoring the concurrency limiter (cc).
431
+ const rlimit = pLimit(eff.cc)
432
+ const reps = await Promise.all(Array.from({ length: eff.repairN }, (_, r) => rlimit(() => runRepair(r))))
433
+ // only real, changed candidates are eligible.
434
+ const cands = reps.filter((x) => x.diff.trim() && x.diff !== diffs[best])
435
+ let kept = false, why = ""
436
+ if (!cands.length) { status("ultra: repair produced no new change; winner kept") }
437
+ else if (testCmd) {
438
+ // regression-safe: keep the FIRST repair that PASSES the repo's own tests.
439
+ status(`ultra: repair keep-check (tests: ${testCmd}) on ${cands.length} candidate(s)...`)
440
+ for (let r = 0; r < cands.length; r++) {
441
+ const pass = await runTests(dir, base, work, `rep-${r}`, cands[r].diff, testCmd, eff.agentTimeoutMs, ctx.abort)
442
+ if (pass) { diffs[best] = cands[r].diff; summaries[best] = cands[r].summary; kept = true; why = `repair ${r} passes the tests`; break }
443
+ }
444
+ if (!kept) why = "no repair passed the tests"
445
+ } else {
446
+ // no tests: keep the FIRST repair the verifier CLEARLY prefers over the current winner.
447
+ const preferRepair = async (rsum: string) => {
448
+ const lim = pLimit(eff.cc)
449
+ const votes = await Promise.all(Array.from({ length: eff.k }, () => judge(eff, lim, task, summaries[best], rsum)))
450
+ return votes.filter((v) => v === "B").length > eff.k / 2 // B = repaired
451
+ }
452
+ for (let r = 0; r < cands.length; r++) {
453
+ if (await preferRepair(cands[r].summary)) { diffs[best] = cands[r].diff; summaries[best] = cands[r].summary; kept = true; why = `verifier prefers repair ${r}`; break }
454
+ }
455
+ if (!kept) why = "verifier keeps winner"
456
+ }
457
+ if (kept) { toast(`repair kept (${why})`, "success"); status(`ultra: repair kept (${why})`) }
458
+ else if (cands.length) status(`ultra: all repairs discarded (${why})`)
459
+ }
460
+
309
461
  // Normalised added-lines of a diff, so we can tell when attempts AGREE on the same change.
310
462
  const nonEmpty = diffs.filter((d) => d.trim()).length
311
463
  // Apply the verifier's TOP pick when it is confident, OR when a majority of attempts
@@ -345,7 +497,7 @@ export const Ultra: Plugin = async (_input, options) => {
345
497
  config: async (cfg: any) => {
346
498
  cfg.command = {
347
499
  ultra: {
348
- description: "Best-of-N + verifier: run the task N times in isolated worktrees, apply the best. /ultra <task>",
500
+ description: "[v2] Best-of-N + verifier + repair: runs the task N times in isolated worktrees, verifies, then repairs the winner toward best-of-N. /ultra <task>",
349
501
  template:
350
502
  "Call the `ultra` tool exactly once, with `task` set to the request below. It runs the task several " +
351
503
  "times in isolated git worktrees and verifies the results. When it returns, report to the user exactly " +
@@ -357,7 +509,7 @@ export const Ultra: Plugin = async (_input, options) => {
357
509
  // routes each request through best-of-N. Sub-attempts run the default agent, so no recursion.
358
510
  cfg.agent = {
359
511
  ultra: {
360
- description: "Best-of-N mode: run the request N times in isolated worktrees and apply the verified winner.",
512
+ description: "[v2] Best-of-N + repair mode: run the request N times in isolated worktrees, verify, repair, then apply the verified winner.",
361
513
  mode: "primary",
362
514
  color: "#A855F7",
363
515
  prompt: