@castlemilk/omega 0.6.14 → 0.6.16

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 (51) hide show
  1. package/dist/cli.js +803 -38
  2. package/dist/server/dist/app.js +2 -2
  3. package/dist/server/dist/app.js.map +1 -1
  4. package/dist/server/dist/routes/benchmarks.d.ts +4 -0
  5. package/dist/server/dist/routes/benchmarks.d.ts.map +1 -0
  6. package/dist/server/dist/routes/benchmarks.js +131 -0
  7. package/dist/server/dist/routes/benchmarks.js.map +1 -0
  8. package/dist/server/dist/routes/prompt-versions.d.ts.map +1 -1
  9. package/dist/server/dist/routes/prompt-versions.js +15 -19
  10. package/dist/server/dist/routes/prompt-versions.js.map +1 -1
  11. package/dist/server/node_modules/@grpc/proto-loader/build/bin/proto-loader-gen-types.js +0 -0
  12. package/dist/server/node_modules/@omega/agent/dist/index.d.ts +1 -1
  13. package/dist/server/node_modules/@omega/agent/dist/index.d.ts.map +1 -1
  14. package/dist/server/node_modules/@omega/agent/dist/index.js +1 -1
  15. package/dist/server/node_modules/@omega/agent/dist/index.js.map +1 -1
  16. package/dist/server/node_modules/@omega/agent/dist/prompts.d.ts +1 -1
  17. package/dist/server/node_modules/@omega/agent/dist/prompts.d.ts.map +1 -1
  18. package/dist/server/node_modules/@omega/agent/dist/prompts.js +23 -13
  19. package/dist/server/node_modules/@omega/agent/dist/prompts.js.map +1 -1
  20. package/dist/server/node_modules/@omega/db/generated/client/libquery_engine-darwin-arm64.dylib.node +0 -0
  21. package/dist/server/node_modules/mime/cli.js +0 -0
  22. package/dist/server/node_modules/mime/src/build.js +0 -0
  23. package/dist/server/node_modules/wrap-ansi/index.js +0 -0
  24. package/dist/server/pglite-data/base/5/1247 +0 -0
  25. package/dist/server/pglite-data/base/5/1249 +0 -0
  26. package/dist/server/pglite-data/base/5/1259 +0 -0
  27. package/dist/server/pglite-data/base/5/16394 +0 -0
  28. package/dist/server/pglite-data/base/5/16404 +0 -0
  29. package/dist/server/pglite-data/base/5/16406 +0 -0
  30. package/dist/server/pglite-data/base/5/16421 +0 -0
  31. package/dist/server/pglite-data/base/5/16510 +0 -0
  32. package/dist/server/pglite-data/base/5/2604 +0 -0
  33. package/dist/server/pglite-data/base/5/2606 +0 -0
  34. package/dist/server/pglite-data/base/5/2610 +0 -0
  35. package/dist/server/pglite-data/base/5/2620 +0 -0
  36. package/dist/server/pglite-data/base/5/2662 +0 -0
  37. package/dist/server/pglite-data/base/5/2663 +0 -0
  38. package/dist/server/pglite-data/base/5/pg_internal.init +0 -0
  39. package/dist/server/pglite-data/global/pg_control +0 -0
  40. package/dist/server/pglite-data/global/pg_internal.init +0 -0
  41. package/dist/server/pglite-data/pg_wal/000000010000000000000001 +0 -0
  42. package/dist/server/pglite-data/pg_xact/0000 +0 -0
  43. package/dist/server/pglite-data/postmaster.pid +1 -1
  44. package/dist/server/src/app.ts +2 -2
  45. package/dist/server/src/routes/benchmarks.ts +152 -0
  46. package/dist/server/src/routes/prompt-versions.ts +15 -20
  47. package/dist/web/dist/assets/index-B_XBuJZF.js +40 -0
  48. package/dist/web/dist/assets/index-CL9S3Jj5.js +40 -0
  49. package/dist/web/dist/assets/index-bU-GzLxD.css +1 -0
  50. package/dist/web/dist/index.html +2 -2
  51. package/package.json +14 -13
package/dist/cli.js CHANGED
@@ -330,8 +330,8 @@ function getApiUrl() {
330
330
  const opts = program.opts();
331
331
  return opts.api ?? "http://localhost:4000";
332
332
  }
