(task)
| 106 | } |
| 107 | |
| 108 | async function runTask(task) { |
| 109 | const work = await fs.mkdtemp(path.join(os.tmpdir(), `qodex-eval-${task.id}-`)); |
| 110 | try { |
| 111 | for (const [rel, content] of Object.entries(task.setup?.files ?? {})) { |
| 112 | const abs = path.join(work, rel); |
| 113 | await fs.mkdir(path.dirname(abs), { recursive: true }); |
| 114 | await fs.writeFile(abs, content, 'utf-8'); |
| 115 | } |
| 116 | |
| 117 | // A TypeScript task needs tsc reachable via `npx --no-install tsc` (what both the |
| 118 | // auto-verify gate and the check command run). Real projects have it in node_modules; |
| 119 | // our throwaway workspace doesn't, so symlink this repo's node_modules in. Contained |
| 120 | // to the harness — product code is untouched. |
| 121 | if (fsSync.existsSync(path.join(work, 'tsconfig.json'))) { |
| 122 | try { |
| 123 | await fs.symlink(path.join(repoRoot, 'node_modules'), path.join(work, 'node_modules'), 'dir'); |
| 124 | } catch { /* best-effort */ } |
| 125 | } |
| 126 | |
| 127 | const started = Date.now(); |
| 128 | const run = await runAgent(task.prompt, work); |
| 129 | const wallMs = Date.now() - started; |
| 130 | const tele = parseAgentEvents(run.stdout); |
| 131 | |
| 132 | const outcome = await observeOutcome(task, work, tele.error); |
| 133 | const verdict = evaluateChecks(task.check, outcome); |
| 134 | |
| 135 | process.stdout.write(` ${verdict.passed ? '✅' : '❌'} ${task.id} (${tele.iterations} iters, ${tele.toolCalls} tools, ${(wallMs / 1000).toFixed(1)}s)\n`); |
| 136 | if (!verdict.passed) for (const r of verdict.reasons) process.stdout.write(` · ${r}\n`); |
| 137 | |
| 138 | return { |
| 139 | id: task.id, |
| 140 | passed: verdict.passed, |
| 141 | reasons: verdict.reasons, |
| 142 | iterations: tele.iterations, |
| 143 | toolCalls: tele.toolCalls, |
| 144 | costUsd: tele.costUsd, |
| 145 | wallMs, |
| 146 | error: tele.error, |
| 147 | }; |
| 148 | } finally { |
| 149 | await fs.rm(work, { recursive: true, force: true }).catch(() => {}); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | async function main() { |
| 154 | const filter = process.argv.slice(2); |
no test coverage detected