333
- async function apiFetch(path10, init) {
334
- const url = `${getApiUrl()}${path10}`;
333
+ async function apiFetch(path11, init) {
334
+ const url = `${getApiUrl()}${path11}`;
335
335
  const res = await fetch(url, init);
336
336
  const data = await res.json().catch(() => ({}));
337
337
  if (!res.ok) {
@@ -734,8 +734,8 @@ function getOutputDir() {
734
734
  }
735
735
  try {
736
736
  const __filename3 = fileURLToPath3(import.meta.url);
737
- const __dirname3 = path3.dirname(__filename3);
738
- return path3.join(path3.resolve(__dirname3, "../../../.."), "packages/skills/src");
737
+ const __dirname4 = path3.dirname(__filename3);
738
+ return path3.join(path3.resolve(__dirname4, "../../../.."), "packages/skills/src");
739
739
  } catch {
740
740
  return path3.join(process.cwd(), "harness-skills");
741
741
  }
@@ -819,7 +819,8 @@ agentCmd.command("diffs").description("Show diffs for a task").argument("<id>",
819
819
 
820
820
  // ../../apps/cli/src/commands/bench.ts
821
821
  import { Command as Command8 } from "commander";
822
- import path9 from "node:path";
822
+ import fs8 from "node:fs/promises";
823
+ import path10 from "node:path";
823
824
 
824
825
  // ../bench/dist/api-client.js
825
826
  var ApiError = class extends Error {
@@ -836,15 +837,15 @@ async function apiFetch2(url, init) {
836
837
  }
837
838
  return res.json();
838
839
  }
839
- async function ensureProject2(apiUrl, name, path10) {
840
+ async function ensureProject2(apiUrl, name, path11) {
840
841
  const res = await fetch(`${apiUrl}/projects`, {
841
842
  method: "POST",
842
843
  headers: { "Content-Type": "application/json" },
843
- body: JSON.stringify({ name, path: path10 })
844
+ body: JSON.stringify({ name, path: path11 })
844
845
  });
845
846
  if (res.status === 409) {
846
- const projects = await fetch(`${apiUrl}/projects?path=${encodeURIComponent(path10)}`).then((r) => r.json());
847
- const existing = projects.find((p) => p.path === path10);
847
+ const projects = await fetch(`${apiUrl}/projects?path=${encodeURIComponent(path11)}`).then((r) => r.json());
848
+ const existing = projects.find((p) => p.path === path11);
848
849
  if (existing)
849
850
  return existing;
850
851
  }
@@ -1049,7 +1050,7 @@ async function writeReport(report, outputDir = ".omega/reports") {
1049
1050
  const jsonFile = path5.join(outputDir, `benchmark-${ts}.json`);
1050
1051
  const mdFile = path5.join(outputDir, `benchmark-${ts}.md`);
1051
1052
  await fs2.writeFile(jsonFile, JSON.stringify(report, null, 2), "utf-8");
1052
- const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1053
+ const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1053
1054
  const md = [
1054
1055
  "# Omega Benchmark Report",
1055
1056
  "",
@@ -1059,7 +1060,7 @@ async function writeReport(report, outputDir = ".omega/reports") {
1059
1060
  `- Passed: ${String(report.passed)}`,
1060
1061
  `- Failed: ${String(report.failed)}`,
1061
1062
  `- Timeouts: ${String(report.timeouts)}`,
1062
- `- Pass rate: ${String(passRate)}%`,
1063
+ `- Pass rate: ${String(passRate2)}%`,
1063
1064
  `- Total duration: ${formatDuration(report.totalDurationMs)}`,
1064
1065
  "",
1065
1066
  "## Results",
@@ -1077,11 +1078,11 @@ async function writeReport(report, outputDir = ".omega/reports") {
1077
1078
  return jsonFile;
1078
1079
  }
1079
1080
  function printSummary(report) {
1080
- const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1081
+ const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1081
1082
  console.log(`
1082
1083
  Benchmark: ${report.suite}`);
1083
1084
  console.log(`Total: ${String(report.total)} | Passed: ${String(report.passed)} | Failed: ${String(report.failed)} | Timeouts: ${String(report.timeouts)}`);
1084
- console.log(`Pass rate: ${String(passRate)}%`);
1085
+ console.log(`Pass rate: ${String(passRate2)}%`);
1085
1086
  console.log(`Duration: ${formatDuration(report.totalDurationMs)}`);
1086
1087
  for (const r of report.results) {
1087
1088
  console.log(resultLine(r, report.results.indexOf(r)));
@@ -1127,6 +1128,28 @@ function basePackage() {
1127
1128
  }
1128
1129
  };
1129
1130
  }
1131
+ function simpleTask(id, name, title, description, files, complexity = "simple") {
1132
+ return {
1133
+ id,
1134
+ name,
1135
+ title,
1136
+ description,
1137
+ complexity,
1138
+ setup: async (projectPath) => {
1139
+ await writeJson(projectPath, "package.json", basePackage());
1140
+ for (const [file, content] of Object.entries(files)) {
1141
+ await writeFile2(projectPath, file, content);
1142
+ }
1143
+ if (!files["lint.js"])
1144
+ await writeFile2(projectPath, "lint.js", `console.log('lint ok');
1145
+ `);
1146
+ if (!files["build.js"])
1147
+ await writeFile2(projectPath, "build.js", `console.log('build ok');
1148
+ `);
1149
+ },
1150
+ evaluate: (ctx) => Promise.resolve(validate(ctx))
1151
+ };
1152
+ }
1130
1153
  var TASKS = [
1131
1154
  {
1132
1155
  id: "greet-function",
@@ -1219,6 +1242,257 @@ console.log('constant test passed');
1219
1242
  },
1220
1243
  evaluate: (ctx) => Promise.resolve(validate(ctx))
1221
1244
  },
1245
+ simpleTask("sum-function", "Add a sum function", "Add math.js with a sum function", "Create math.js that exports sum(a, b) returning the numeric sum of the two arguments.", {
1246
+ "test.js": `import { sum } from './math.js';
1247
+ import assert from 'node:assert';
1248
+ assert.strictEqual(sum(2, 3), 5);
1249
+ assert.strictEqual(sum(-1, 1), 0);
1250
+ console.log('sum test passed');
1251
+ `
1252
+ }),
1253
+ simpleTask("reverse-string", "Add reverse string function", "Add string.js with reverse function", "Create string.js that exports reverse(str) returning the input string reversed.", {
1254
+ "test.js": `import { reverse } from './string.js';
1255
+ import assert from 'node:assert';
1256
+ assert.strictEqual(reverse('abc'), 'cba');
1257
+ assert.strictEqual(reverse('Hello'), 'olleH');
1258
+ console.log('reverse test passed');
1259
+ `
1260
+ }),
1261
+ simpleTask("is-even", "Add isEven helper", "Add helpers.js with isEven function", "Create helpers.js that exports isEven(n) returning true when n is divisible by 2.", {
1262
+ "test.js": `import { isEven } from './helpers.js';
1263
+ import assert from 'node:assert';
1264
+ assert.strictEqual(isEven(4), true);
1265
+ assert.strictEqual(isEven(3), false);
1266
+ console.log('isEven test passed');
1267
+ `
1268
+ }),
1269
+ simpleTask("uppercase-first", "Uppercase first letter", "Add capitalize.js with capitalize function", "Create capitalize.js that exports capitalize(str) returning the string with the first character upper-cased.", {
1270
+ "test.js": `import { capitalize } from './capitalize.js';
1271
+ import assert from 'node:assert';
1272
+ assert.strictEqual(capitalize('hello'), 'Hello');
1273
+ assert.strictEqual(capitalize(''), '');
1274
+ console.log('capitalize test passed');
1275
+ `
1276
+ }),
1277
+ simpleTask("remove-console-log", "Remove debug console.log", "Clean up debug logging", "Remove the console.log call from src/main.js so the linter passes. Do not change the exported function behavior.", {
1278
+ "src/main.js": `export function run() { console.log('debug'); return 'ok'; }
1279
+ `,
1280
+ "test.js": `import { run } from './src/main.js';
1281
+ import assert from 'node:assert';
1282
+ assert.strictEqual(run(), 'ok');
1283
+ console.log('run test passed');
1284
+ `,
1285
+ "lint.js": `import fs from 'node:fs/promises';
1286
+ const code = await fs.readFile('./src/main.js', 'utf-8');
1287
+ if (code.includes('console.log')) { console.error('lint failed: console.log found'); process.exit(1); }
1288
+ console.log('lint ok');
1289
+ `
1290
+ }),
1291
+ simpleTask("fix-indentation", "Fix indentation", "Normalize indentation in src/code.js", "Convert the mixed tabs/spaces indentation in src/code.js to two spaces so the lint script passes. Keep behavior.", {
1292
+ "src/code.js": `export function add(a, b) {
1293
+ return a + b;
1294
+ }
1295
+ `,
1296
+ "test.js": `import { add } from './src/code.js';
1297
+ import assert from 'node:assert';
1298
+ assert.strictEqual(add(1, 2), 3);
1299
+ console.log('add test passed');
1300
+ `,
1301
+ "lint.js": `import fs from 'node:fs/promises';
1302
+ const code = await fs.readFile('./src/code.js', 'utf-8');
1303
+ if (code.includes('\\t')) { console.error('lint failed: tabs found'); process.exit(1); }
1304
+ console.log('lint ok');
1305
+ `
1306
+ }),
1307
+ simpleTask("arrow-function", "Convert to arrow function", "Refactor src/math.js to use arrow function", "Rewrite src/math.js so double is defined as a const arrow function. Do not change behavior.", {
1308
+ "src/math.js": `export function double(n) { return n * 2; }
1309
+ `,
1310
+ "test.js": `import fs from 'node:fs/promises';
1311
+ import { double } from './src/math.js';
1312
+ import assert from 'node:assert';
1313
+ const code = await fs.readFile('./src/math.js', 'utf-8');
1314
+ assert.strictEqual(double(5), 10);
1315
+ if (!code.includes('=>')) { console.error('lint failed: not arrow function'); process.exit(1); }
1316
+ console.log('arrow test passed');
1317
+ `
1318
+ }),
1319
+ simpleTask("default-export", "Add a default export", "Make greet the default export", "Update greet.js so the greet function is the default export instead of a named export. Update the test accordingly.", {
1320
+ "greet.js": `export function greet(name) { return 'Hello, ' + name + '!'; }
1321
+ `,
1322
+ "test.js": `import greet from './greet.js';
1323
+ import assert from 'node:assert';
1324
+ assert.strictEqual(greet('World'), 'Hello, World!');
1325
+ console.log('default export test passed');
1326
+ `
1327
+ }),
1328
+ simpleTask("update-env", "Update environment variable default", "Set PORT in .env", "Create or update .env so it contains PORT=3000.", {
1329
+ "test.js": `import { readFile } from 'node:fs/promises';
1330
+ const env = await readFile('./.env', 'utf-8');
1331
+ if (!env.includes('PORT=3000')) { console.error('PORT=3000 not found'); process.exit(1); }
1332
+ console.log('env test passed');
1333
+ `
1334
+ }),
1335
+ simpleTask("add-package-script", "Add package script", "Add a format script to package.json", "Edit package.json to add a format script with value prettier --write .. Do not remove existing scripts.", {
1336
+ "test.js": `import { readFile } from 'node:fs/promises';
1337
+ const pkg = JSON.parse(await readFile('./package.json', 'utf-8'));
1338
+ if (pkg.scripts?.format !== 'prettier --write .') { console.error('format script missing'); process.exit(1); }
1339
+ console.log('package script test passed');
1340
+ `
1341
+ }),
1342
+ simpleTask("update-readme", "Update README heading", "Fix the README title", "Update README.md so the first line is # Omega Synthetic Bench instead of # Old Title.", {
1343
+ "README.md": `# Old Title
1344
+
1345
+ This is a sample project.
1346
+ `,
1347
+ "test.js": `import { readFile } from 'node:fs/promises';
1348
+ const readme = await readFile('./README.md', 'utf-8');
1349
+ if (!readme.startsWith('# Omega Synthetic Bench')) { console.error('README heading wrong'); process.exit(1); }
1350
+ console.log('readme test passed');
1351
+ `
1352
+ }),
1353
+ simpleTask("rename-variable", "Rename a variable", "Rename x to value", "In src/calc.js, rename the parameter x to value everywhere. Do not change behavior.", {
1354
+ "src/calc.js": `export function calc(x) { return x * 2; }
1355
+ `,
1356
+ "test.js": `import { calc } from './src/calc.js';
1357
+ import assert from 'node:assert';
1358
+ assert.strictEqual(calc(4), 8);
1359
+ console.log('rename test passed');
1360
+ `,
1361
+ "lint.js": `import fs from 'node:fs/promises';
1362
+ const code = await fs.readFile('./src/calc.js', 'utf-8');
1363
+ if (code.includes('(x)') || code.includes(' x ')) { console.error('old variable name found'); process.exit(1); }
1364
+ console.log('lint ok');
1365
+ `
1366
+ }),
1367
+ simpleTask("add-input-validation", "Add input validation", "Validate input in divide function", "Update src/divide.js so divide(a, b) throws an error when b is 0.", {
1368
+ "src/divide.js": `export function divide(a, b) { return a / b; }
1369
+ `,
1370
+ "test.js": `import { divide } from './src/divide.js';
1371
+ import assert from 'node:assert';
1372
+ assert.strictEqual(divide(10, 2), 5);
1373
+ try { divide(1, 0); console.error('should throw'); process.exit(1); } catch { }
1374
+ console.log('divide test passed');
1375
+ `
1376
+ }),
1377
+ simpleTask("handle-empty-array", "Handle empty array", "Make sumAll handle empty arrays", "Update src/sumAll.js so sumAll([]) returns 0 instead of throwing.", {
1378
+ "src/sumAll.js": `export function sumAll(numbers) { return numbers.reduce((a, b) => a + b); }
1379
+ `,
1380
+ "test.js": `import { sumAll } from './src/sumAll.js';
1381
+ import assert from 'node:assert';
1382
+ assert.strictEqual(sumAll([1, 2, 3]), 6);
1383
+ assert.strictEqual(sumAll([]), 0);
1384
+ console.log('sumAll test passed');
1385
+ `
1386
+ }),
1387
+ simpleTask("add-unit-test", "Add a unit test", "Write a test for multiply", "Create src/multiply.js exporting multiply(a, b). Then create test.js that asserts multiply(3, 4) === 12.", {
1388
+ "src/multiply.js": `export function multiply(a, b) { return a * b; }
1389
+ `,
1390
+ "test.js": `// replace with proper tests
1391
+ `,
1392
+ "lint.js": `import fs from 'node:fs/promises';
1393
+ const test = await fs.readFile('./test.js', 'utf-8');
1394
+ if (!test.includes('multiply')) { console.error('test does not cover multiply'); process.exit(1); }
1395
+ console.log('lint ok');
1396
+ `
1397
+ }),
1398
+ simpleTask("create-module-index", "Create module index", "Export all helpers from index.js", "Create src/index.js that re-exports foo from ./foo.js and bar from ./bar.js.", {
1399
+ "src/foo.js": `export const foo = 'foo';
1400
+ `,
1401
+ "src/bar.js": `export const bar = 'bar';
1402
+ `,
1403
+ "test.js": `import { foo, bar } from './src/index.js';
1404
+ import assert from 'node:assert';
1405
+ assert.strictEqual(foo, 'foo');
1406
+ assert.strictEqual(bar, 'bar');
1407
+ console.log('index test passed');
1408
+ `
1409
+ }),
1410
+ simpleTask("add-async-function", "Add async fetch wrapper", "Create fetchText function", "Create src/fetch.js exporting fetchText(url) that returns a Promise resolving to the string fetched: <url>.", {
1411
+ "test.js": `import { fetchText } from './src/fetch.js';
1412
+ import assert from 'node:assert';
1413
+ const result = await fetchText('https://example.com');
1414
+ assert.strictEqual(result, 'fetched: https://example.com');
1415
+ console.log('fetch test passed');
1416
+ `
1417
+ }),
1418
+ simpleTask("add-try-catch", "Wrap parsing in try/catch", "Make safeParse not throw", "Update src/parse.js so safeParse(str) returns null when JSON parsing fails instead of throwing.", {
1419
+ "src/parse.js": `export function safeParse(str) { return JSON.parse(str); }
1420
+ `,
1421
+ "test.js": `import { safeParse } from './src/parse.js';
1422
+ import assert from 'node:assert';
1423
+ assert.deepStrictEqual(safeParse('{"a":1}'), { a: 1 });
1424
+ assert.strictEqual(safeParse('not json'), null);
1425
+ console.log('safeParse test passed');
1426
+ `
1427
+ }),
1428
+ simpleTask("timeout-promise", "Add delay helper", "Create sleep(ms) helper", "Create src/time.js exporting sleep(ms) that returns a Promise resolving after ms milliseconds.", {
1429
+ "test.js": `import { sleep } from './src/time.js';
1430
+ const start = Date.now();
1431
+ await sleep(50);
1432
+ if (Date.now() - start < 40) { console.error('sleep too short'); process.exit(1); }
1433
+ console.log('sleep test passed');
1434
+ `
1435
+ }),
1436
+ simpleTask("read-test-path", "Create file at path from test", "Create lib/helpers.js with capitalize function", "Create a file that exports a capitalize(str) function. Read the existing test to find the exact import path and expected output.", {
1437
+ "test.js": `import { capitalize } from './lib/helpers.js';
1438
+ import assert from 'node:assert';
1439
+ assert.strictEqual(capitalize('hello'), 'Hello');
1440
+ console.log('capitalize test passed');
1441
+ `
1442
+ }, "medium"),
1443
+ simpleTask("add-cli-args-parser", "Add CLI argument parser", "Create parseArgs function", "Create src/args.js exporting parseArgs(argv) that returns an object from --key value pairs.", {
1444
+ "test.js": `import { parseArgs } from './src/args.js';
1445
+ import assert from 'node:assert';
1446
+ assert.deepStrictEqual(parseArgs(['--name', 'x', '--count', '2']), { name: 'x', count: '2' });
1447
+ console.log('args test passed');
1448
+ `
1449
+ }, "medium"),
1450
+ simpleTask("create-middleware", "Create logging middleware", "Add logger middleware", "Create src/middleware.js exporting logger(req, next) that calls next(), increments req.logCount and returns the result.", {
1451
+ "test.js": `import { logger } from './src/middleware.js';
1452
+ import assert from 'node:assert';
1453
+ const req = { logCount: 0 };
1454
+ const result = logger(req, () => 'done');
1455
+ assert.strictEqual(result, 'done');
1456
+ assert.strictEqual(req.logCount, 1);
1457
+ console.log('middleware test passed');
1458
+ `
1459
+ }, "medium"),
1460
+ simpleTask("add-throttle", "Add throttle helper", "Create throttle function", "Create src/throttle.js exporting throttle(fn, ms) that returns a throttled function (only calls fn at most once per ms).", {
1461
+ "test.js": `import { throttle } from './src/throttle.js';
1462
+ let calls = 0;
1463
+ const fn = throttle(() => calls++, 100);
1464
+ fn(); fn(); fn();
1465
+ assert.strictEqual(calls, 1);
1466
+ console.log('throttle test passed');
1467
+ `
1468
+ }, "medium"),
1469
+ simpleTask("memoize-function", "Add memoize helper", "Create memoize function", "Create src/memoize.js exporting memoize(fn) that caches results by first argument.", {
1470
+ "test.js": `import { memoize } from './src/memoize.js';
1471
+ let calls = 0;
1472
+ const fn = memoize((n) => { calls++; return n * 2; });
1473
+ assert.strictEqual(fn(3), 6);
1474
+ assert.strictEqual(fn(3), 6);
1475
+ assert.strictEqual(calls, 1);
1476
+ console.log('memoize test passed');
1477
+ `
1478
+ }, "medium"),
1479
+ simpleTask("fix-race-condition", "Fix a race condition", "Make counter increment atomic", "Update src/counter.js so increment() returns a Promise and uses a local variable correctly. Keep the async interface.", {
1480
+ "src/counter.js": `let count = 0;
1481
+ export async function increment() {
1482
+ const current = count;
1483
+ await Promise.resolve();
1484
+ count = current + 1;
1485
+ return count;
1486
+ }
1487
+ `,
1488
+ "test.js": `import { increment } from './src/counter.js';
1489
+ import assert from 'node:assert';
1490
+ await Promise.all([increment(), increment(), increment()]);
1491
+ const final = await increment();
1492
+ assert.strictEqual(final, 4);
1493
+ console.log('counter test passed');
1494
+ `
1495
+ }, "medium"),
1222
1496
  {
1223
1497
  id: "noop-validation",
1224
1498
  name: "Run validation on a clean project",
@@ -1289,10 +1563,10 @@ async function readTask(taskDir) {
1289
1563
  const instructionRaw = await fs4.readFile(path7.join(taskDir, "instruction.md"), "utf-8");
1290
1564
  return { toml: parseToml(tomlRaw), instruction: instructionRaw };
1291
1565
  }
1292
- async function cloneRepo(repoUrl, commit, targetPath) {
1566
+ async function cloneRepo(repoUrl, commit2, targetPath) {
1293
1567
  await fs4.mkdir(path7.dirname(targetPath), { recursive: true });
1294
1568
  await execFileAsync("git", ["clone", repoUrl, targetPath], { timeout: 12e4 });
1295
- await execFileAsync("git", ["-C", targetPath, "checkout", commit], { timeout: 6e4 });
1569
+ await execFileAsync("git", ["-C", targetPath, "checkout", commit2], { timeout: 6e4 });
1296
1570
  }
1297
1571
  async function loadDeepSWESuite(options) {
1298
1572
  const entries = await fs4.readdir(options.tasksDir, { withFileTypes: true });
@@ -1321,7 +1595,7 @@ async function loadDeepSWESuite(options) {
1321
1595
  const id = toml.metadata?.task_id ?? path7.basename(dir);
1322
1596
  const title = toml.metadata?.display_title ?? toml.metadata?.original_title ?? id;
1323
1597
  const repo = toml.metadata?.repository_url;
1324
- const commit = toml.metadata?.base_commit_hash;
1598
+ const commit2 = toml.metadata?.base_commit_hash;
1325
1599
  tasks.push({
1326
1600
  id: `deepswe-${id}`,
1327
1601
  name: id,
@@ -1329,10 +1603,10 @@ async function loadDeepSWESuite(options) {
1329
1603
  description: instruction,
1330
1604
  complexity: "complex",
1331
1605
  setup: async (projectPath) => {
1332
- if (!repo || !commit) {
1606
+ if (!repo || !commit2) {
1333
1607
  throw new Error(`DeepSWE task ${id} is missing repository_url or base_commit_hash`);
1334
1608
  }
1335
- await cloneRepo(repo, commit, projectPath);
1609
+ await cloneRepo(repo, commit2, projectPath);
1336
1610
  },
1337
1611
  evaluate: (ctx) => {
1338
1612
  const hasDiff = ctx.diffs.length > 0 && ctx.diffs.some((d) => d.patch.trim().length > 0);
@@ -1366,12 +1640,12 @@ function summariseTraceFlow(report, maxLines = 60) {
1366
1640
  return lines.slice(0, maxLines).join("\n") + "\n... (truncated)";
1367
1641
  }
1368
1642
  function buildOptimisePrompt(report, failedResult, traceFlowText) {
1369
- const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1643
+ const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1370
1644
  const failed = report.results.filter((r) => !r.evaluation.passed);
1371
1645
  const lines = [];
1372
1646
  lines.push("# Prompt optimisation task");
1373
1647
  lines.push("");
1374
- lines.push(`The latest benchmark run achieved ${String(passRate)}% pass rate (${String(report.passed)}/${String(report.total)}).`);
1648
+ lines.push(`The latest benchmark run achieved ${String(passRate2)}% pass rate (${String(report.passed)}/${String(report.total)}).`);
1375
1649
  lines.push("");
1376
1650
  lines.push("## Failed tasks");
1377
1651
  for (const r of failed.slice(0, 5)) {
@@ -1392,11 +1666,14 @@ function buildOptimisePrompt(report, failedResult, traceFlowText) {
1392
1666
  lines.push("");
1393
1667
  }
1394
1668
  lines.push("## Instructions");
1395
- lines.push("Analyse the failures above and edit `packages/agent/src/prompts.ts` (and optionally `packages/agent/src/planner.ts`) to improve the Omega agent.");
1669
+ lines.push("Analyse the failures above and edit ONLY `packages/agent/src/prompts.ts` to improve the Omega agent.");
1670
+ lines.push("Your goal is to change the system prompt text so the agent avoids the observed failures.");
1396
1671
  lines.push("Rules:");
1397
- lines.push("- Keep changes minimal and targeted.");
1672
+ lines.push("- ONLY edit `packages/agent/src/prompts.ts`. Do NOT edit any other files, including schema files, migrations, or unrelated code.");
1673
+ lines.push("- Keep changes minimal and targeted to the failure pattern.");
1674
+ lines.push("- Use edit_file with an exact old_string from the file. Read the file first if needed.");
1398
1675
  lines.push("- Do not break existing tool signatures or system behaviour.");
1399
- lines.push("- Run `pnpm lint` and `pnpm test` after edits.");
1676
+ lines.push("- After editing, run `pnpm lint` and `pnpm test` to verify.");
1400
1677
  lines.push("- Finish with a summary of what changed and why it should help.");
1401
1678
  return lines.join("\n");
1402
1679
  }
@@ -1432,6 +1709,343 @@ async function submitOptimiseTask(apiUrl, projectId, prompt) {
1432
1709
  return res.json();
1433
1710
  }
1434
1711
 
1712
+ // ../bench/dist/ab.js
1713
+ function passRate(report) {
1714
+ return report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1715
+ }
1716
+ function findResult(report, taskId) {
1717
+ return report.results.find((r) => r.task.id === taskId);
1718
+ }
1719
+ function buildAbReport(baseline, candidate, baselineReport, candidateReport) {
1720
+ const baselineRate = passRate(baselineReport);
1721
+ const candidateRate = passRate(candidateReport);
1722
+ const taskIds = /* @__PURE__ */ new Set();
1723
+ for (const r of baselineReport.results)
1724
+ taskIds.add(r.task.id);
1725
+ for (const r of candidateReport.results)
1726
+ taskIds.add(r.task.id);
1727
+ const perTask = Array.from(taskIds).map((taskId) => {
1728
+ const base = findResult(baselineReport, taskId);
1729
+ const cand = findResult(candidateReport, taskId);
1730
+ return {
1731
+ taskId,
1732
+ taskName: base?.task.name ?? cand?.task.name ?? taskId,
1733
+ baselinePassed: base?.evaluation.passed ?? false,
1734
+ candidatePassed: cand?.evaluation.passed ?? false,
1735
+ baselineTokens: base?.usage?.totalTokens,
1736
+ candidateTokens: cand?.usage?.totalTokens
1737
+ };
1738
+ });
1739
+ return {
1740
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1741
+ baseline,
1742
+ candidate,
1743
+ baselineReport,
1744
+ candidateReport,
1745
+ delta: {
1746
+ passRate: candidateRate - baselineRate,
1747
+ passedDelta: candidateReport.passed - baselineReport.passed,
1748
+ durationMsDelta: candidateReport.totalDurationMs - baselineReport.totalDurationMs,
1749
+ promptTokensDelta: (candidateReport.totalUsage?.promptTokens ?? 0) - (baselineReport.totalUsage?.promptTokens ?? 0),
1750
+ completionTokensDelta: (candidateReport.totalUsage?.completionTokens ?? 0) - (baselineReport.totalUsage?.completionTokens ?? 0),
1751
+ totalTokensDelta: (candidateReport.totalUsage?.totalTokens ?? 0) - (baselineReport.totalUsage?.totalTokens ?? 0)
1752
+ },
1753
+ perTask
1754
+ };
1755
+ }
1756
+ async function runAbBenchmark(tasks, baseline, candidate, options) {
1757
+ const suiteName = options.suiteName ?? "ab";
1758
+ const baselineReport = await runBenchmark(tasks, {
1759
+ ...options,
1760
+ suiteName: `${suiteName}-baseline`
1761
+ });
1762
+ const candidateReport = await runBenchmark(tasks, {
1763
+ ...options,
1764
+ suiteName: `${suiteName}-candidate`
1765
+ });
1766
+ return buildAbReport(baseline, candidate, baselineReport, candidateReport);
1767
+ }
1768
+ function deltaSymbol(value) {
1769
+ if (value > 0)
1770
+ return "+";
1771
+ if (value < 0)
1772
+ return "";
1773
+ return "";
1774
+ }
1775
+ function formatDelta(value) {
1776
+ return `${deltaSymbol(value)}${String(value)}`;
1777
+ }
1778
+ function formatAbReport(report) {
1779
+ const lines = [];
1780
+ lines.push("# Omega A/B Benchmark Report");
1781
+ lines.push("");
1782
+ lines.push(`- Timestamp: ${report.timestamp}`);
1783
+ lines.push(`- Baseline: ${report.baseline.name}`);
1784
+ lines.push(`- Candidate: ${report.candidate.name}`);
1785
+ lines.push("");
1786
+ lines.push("## Summary");
1787
+ lines.push(`| Metric | Baseline | Candidate | Delta |`);
1788
+ lines.push(`|---|---|---|---|`);
1789
+ lines.push(`| Pass rate | ${String(passRate(report.baselineReport))}% | ${String(passRate(report.candidateReport))}% | ${formatDelta(report.delta.passRate)}% |`);
1790
+ lines.push(`| Passed | ${String(report.baselineReport.passed)}/${String(report.baselineReport.total)} | ${String(report.candidateReport.passed)}/${String(report.candidateReport.total)} | ${formatDelta(report.delta.passedDelta)} |`);
1791
+ lines.push(`| Duration | ${formatDuration2(report.baselineReport.totalDurationMs)} | ${formatDuration2(report.candidateReport.totalDurationMs)} | ${formatDuration2(report.delta.durationMsDelta)} |`);
1792
+ lines.push(`| Tokens | ${String(report.baselineReport.totalUsage?.totalTokens ?? 0)} | ${String(report.candidateReport.totalUsage?.totalTokens ?? 0)} | ${formatDelta(report.delta.totalTokensDelta)} |`);
1793
+ lines.push("");
1794
+ lines.push("## Per-task comparison");
1795
+ lines.push(`| Task | Baseline | Candidate | Token delta |`);
1796
+ lines.push(`|---|---|---|---|`);
1797
+ for (const t of report.perTask) {
1798
+ const tokenDelta = t.baselineTokens !== void 0 && t.candidateTokens !== void 0 ? formatDelta(t.candidateTokens - t.baselineTokens) : "-";
1799
+ lines.push(`| ${t.taskName} | ${t.baselinePassed ? "pass" : "fail"} | ${t.candidatePassed ? "pass" : "fail"} | ${tokenDelta} |`);
1800
+ }
1801
+ lines.push("");
1802
+ return lines.join("\n");
1803
+ }
1804
+ function formatDuration2(ms) {
1805
+ if (ms < 1e3)
1806
+ return `${String(Math.round(ms))}ms`;
1807
+ return `${(ms / 1e3).toFixed(1)}s`;
1808
+ }
1809
+
1810
+ // ../agent/dist/tool-definitions.js
1811
+ var AGENT_TOOLS = [
1812
+ {
1813
+ name: "read_file",
1814
+ description: "Read a file relative to project root.",
1815
+ parameters: {
1816
+ type: "object",
1817
+ properties: { path: { type: "string" } },
1818
+ required: ["path"]
1819
+ }
1820
+ },
1821
+ {
1822
+ name: "write_file",
1823
+ description: "Write content to a file relative to project root.",
1824
+ parameters: {
1825
+ type: "object",
1826
+ properties: { path: { type: "string" }, content: { type: "string" } },
1827
+ required: ["path", "content"]
1828
+ }
1829
+ },
1830
+ {
1831
+ name: "edit_file",
1832
+ description: "Apply a targeted edit to an existing file relative to project root. Replaces one occurrence of old_string with new_string.",
1833
+ parameters: {
1834
+ type: "object",
1835
+ properties: {
1836
+ path: { type: "string" },
1837
+ old_string: { type: "string" },
1838
+ new_string: { type: "string" }
1839
+ },
1840
+ required: ["path", "old_string", "new_string"]
1841
+ }
1842
+ },
1843
+ {
1844
+ name: "run_command",
1845
+ description: "Run a shell command in the project root.",
1846
+ parameters: {
1847
+ type: "object",
1848
+ properties: { command: { type: "string" } },
1849
+ required: ["command"]
1850
+ }
1851
+ },
1852
+ {
1853
+ name: "think",
1854
+ description: "Record reasoning.",
1855
+ parameters: {
1856
+ type: "object",
1857
+ properties: { thought: { type: "string" } },
1858
+ required: ["thought"]
1859
+ }
1860
+ },
1861
+ {
1862
+ name: "finish",
1863
+ description: "Mark the task complete.",
1864
+ parameters: {
1865
+ type: "object",
1866
+ properties: { summary: { type: "string" }, success: { type: "boolean" } },
1867
+ required: ["summary", "success"]
1868
+ }
1869
+ },
1870
+ {
1871
+ name: "publish",
1872
+ description: "Build, validate, and publish the project.",
1873
+ parameters: {
1874
+ type: "object",
1875
+ properties: { version: { type: "string" } },
1876
+ required: []
1877
+ }
1878
+ }
1879
+ ];
1880
+
1881
+ // ../agent/dist/planner.js
1882
+ var toolDescriptions = AGENT_TOOLS.map((t) => `- ${t.name}: ${t.description}`).join("\n");
1883
+ var PLAN_PROMPT = `You are a planning assistant. Given a task, produce a concise step-by-step plan.
1884
+
1885
+ Respond with strict JSON in this exact shape (no markdown):
1886
+ {
1887
+ "reasoning": "brief reasoning",
1888
+ "plan": [
1889
+ { "name": "step name", "tool": "optional tool name", "input": { optional tool args } }
1890
+ ]
1891
+ }
1892
+
1893
+ Available tools:
1894
+ ${toolDescriptions}
1895
+
1896
+ If a step does not need a tool, omit tool/input. Use edit_file for small file changes.`;
1897
+
1898
+ // ../agent/dist/tools.js
1899
+ import { execFile as execFile2 } from "node:child_process";
1900
+ import { promisify as promisify2 } from "node:util";
1901
+ var execFileAsync2 = promisify2(execFile2);
1902
+
1903
+ // ../agent/dist/validator.js
1904
+ import { execFile as execFile3 } from "node:child_process";
1905
+ import { promisify as promisify3 } from "node:util";
1906
+ var execFileAsync3 = promisify3(execFile3);
1907
+
1908
+ // ../agent/dist/publisher.js
1909
+ import { execFile as execFile5 } from "node:child_process";
1910
+ import { promisify as promisify5 } from "node:util";
1911
+
1912
+ // ../agent/dist/git.js
1913
+ import { execFile as execFile4 } from "node:child_process";
1914
+ import { promisify as promisify4 } from "node:util";
1915
+ var execFileAsync4 = promisify4(execFile4);
1916
+
1917
+ // ../agent/dist/publisher.js
1918
+ var execFileAsync5 = promisify5(execFile5);
1919
+
1920
+ // ../agent/dist/prompts.js
1921
+ import fs6 from "node:fs";
1922
+ function loadPromptFromEnv(name) {
1923
+ const direct = process.env[name];
1924
+ if (direct)
1925
+ return direct;
1926
+ const fileKey = `${name}_FILE`;
1927
+ const filePath = process.env[fileKey];
1928
+ if (filePath) {
1929
+ try {
1930
+ return fs6.readFileSync(filePath, "utf-8");
1931
+ } catch {
1932
+ }
1933
+ }
1934
+ return void 0;
1935
+ }
1936
+ var AGENT_SYSTEM_PROMPT = loadPromptFromEnv("OMEGA_SYSTEM_PROMPT") ?? `You are Omega, an autonomous software engineering agent running inside a project repository.
1937
+
1938
+ Your job is to complete the user's task by calling tools. Do not write prose or explanations outside tool calls. Prefer targeted edits over full rewrites.
1939
+
1940
+ Available tools:
1941
+
1942
+ - read_file: Read a file relative to project root. Arguments: { "path": "relative/path" }
1943
+ - write_file: Overwrite or create a file. Arguments: { "path": "relative/path", "content": "full file content" }
1944
+ - edit_file: Replace one exact occurrence of old_string with new_string in an existing file. Use this for small changes. Arguments: { "path": "relative/path", "old_string": "...", "new_string": "..." }
1945
+ - run_command: Run a single simple command. No pipes (|), &&, ;, redirects, or globs. Prefer pnpm/npm/node. Arguments: { "command": "pnpm lint" }
1946
+ - think: Record a reasoning step. Arguments: { "thought": "..." }
1947
+ - finish: Mark the task complete. Arguments: { "summary": "what was done", "success": true }. Use summary, not message.
1948
+ - publish: Run lint/test/build validation in one step. This is the preferred validation tool. Arguments: { "version": "optional" }
1949
+
1950
+ Rules:
1951
+ 1. Read the task, then use think to plan.
1952
+ 2. Use edit_file for small changes; write_file only when creating a file or rewriting most of it.
1953
+ 3. ALWAYS read a file with read_file before using edit_file on it. Do NOT read a file before write_file when creating a new file.
1954
+ 4. Before creating JavaScript files, read package.json to check whether "type" is "module" and use ESM (export) or CommonJS (module.exports) accordingly.
1955
+ 5. Immediately after making any edit, call publish to run validation. Do not read more files, do not think, and do not call finish before validating.
1956
+ 6. If validation fails, fix the issue and call publish again. Repeat until validation passes.
1957
+ 7. Do NOT call finish until validation passes. If you call finish without validation, the task will fail.
1958
+ 8. Do not expose secrets or run destructive commands.
1959
+ 9. Finish only when the task is done. Always include summary and success.
1960
+ 10. Do not repeat the same tool call with the same arguments. If something fails, change your approach.
1961
+ 11. Do not ask the user for clarification. Do not say "No task was provided". Use the Task and Description above to proceed.
1962
+ 12. You have a limited number of steps. If you spend too many steps reading or planning without validating, the task will fail.
1963
+
1964
+ Example tool call sequence for "Add a greet function":
1965
+ 1. think: { "thought": "I need to create greet.js and export a greet function." }
1966
+ 2. write_file: { "path": "greet.js", "content": "export function greet(name) { return 'Hello, ' + name + '!'; }\\n" }
1967
+ 3. publish: { }
1968
+ 4. finish: { "summary": "Created greet.js with greet(name) function and verified it passes tests.", "success": true }
1969
+
1970
+ Example tool call sequence for "Fix a lint warning":
1971
+ 1. think: { "thought": "The linter flags var. I need to read src/index.js and replace var with const or let." }
1972
+ 2. read_file: { "path": "src/index.js" }
1973
+ 3. edit_file: { "path": "src/index.js", "old_string": "var greeting = 'Hello';", "new_string": "const greeting = 'Hello';" }
1974
+ 4. publish: { }
1975
+ 5. finish: { "summary": "Replaced var with const and verified lint passes.", "success": true }`;
1976
+ var TEXT_TOOLS_SYSTEM_PROMPT = loadPromptFromEnv("OMEGA_TEXT_TOOLS_PROMPT") ?? `You are Omega, an autonomous software engineering agent running inside a project repository.
1977
+
1978
+ You MUST respond with a single JSON object containing a "tool_calls" array. Do not output markdown, explanations, or reasoning outside the JSON.
1979
+
1980
+ Available tools (use ONLY these exact names):
1981
+
1982
+ - read_file: { "path": "relative/path" }
1983
+ - write_file: { "path": "relative/path", "content": "full file content" }
1984
+ - edit_file: { "path": "relative/path", "old_string": "...", "new_string": "..." }
1985
+ - run_command: { "command": "simple command, no pipes/&&/;/redirects" }
1986
+ - think: { "thought": "reasoning text" }
1987
+ - finish: { "summary": "what was done", "success": true | false }
1988
+ - publish: { "version": "optional" }
1989
+
1990
+ Rules:
1991
+ - Plan with think, then act.
1992
+ - Use edit_file for small changes; write_file only for new files or large rewrites.
1993
+ - ALWAYS read a file with read_file before using edit_file on it. Do NOT read a file before write_file when creating a new file.
1994
+ - Before creating JavaScript files, read package.json to check whether "type" is "module" and use ESM (export) or CommonJS (module.exports) accordingly.
1995
+ - Immediately after making any edit, call publish to run validation. Do not read more files or call finish before validating.
1996
+ - If validation fails, fix the issue and call publish again.
1997
+ - Do NOT call finish until validation passes.
1998
+ - Do not expose secrets or run destructive commands.
1999
+ - Finish only when done. Use summary, not message.
2000
+ - Do not repeat the same tool call with the same arguments.
2001
+ - Do not ask the user for clarification. Do not say "No task was provided". Use the Task and Description above to proceed.
2002
+
2003
+ Example response for "Add a greet function":
2004
+ { "tool_calls": [
2005
+ { "id": "1", "name": "think", "arguments": { "thought": "Create greet.js with greet(name)." } },
2006
+ { "id": "2", "name": "write_file", "arguments": { "path": "greet.js", "content": "export function greet(name) { return 'Hello, ' + name + '!'; }\\n" } },
2007
+ { "id": "3", "name": "publish", "arguments": {} },
2008
+ { "id": "4", "name": "finish", "arguments": { "summary": "Created greet.js and verified tests pass.", "success": true } }
2009
+ ] }`;
2010
+
2011
+ // ../agent/dist/prompt-versioning.js
2012
+ import fs7 from "node:fs/promises";
2013
+ import path9 from "node:path";
2014
+ import crypto from "node:crypto";
2015
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
2016
+ var __dirname3 = path9.dirname(fileURLToPath4(import.meta.url));
2017
+ var PROMPTS_PATH = path9.resolve(__dirname3, "..", "src", "prompts.ts");
2018
+ var PLANNER_PATH = path9.resolve(__dirname3, "..", "src", "planner.ts");
2019
+ function hashString(value) {
2020
+ return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
2021
+ }
2022
+ function hashPrompts(input) {
2023
+ return hashString([input.systemPrompt, input.textToolsPrompt, input.planningPrompt ?? ""].join("\n---\n"));
2024
+ }
2025
+ async function readPromptsSource() {
2026
+ const source = await fs7.readFile(PROMPTS_PATH, "utf-8");
2027
+ const systemMatch = /export const AGENT_SYSTEM_PROMPT =\s*(?:loadPromptFromEnv\('OMEGA_SYSTEM_PROMPT'\) \?\?\s*)?`([\s\S]*?)`;/m.exec(source);
2028
+ const textToolsMatch = /export const TEXT_TOOLS_SYSTEM_PROMPT =\s*(?:loadPromptFromEnv\('OMEGA_TEXT_TOOLS_PROMPT'\) \?\?\s*)?`([\s\S]*?)`;/m.exec(source);
2029
+ return {
2030
+ systemPrompt: systemMatch?.[1] ?? "",
2031
+ textToolsPrompt: textToolsMatch?.[1] ?? ""
2032
+ };
2033
+ }
2034
+ async function loadCurrentPrompts() {
2035
+ const { systemPrompt, textToolsPrompt } = await readPromptsSource();
2036
+ const plannerSource = await fs7.readFile(PLANNER_PATH, "utf-8");
2037
+ const planningMatch = /const PLAN_PROMPT = `([\s\S]*?)`;/m.exec(plannerSource);
2038
+ const planningPrompt = planningMatch?.[1];
2039
+ return {
2040
+ name: `auto-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`,
2041
+ sourcePath: PROMPTS_PATH,
2042
+ systemPrompt,
2043
+ textToolsPrompt,
2044
+ planningPrompt,
2045
+ hash: hashPrompts({ systemPrompt, textToolsPrompt, planningPrompt })
2046
+ };
2047
+ }
2048
+
1435
2049
  // ../../apps/cli/src/commands/bench.ts
1436
2050
  async function waitForApi2(apiUrl, maxMs = 1e4) {
1437
2051
  const deadline = Date.now() + maxMs;
@@ -1447,38 +2061,75 @@ async function waitForApi2(apiUrl, maxMs = 1e4) {
1447
2061
  }
1448
2062
  function currentProject(apiUrl) {
1449
2063
  const cwd = process.cwd();
1450
- const name = path9.basename(cwd);
2064
+ const name = path10.basename(cwd);
1451
2065
  return ensureProject2(apiUrl, `bench-${name}`, cwd);
1452
2066
  }
1453
- var runCmd = new Command8("run").description("Run a benchmark suite").option("--suite <name>", "suite name: synthetic | deep-swe", "synthetic").option("--path <dir>", "path to DeepSWE tasks directory (for deep-swe suite)").option("--n-tasks <n>", "limit number of tasks (for deep-swe)", parseInt).option("--sample-seed <n>", "seed for deterministic sampling (for deep-swe)", parseInt).option("--timeout <ms>", "per-task timeout in ms", "120000").option("--output-dir <dir>", "report output directory", ".omega/reports").option("--provider <name>", "provider to use for benchmark tasks").option("--model <model>", "model to use for benchmark tasks").action(async (opts) => {
1454
- const apiUrl = getApiUrl();
1455
- await waitForApi2(apiUrl);
1456
- const timeoutMs = Number(opts.timeout);
1457
- let tasks;
1458
- let suiteName;
2067
+ async function savePromptVersion(apiUrl, name) {
2068
+ const current = await loadCurrentPrompts();
2069
+ const versionRes = await fetch(`${apiUrl}/prompt-versions`, {
2070
+ method: "POST",
2071
+ headers: { "Content-Type": "application/json" },
2072
+ body: JSON.stringify({
2073
+ name: name ?? current.name,
2074
+ sourcePath: current.sourcePath,
2075
+ systemPrompt: current.systemPrompt,
2076
+ textToolsPrompt: current.textToolsPrompt,
2077
+ hash: current.hash,
2078
+ metadata: { planningPrompt: current.planningPrompt }
2079
+ })
2080
+ });
2081
+ if (!versionRes.ok) {
2082
+ console.warn("Could not save prompt version:", await versionRes.text());
2083
+ return void 0;
2084
+ }
2085
+ return versionRes.json();
2086
+ }
2087
+ async function loadPromptSpec(source) {
2088
+ if (source === "current") {
2089
+ const current = await loadCurrentPrompts();
2090
+ return {
2091
+ name: "current",
2092
+ systemPrompt: current.systemPrompt,
2093
+ textToolsPrompt: current.textToolsPrompt
2094
+ };
2095
+ }
2096
+ const raw = await fs8.readFile(path10.resolve(source), "utf-8");
2097
+ const parsed = JSON.parse(raw);
2098
+ if (!parsed.systemPrompt || !parsed.textToolsPrompt) {
2099
+ throw new Error("Prompt spec must include systemPrompt and textToolsPrompt");
2100
+ }
2101
+ return parsed;
2102
+ }
2103
+ async function resolveTasks(opts) {
1459
2104
  if (opts.suite === "deep-swe") {
1460
2105
  if (!opts.path) {
1461
2106
  throw new Error("--path is required for the deep-swe suite");
1462
2107
  }
1463
- tasks = await loadDeepSWESuite({
2108
+ const tasks = await loadDeepSWESuite({
1464
2109
  tasksDir: opts.path,
1465
2110
  nTasks: opts.nTasks,
1466
2111
  sampleSeed: opts.sampleSeed
1467
2112
  });
1468
- suiteName = "deep-swe";
1469
- } else if (opts.suite === "synthetic") {
1470
- tasks = syntheticSuite();
2113
+ return { tasks, suiteName: "deep-swe" };
2114
+ }
2115
+ if (opts.suite === "synthetic") {
2116
+ let tasks = syntheticSuite();
1471
2117
  if (opts.nTasks !== void 0 && opts.nTasks > 0) {
1472
2118
  tasks = tasks.slice(0, opts.nTasks);
1473
2119
  }
1474
- suiteName = "synthetic";
1475
- } else {
1476
- throw new Error(`Unknown suite: ${opts.suite}`);
2120
+ return { tasks, suiteName: "synthetic" };
1477
2121
  }
2122
+ throw new Error(`Unknown suite: ${opts.suite}`);
2123
+ }
2124
+ var runCmd = new Command8("run").description("Run a benchmark suite").option("--suite <name>", "suite name: synthetic | deep-swe", "synthetic").option("--path <dir>", "path to DeepSWE tasks directory (for deep-swe suite)").option("--n-tasks <n>", "limit number of tasks", parseInt).option("--sample-seed <n>", "seed for deterministic sampling (for deep-swe)", parseInt).option("--timeout <ms>", "per-task timeout in ms", "120000").option("--output-dir <dir>", "report output directory", ".omega/reports").option("--provider <name>", "provider to use for benchmark tasks").option("--model <model>", "model to use for benchmark tasks").action(async (opts) => {
2125
+ const apiUrl = getApiUrl();
2126
+ await waitForApi2(apiUrl);
2127
+ const { tasks, suiteName } = await resolveTasks(opts);
1478
2128
  if (tasks.length === 0) {
1479
2129
  console.log("No benchmark tasks to run.");
1480
2130
  return;
1481
2131
  }
2132
+ const timeoutMs = Number(opts.timeout);
1482
2133
  console.log(`Running ${String(tasks.length)} benchmark tasks against ${apiUrl}`);
1483
2134
  const report = await runBenchmark(tasks, {
1484
2135
  apiUrl,
@@ -1514,7 +2165,121 @@ var optimiseCmd = new Command8("optimise").description("Create a self-improve ta
1514
2165
  console.log(`Created self-improve task ${task.id}`);
1515
2166
  console.log(`Run \`omega task run ${task.id}\` to execute it.`);
1516
2167
  });
1517
- var benchCmd = new Command8("bench").description("Run benchmarks and optimise prompts").addCommand(runCmd).addCommand(optimiseCmd);
2168
+ var abCmd = new Command8("ab").description("Run an A/B benchmark comparing two prompt versions").option("--baseline <source>", 'baseline prompts: "current" or path to JSON file', "current").option("--candidate <source>", "candidate prompts: path to JSON file").option("--suite <name>", "suite name: synthetic | deep-swe", "synthetic").option("--path <dir>", "path to DeepSWE tasks directory (for deep-swe suite)").option("--n-tasks <n>", "limit number of tasks", parseInt).option("--sample-seed <n>", "seed for deterministic sampling (for deep-swe)", parseInt).option("--timeout <ms>", "per-task timeout in ms", "120000").option("--output-dir <dir>", "report output directory", ".omega/reports").option("--provider <name>", "provider to use for benchmark tasks").option("--model <model>", "model to use for benchmark tasks").action(async (opts) => {
2169
+ if (!opts.candidate) {
2170
+ throw new Error("--candidate is required");
2171
+ }
2172
+ if (opts.suite === "deep-swe") {
2173
+ throw new Error("DeepSWE A/B not yet supported; use --suite synthetic");
2174
+ }
2175
+ const apiUrl = getApiUrl();
2176
+ await waitForApi2(apiUrl);
2177
+ const { tasks, suiteName } = await resolveTasks(opts);
2178
+ if (tasks.length === 0) {
2179
+ console.log("No benchmark tasks to run.");
2180
+ return;
2181
+ }
2182
+ const timeoutMs = Number(opts.timeout);
2183
+ const baselineSpec = await loadPromptSpec(opts.baseline);
2184
+ const candidateSpec = await loadPromptSpec(opts.candidate);
2185
+ console.log(`Baseline: ${baselineSpec.name}`);
2186
+ console.log(`Candidate: ${candidateSpec.name}`);
2187
+ const report = await runAbBenchmark(tasks, baselineSpec, candidateSpec, {
2188
+ apiUrl,
2189
+ suiteName,
2190
+ timeoutMs,
2191
+ provider: opts.provider,
2192
+ model: opts.model,
2193
+ onProgress: (result) => {
2194
+ const symbol = result.evaluation.passed ? "\u2713" : "\u2717";
2195
+ console.log(`${symbol} ${result.task.name} [${result.status}] ${String(result.durationMs)}ms`);
2196
+ }
2197
+ });
2198
+ await fs8.mkdir(opts.outputDir, { recursive: true });
2199
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2200
+ const jsonFile = path10.join(opts.outputDir, `ab-${ts}.json`);
2201
+ const mdFile = path10.join(opts.outputDir, `ab-${ts}.md`);
2202
+ await fs8.writeFile(jsonFile, JSON.stringify(report, null, 2), "utf-8");
2203
+ await fs8.writeFile(mdFile, formatAbReport(report), "utf-8");
2204
+ console.log("\n" + formatAbReport(report));
2205
+ console.log(`
2206
+ A/B report written to ${jsonFile}`);
2207
+ });
2208
+ var loopCmd = new Command8("loop").description("Run a self-improve benchmark loop").option("--suite <name>", "suite name: synthetic | deep-swe", "synthetic").option("--path <dir>", "path to DeepSWE tasks directory (for deep-swe suite)").option("--n-tasks <n>", "limit number of tasks", parseInt).option("--sample-seed <n>", "seed for deterministic sampling (for deep-swe)", parseInt).option("--iterations <n>", "max iterations", "1").option("--timeout <ms>", "per-task timeout in ms", "120000").option("--output-dir <dir>", "report output directory", ".omega/reports").option("--provider <name>", "provider to use for self-improve tasks").option("--model <model>", "model to use for self-improve tasks").action(async (opts) => {
2209
+ const apiUrl = getApiUrl();
2210
+ await waitForApi2(apiUrl);
2211
+ const { tasks, suiteName } = await resolveTasks(opts);
2212
+ if (tasks.length === 0) {
2213
+ console.log("No benchmark tasks to run.");
2214
+ return;
2215
+ }
2216
+ const timeoutMs = Number(opts.timeout);
2217
+ const maxIterations = Number(opts.iterations);
2218
+ const project = await currentProject(apiUrl);
2219
+ for (let i = 1; i <= maxIterations; i++) {
2220
+ console.log(`
2221
+ === Iteration ${String(i)}/${String(maxIterations)} ===`);
2222
+ const beforeVersion = await savePromptVersion(
2223
+ apiUrl,
2224
+ `loop-before-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`
2225
+ );
2226
+ if (beforeVersion) {
2227
+ console.log(`Saved before-loop prompt version: ${beforeVersion.name} (${beforeVersion.id})`);
2228
+ }
2229
+ console.log(`
2230
+ Running ${String(tasks.length)} benchmark tasks...`);
2231
+ const report = await runBenchmark(tasks, {
2232
+ apiUrl,
2233
+ suiteName,
2234
+ timeoutMs,
2235
+ provider: opts.provider,
2236
+ model: opts.model,
2237
+ onProgress: (result) => {
2238
+ const symbol = result.evaluation.passed ? "\u2713" : "\u2717";
2239
+ console.log(`${symbol} ${result.task.name} [${result.status}] ${String(result.durationMs)}ms`);
2240
+ }
2241
+ });
2242
+ await writeReport(report, opts.outputDir);
2243
+ printSummary(report);
2244
+ const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
2245
+ if (passRate2 === 100) {
2246
+ console.log("\nAll tasks passed. Loop complete.");
2247
+ break;
2248
+ }
2249
+ const context = await loadOptimisationContext(apiUrl, opts.outputDir);
2250
+ if (!context) {
2251
+ throw new Error("Benchmark report could not be loaded for optimisation");
2252
+ }
2253
+ const prompt = buildOptimisePrompt(context.report, context.failedResult, context.traceFlowText);
2254
+ const task = await submitOptimiseTask(apiUrl, project.id, prompt);
2255
+ if (opts.provider || opts.model) {
2256
+ await fetch(`${apiUrl}/tasks/${task.id}`, {
2257
+ method: "PATCH",
2258
+ headers: { "Content-Type": "application/json" },
2259
+ body: JSON.stringify({ provider: opts.provider, model: opts.model })
2260
+ });
2261
+ }
2262
+ console.log(
2263
+ `
2264
+ Created self-improve task ${task.id}${opts.provider ? ` (provider: ${opts.provider})` : ""}${opts.model ? ` (model: ${opts.model})` : ""}`
2265
+ );
2266
+ console.log("Running agent...");
2267
+ await runTask(apiUrl, task.id);
2268
+ const finished = await waitForTask(apiUrl, task.id, timeoutMs * 5);
2269
+ console.log(`Agent finished: ${finished.status}`);
2270
+ if (finished.error) {
2271
+ console.error(`Agent error: ${finished.error}`);
2272
+ }
2273
+ const afterVersion = await savePromptVersion(
2274
+ apiUrl,
2275
+ `loop-after-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`
2276
+ );
2277
+ if (afterVersion) {
2278
+ console.log(`Saved after-loop prompt version: ${afterVersion.name} (${afterVersion.id})`);
2279
+ }
2280
+ }
2281
+ });
2282
+ var benchCmd = new Command8("bench").description("Run benchmarks and optimise prompts").addCommand(runCmd).addCommand(abCmd).addCommand(optimiseCmd).addCommand(loopCmd);
1518
2283
 
1519
2284
  // ../../apps/cli/src/index.ts
1520
2285
  program2.name("harness").description("Omega harness CLI").version("0.1.0").option("--api <url>", "API base URL", "http://localhost:4000");