@castlemilk/omega 0.6.13 → 0.6.14

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 (104) hide show
  1. package/dist/cli.js +264 -1389
  2. package/dist/server/dist/app.d.ts.map +1 -1
  3. package/dist/server/dist/app.js +2 -0
  4. package/dist/server/dist/app.js.map +1 -1
  5. package/dist/server/dist/lib/run-task.d.ts.map +1 -1
  6. package/dist/server/dist/lib/run-task.js +2 -3
  7. package/dist/server/dist/lib/run-task.js.map +1 -1
  8. package/dist/server/dist/routes/metrics.d.ts.map +1 -1
  9. package/dist/server/dist/routes/metrics.js +2 -76
  10. package/dist/server/dist/routes/metrics.js.map +1 -1
  11. package/dist/server/dist/routes/reports.d.ts +3 -0
  12. package/dist/server/dist/routes/reports.d.ts.map +1 -0
  13. package/dist/server/dist/routes/reports.js +44 -0
  14. package/dist/server/dist/routes/reports.js.map +1 -0
  15. package/dist/server/dist/routes/tasks.d.ts.map +1 -1
  16. package/dist/server/dist/routes/tasks.js +0 -4
  17. package/dist/server/dist/routes/tasks.js.map +1 -1
  18. package/dist/server/node_modules/@grpc/proto-loader/build/bin/proto-loader-gen-types.js +0 -0
  19. package/dist/server/node_modules/@omega/agent/dist/executor.d.ts.map +1 -1
  20. package/dist/server/node_modules/@omega/agent/dist/executor.js +10 -83
  21. package/dist/server/node_modules/@omega/agent/dist/executor.js.map +1 -1
  22. package/dist/server/node_modules/@omega/agent/dist/index.d.ts +1 -1
  23. package/dist/server/node_modules/@omega/agent/dist/index.d.ts.map +1 -1
  24. package/dist/server/node_modules/@omega/agent/dist/index.js +1 -1
  25. package/dist/server/node_modules/@omega/agent/dist/index.js.map +1 -1
  26. package/dist/server/node_modules/@omega/agent/dist/planner.d.ts +0 -5
  27. package/dist/server/node_modules/@omega/agent/dist/planner.d.ts.map +1 -1
  28. package/dist/server/node_modules/@omega/agent/dist/planner.js +5 -31
  29. package/dist/server/node_modules/@omega/agent/dist/planner.js.map +1 -1
  30. package/dist/server/node_modules/@omega/agent/dist/prompt-versioning.d.ts.map +1 -1
  31. package/dist/server/node_modules/@omega/agent/dist/prompt-versioning.js +2 -2
  32. package/dist/server/node_modules/@omega/agent/dist/prompt-versioning.js.map +1 -1
  33. package/dist/server/node_modules/@omega/agent/dist/prompts.d.ts.map +1 -1
  34. package/dist/server/node_modules/@omega/agent/dist/prompts.js +16 -9
  35. package/dist/server/node_modules/@omega/agent/dist/prompts.js.map +1 -1
  36. package/dist/server/node_modules/@omega/agent/package.json +0 -4
  37. package/dist/server/node_modules/@omega/core/dist/provider.d.ts +2 -12
  38. package/dist/server/node_modules/@omega/core/dist/provider.d.ts.map +1 -1
  39. package/dist/server/node_modules/@omega/db/dist/seed.js +6 -6
  40. package/dist/server/node_modules/@omega/db/dist/seed.js.map +1 -1
  41. package/dist/server/node_modules/@omega/db/generated/client/edge.js +3 -6
  42. package/dist/server/node_modules/@omega/db/generated/client/index-browser.js +0 -3
  43. package/dist/server/node_modules/@omega/db/generated/client/index.d.ts +0 -223
  44. package/dist/server/node_modules/@omega/db/generated/client/index.js +3 -6
  45. package/dist/server/node_modules/@omega/db/generated/client/libquery_engine-darwin-arm64.dylib.node +0 -0
  46. package/dist/server/node_modules/@omega/db/generated/client/package.json +1 -1
  47. package/dist/server/node_modules/@omega/db/generated/client/schema.prisma +0 -3
  48. package/dist/server/node_modules/@omega/db/generated/client/wasm.js +3 -6
  49. package/dist/server/node_modules/@omega/providers/dist/anthropic.d.ts +2 -2
  50. package/dist/server/node_modules/@omega/providers/dist/anthropic.d.ts.map +1 -1
  51. package/dist/server/node_modules/@omega/providers/dist/anthropic.js +1 -16
  52. package/dist/server/node_modules/@omega/providers/dist/anthropic.js.map +1 -1
  53. package/dist/server/node_modules/@omega/providers/dist/gemini.d.ts +2 -2
  54. package/dist/server/node_modules/@omega/providers/dist/gemini.d.ts.map +1 -1
  55. package/dist/server/node_modules/@omega/providers/dist/gemini.js +1 -14
  56. package/dist/server/node_modules/@omega/providers/dist/gemini.js.map +1 -1
  57. package/dist/server/node_modules/@omega/providers/dist/kimi.d.ts +4 -3
  58. package/dist/server/node_modules/@omega/providers/dist/kimi.d.ts.map +1 -1
  59. package/dist/server/node_modules/@omega/providers/dist/kimi.js +4 -6
  60. package/dist/server/node_modules/@omega/providers/dist/kimi.js.map +1 -1
  61. package/dist/server/node_modules/@omega/providers/dist/ollama.d.ts +2 -2
  62. package/dist/server/node_modules/@omega/providers/dist/ollama.d.ts.map +1 -1
  63. package/dist/server/node_modules/@omega/providers/dist/ollama.js +1 -13
  64. package/dist/server/node_modules/@omega/providers/dist/ollama.js.map +1 -1
  65. package/dist/server/node_modules/@omega/providers/dist/openai.d.ts +3 -3
  66. package/dist/server/node_modules/@omega/providers/dist/openai.d.ts.map +1 -1
  67. package/dist/server/node_modules/@omega/providers/dist/openai.js +5 -28
  68. package/dist/server/node_modules/@omega/providers/dist/openai.js.map +1 -1
  69. package/dist/server/node_modules/@omega/providers/dist/providers.test.js +9 -13
  70. package/dist/server/node_modules/@omega/providers/dist/providers.test.js.map +1 -1
  71. package/dist/server/node_modules/mime/cli.js +0 -0
  72. package/dist/server/node_modules/mime/src/build.js +0 -0
  73. package/dist/server/node_modules/wrap-ansi/index.js +0 -0
  74. package/dist/server/pglite-data/base/5/1247 +0 -0
  75. package/dist/server/pglite-data/base/5/1249 +0 -0
  76. package/dist/server/pglite-data/base/5/1259 +0 -0
  77. package/dist/server/pglite-data/base/5/16384 +0 -0
  78. package/dist/server/pglite-data/base/5/16394 +0 -0
  79. package/dist/server/pglite-data/base/5/16404 +0 -0
  80. package/dist/server/pglite-data/base/5/16406 +0 -0
  81. package/dist/server/pglite-data/base/5/16421 +0 -0
  82. package/dist/server/pglite-data/base/5/16480 +0 -0
  83. package/dist/server/pglite-data/base/5/16510 +0 -0
  84. package/dist/server/pglite-data/base/5/2604 +0 -0
  85. package/dist/server/pglite-data/base/5/2606 +0 -0
  86. package/dist/server/pglite-data/base/5/2610 +0 -0
  87. package/dist/server/pglite-data/base/5/2620 +0 -0
  88. package/dist/server/pglite-data/base/5/2662 +0 -0
  89. package/dist/server/pglite-data/base/5/2663 +0 -0
  90. package/dist/server/pglite-data/base/5/pg_internal.init +0 -0
  91. package/dist/server/pglite-data/global/pg_control +0 -0
  92. package/dist/server/pglite-data/global/pg_internal.init +0 -0
  93. package/dist/server/pglite-data/pg_wal/000000010000000000000001 +0 -0
  94. package/dist/server/pglite-data/pg_xact/0000 +0 -0
  95. package/dist/server/pglite-data/postmaster.pid +1 -1
  96. package/dist/server/src/app.ts +2 -0
  97. package/dist/server/src/lib/run-task.ts +2 -3
  98. package/dist/server/src/routes/metrics.ts +2 -81
  99. package/dist/server/src/routes/reports.ts +54 -0
  100. package/dist/server/src/routes/tasks.ts +2 -6
  101. package/dist/web/dist/assets/index-Ce9D9lg5.css +1 -0
  102. package/dist/web/dist/assets/index-D-wO1mM7.js +40 -0
  103. package/dist/web/dist/index.html +2 -2
  104. package/package.json +13 -14
package/dist/cli.js CHANGED
@@ -66,20 +66,20 @@ function useTasks(pollMs = 1e3) {
66
66
  const data = await res.json();
67
67
  setConnected(true);
68
68
  const current = {};
69
- for (const task2 of data) {
70
- current[task2.id] = task2;
71
- const prev = previousTasks.current[task2.id];
69
+ for (const task of data) {
70
+ current[task.id] = task;
71
+ const prev = previousTasks.current[task.id];
72
72
  if (!prev) {
73
- addLog(`New task queued: "${task2.title}"`, "info");
74
- } else if (prev.status !== task2.status) {
75
- if (task2.status === "in_progress") {
76
- const provider = task2.provider ?? "unknown";
77
- const model = task2.model ?? "unknown";
78
- addLog(`LLM picked up "${task2.title}" \u2192 ${provider}/${model}`, "info");
79
- } else if (task2.status === "done") {
80
- addLog(`Completed: "${task2.title}"`, "success");
81
- } else if (task2.status === "failed") {
82
- addLog(`Failed: "${task2.title}"${task2.error ? ` \u2014 ${task2.error}` : ""}`, "error");
73
+ addLog(`New task queued: "${task.title}"`, "info");
74
+ } else if (prev.status !== task.status) {
75
+ if (task.status === "in_progress") {
76
+ const provider = task.provider ?? "unknown";
77
+ const model = task.model ?? "unknown";
78
+ addLog(`LLM picked up "${task.title}" \u2192 ${provider}/${model}`, "info");
79
+ } else if (task.status === "done") {
80
+ addLog(`Completed: "${task.title}"`, "success");
81
+ } else if (task.status === "failed") {
82
+ addLog(`Failed: "${task.title}"${task.error ? ` \u2014 ${task.error}` : ""}`, "error");
83
83
  }
84
84
  }
85
85
  }
@@ -231,16 +231,16 @@ function TaskList({
231
231
  children: [
232
232
  header,
233
233
  visible.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No tasks yet. Create one in the web UI." }),
234
- visible.map((task2) => {
235
- const providerText = task2.provider ? `${task2.provider}/${task2.model ?? ""}` : "-";
234
+ visible.map((task) => {
235
+ const providerText = task.provider ? `${task.provider}/${task.model ?? ""}` : "-";
236
236
  const titleMax = Math.max(10, columns - 62);
237
237
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
238
- /* @__PURE__ */ jsx(Text, { color: statusColor(task2.status), children: statusSymbol(task2.status) }),
239
- /* @__PURE__ */ jsx(Box, { width: 20, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: formatTime(new Date(task2.updatedAt)) }) }),
240
- /* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { color: statusColor(task2.status), bold: true, children: task2.status.toUpperCase() }) }),
238
+ /* @__PURE__ */ jsx(Text, { color: statusColor(task.status), children: statusSymbol(task.status) }),
239
+ /* @__PURE__ */ jsx(Box, { width: 20, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: formatTime(new Date(task.updatedAt)) }) }),
240
+ /* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { color: statusColor(task.status), bold: true, children: task.status.toUpperCase() }) }),
241
241
  /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncate(providerText, 23) }) }),
242
- /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { children: truncate(task2.title, titleMax) }) })
243
- ] }, task2.id);
242
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { children: truncate(task.title, titleMax) }) })
243
+ ] }, task.id);
244
244
  })
245
245
  ]
246
246
  }
@@ -318,76 +318,6 @@ var init_tui = __esm({
318
318
  }
319
319
  });
320
320
 
321
- // ../agent/dist/prompt-versioning.js
322
- var prompt_versioning_exports = {};
323
- __export(prompt_versioning_exports, {
324
- envForPrompts: () => envForPrompts,
325
- hashPrompts: () => hashPrompts,
326
- loadCurrentPrompts: () => loadCurrentPrompts,
327
- readPromptsSource: () => readPromptsSource,
328
- saveCurrentPrompts: () => saveCurrentPrompts
329
- });
330
- import fs7 from "node:fs/promises";
331
- import path10 from "node:path";
332
- import crypto from "node:crypto";
333
- import { fileURLToPath as fileURLToPath4 } from "node:url";
334
- function hashString(value) {
335
- return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
336
- }
337
- function hashPrompts(input) {
338
- return hashString([input.systemPrompt, input.textToolsPrompt, input.planningPrompt ?? ""].join("\n---\n"));
339
- }
340
- async function readPromptsSource() {
341
- const source = await fs7.readFile(PROMPTS_PATH, "utf-8");
342
- const systemMatch = /export const AGENT_SYSTEM_PROMPT =\s*(?:loadPromptFromEnv\('OMEGA_SYSTEM_PROMPT'\) \?\?\s*)?`([\s\S]*?)`;/m.exec(source);
343
- const textToolsMatch = /export const TEXT_TOOLS_SYSTEM_PROMPT =\s*(?:loadPromptFromEnv\('OMEGA_TEXT_TOOLS_PROMPT'\) \?\?\s*)?`([\s\S]*?)`;/m.exec(source);
344
- return {
345
- systemPrompt: systemMatch?.[1] ?? "",
346
- textToolsPrompt: textToolsMatch?.[1] ?? ""
347
- };
348
- }
349
- async function loadCurrentPrompts() {
350
- const { systemPrompt, textToolsPrompt } = await readPromptsSource();
351
- const plannerSource = await fs7.readFile(PLANNER_PATH, "utf-8");
352
- const planningMatch = /const PLAN_PROMPT = `([\s\S]*?)`;/m.exec(plannerSource);
353
- const planningPrompt = planningMatch?.[1];
354
- return {
355
- name: `auto-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`,
356
- sourcePath: PROMPTS_PATH,
357
- systemPrompt,
358
- textToolsPrompt,
359
- planningPrompt,
360
- hash: hashPrompts({ systemPrompt, textToolsPrompt, planningPrompt })
361
- };
362
- }
363
- async function saveCurrentPrompts(input) {
364
- const source = await fs7.readFile(PROMPTS_PATH, "utf-8");
365
- const replaceConst = (name, value) => {
366
- const pattern = new RegExp(`export const ${name} =\\s*(?:process\\.env\\.OMEGA_[A-Z_]+_PROMPT \\?\\?\\s*)?\`[\\s\\S]*?\`;`, "m");
367
- return source.replace(pattern, `export const ${name} =
368
- process.env.OMEGA_${name.replace(/([A-Z])/g, "_$1").toUpperCase()}_PROMPT ??
369
- \`${value}\`;`);
370
- };
371
- let updated = replaceConst("AGENT_SYSTEM_PROMPT", input.systemPrompt);
372
- updated = replaceConst("TEXT_TOOLS_SYSTEM_PROMPT", input.textToolsPrompt);
373
- await fs7.writeFile(PROMPTS_PATH, updated, "utf-8");
374
- }
375
- function envForPrompts(input) {
376
- return {
377
- OMEGA_SYSTEM_PROMPT: input.systemPrompt,
378
- OMEGA_TEXT_TOOLS_PROMPT: input.textToolsPrompt
379
- };
380
- }
381
- var __dirname3, PROMPTS_PATH, PLANNER_PATH;
382
- var init_prompt_versioning = __esm({
383
- "../agent/dist/prompt-versioning.js"() {
384
- "use strict";
385
- __dirname3 = path10.dirname(fileURLToPath4(import.meta.url));
386
- PROMPTS_PATH = path10.resolve(__dirname3, "..", "src", "prompts.ts");
387
- PLANNER_PATH = path10.resolve(__dirname3, "..", "src", "planner.ts");
388
- }
389
- });
390
-
391
321
  // ../../apps/cli/src/index.ts
392
322
  import { program as program2 } from "commander";
393
323
 
@@ -398,10 +328,10 @@ import { Command } from "commander";
398
328
  import { program } from "commander";
399
329
  function getApiUrl() {
400
330
  const opts = program.opts();
401
- return opts.api ?? process.env.HARNESS_API_URL ?? "http://localhost:4000";
331
+ return opts.api ?? "http://localhost:4000";
402
332
  }
403
- async function apiFetch(path12, init) {
404
- const url = `${getApiUrl()}${path12}`;
333
+ async function apiFetch(path10, init) {
334
+ const url = `${getApiUrl()}${path10}`;
405
335
  const res = await fetch(url, init);
406
336
  const data = await res.json().catch(() => ({}));
407
337
  if (!res.ok) {
@@ -516,7 +446,7 @@ var taskFeedCmd = new Command2("feed").description("Feed a task into the harness
516
446
  // ../../apps/cli/src/commands/task.ts
517
447
  var taskCmd = new Command3("task").description("Manage tasks");
518
448
  taskCmd.command("create").description("Create a task").requiredOption("--project <id>", "project id").requiredOption("--title <title>", "task title").option("--description <text>", "task description").option("--complexity <level>", "simple | medium | complex", "simple").option("--tags <tags>", "comma-separated tags").action(async (opts) => {
519
- const task2 = await apiFetch("/tasks", {
449
+ const task = await apiFetch("/tasks", {
520
450
  method: "POST",
521
451
  headers: { "Content-Type": "application/json" },
522
452
  body: JSON.stringify({
@@ -527,7 +457,7 @@ taskCmd.command("create").description("Create a task").requiredOption("--project
527
457
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : []
528
458
  })
529
459
  });
530
- console.log(JSON.stringify(task2, null, 2));
460
+ console.log(JSON.stringify(task, null, 2));
531
461
  });
532
462
  taskCmd.command("list").description("List tasks").option("--project <id>", "filter by project id").action(async (opts) => {
533
463
  const query = opts.project ? `?projectId=${opts.project}` : "";
@@ -542,17 +472,14 @@ taskCmd.addCommand(taskFeedCmd);
542
472
 
543
473
  // ../../apps/cli/src/commands/ui.ts
544
474
  import { Command as Command4 } from "commander";
545
- import path3 from "path";
546
- import open from "open";
547
- import { render } from "ink";
548
- import React2 from "react";
549
-
550
- // ../../apps/cli/src/lib/server.ts
551
475
  import { spawn } from "child_process";
552
476
  import { existsSync as existsSync2 } from "fs";
553
477
  import net from "net";
554
478
  import path2 from "path";
555
479
  import { fileURLToPath as fileURLToPath2 } from "url";
480
+ import open from "open";
481
+ import { render } from "ink";
482
+ import React2 from "react";
556
483
  var __filename2 = fileURLToPath2(import.meta.url);
557
484
  var __dirname2 = path2.dirname(__filename2);
558
485
  var DEFAULT_PORT = 4e3;
@@ -562,7 +489,7 @@ var MAX_GRPC_PORT = 50061;
562
489
  function findAvailablePort(preferred = DEFAULT_PORT, max = MAX_PORT) {
563
490
  return new Promise((resolve, reject) => {
564
491
  if (preferred > max) {
565
- reject(new Error(`No available ports between ${preferred.toString()} and ${max.toString()}`));
492
+ reject(new Error(`No available ports between ${DEFAULT_PORT.toString()} and ${max.toString()}`));
566
493
  return;
567
494
  }
568
495
  const tester = net.createServer();
@@ -584,11 +511,11 @@ function findAvailablePort(preferred = DEFAULT_PORT, max = MAX_PORT) {
584
511
  });
585
512
  }
586
513
  function startBundledServer(env) {
587
- const realServerPath = path2.resolve(__dirname2, "../../server/dist/index.js");
514
+ const realServerPath = path2.resolve(__dirname2, "server/dist/index.js");
588
515
  if (existsSync2(realServerPath)) {
589
516
  return spawn(process.execPath, [realServerPath], { stdio: "inherit", env });
590
517
  }
591
- const legacyServerPath = path2.resolve(__dirname2, "../../server.js");
518
+ const legacyServerPath = path2.resolve(__dirname2, "server.js");
592
519
  if (existsSync2(legacyServerPath)) {
593
520
  return spawn(process.execPath, [legacyServerPath], { stdio: "inherit", env });
594
521
  }
@@ -610,29 +537,9 @@ async function waitForApi(apiUrl, maxMs = 3e4) {
610
537
  }
611
538
  throw new Error("Server did not become ready in time");
612
539
  }
613
- async function startHarnessServer(env, options = {}) {
614
- const port = await findAvailablePort(options.preferredPort ?? DEFAULT_PORT, MAX_PORT);
615
- const grpcPort = await findAvailablePort(options.preferredGrpcPort ?? DEFAULT_GRPC_PORT, MAX_GRPC_PORT);
616
- const apiUrl = `http://localhost:${port.toString()}`;
617
- const serverEnv = {
618
- ...env,
619
- PORT: port.toString(),
620
- GRPC_PORT: grpcPort.toString(),
621
- HARNESS_API_URL: apiUrl
622
- };
623
- const server = startBundledServer(serverEnv) ?? spawn("pnpm", ["--filter", "@omega/server", "dev"], {
624
- stdio: "inherit",
625
- shell: true,
626
- env: serverEnv
627
- });
628
- await waitForApi(apiUrl);
629
- return { process: server, apiUrl, port, grpcPort };
630
- }
631
-
632
- // ../../apps/cli/src/commands/ui.ts
633
540
  async function ensureProject(apiUrl) {
634
541
  const cwd = process.cwd();
635
- const name = path3.basename(cwd);
542
+ const name = path2.basename(cwd);
636
543
  try {
637
544
  const listRes = await fetch(`${apiUrl}/projects`);
638
545
  const projects = await listRes.json();
@@ -658,16 +565,25 @@ async function ensureProject(apiUrl) {
658
565
  var uiCmd = new Command4("ui").description("Open the harness web UI").option("--no-tui", "Do not open the terminal console alongside the web UI").action(async (options) => {
659
566
  const defaultApiUrl = process.env.HARNESS_API_URL ?? `http://localhost:${DEFAULT_PORT.toString()}`;
660
567
  let server;
661
- let serverStarted = false;
662
568
  const alreadyRunning = await isApiReady(defaultApiUrl);
663
569
  const useTui = options.tui;
664
570
  let apiUrl = defaultApiUrl;
665
571
  if (!alreadyRunning) {
666
- const started = await startHarnessServer(process.env);
667
- server = started.process;
668
- apiUrl = started.apiUrl;
669
- serverStarted = true;
572
+ const port = await findAvailablePort();
573
+ const grpcPort = await findAvailablePort(DEFAULT_GRPC_PORT, MAX_GRPC_PORT);
574
+ apiUrl = `http://localhost:${port.toString()}`;
670
575
  process.env.HARNESS_API_URL = apiUrl;
576
+ const env = {
577
+ ...process.env,
578
+ PORT: port.toString(),
579
+ GRPC_PORT: grpcPort.toString(),
580
+ HARNESS_API_URL: apiUrl
581
+ };
582
+ server = startBundledServer(env) ?? spawn("pnpm", ["--filter", "@omega/server", "dev"], {
583
+ stdio: "inherit",
584
+ shell: true,
585
+ env
586
+ });
671
587
  server.on("exit", (code) => {
672
588
  process.exit(code ?? 0);
673
589
  });
@@ -691,7 +607,7 @@ var uiCmd = new Command4("ui").description("Open the harness web UI").option("--
691
607
  const { TuiApp: TuiApp2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
692
608
  const { waitUntilExit } = render(React2.createElement(TuiApp2));
693
609
  await waitUntilExit();
694
- if (serverStarted && server) {
610
+ if (server && !alreadyRunning) {
695
611
  server.kill();
696
612
  }
697
613
  }
@@ -712,7 +628,7 @@ var consoleCmd = new Command5("console").description("Open the harness TUI conso
712
628
 
713
629
  // ../../apps/cli/src/commands/skill.ts
714
630
  import { Command as Command6 } from "commander";
715
- import path4 from "path";
631
+ import path3 from "path";
716
632
  import { fileURLToPath as fileURLToPath3 } from "url";
717
633
 
718
634
  // ../skills/src/parse.ts
@@ -779,8 +695,7 @@ export class ${className} {
779
695
 
780
696
  async execute(args: Record<string, unknown>): Promise<string> {
781
697
  const prompt = ${instructionsLiteral} + '\\n\\nArgs:\\n' + JSON.stringify(args, null, 2);
782
- const response = await this.provider.send(prompt, { system: ${descriptionLiteral} });
783
- return response.content;
698
+ return this.provider.send(prompt, { system: ${descriptionLiteral} });
784
699
  }
785
700
  }
786
701
  `;
@@ -815,22 +730,22 @@ async function registerSkill(sourcePath, outputDir) {
815
730
  // ../../apps/cli/src/commands/skill.ts
816
731
  function getOutputDir() {
817
732
  if (process.env.OMEGA_HARNESS_ROOT) {
818
- return path4.join(path4.resolve(process.env.OMEGA_HARNESS_ROOT), "packages/skills/src");
733
+ return path3.join(path3.resolve(process.env.OMEGA_HARNESS_ROOT), "packages/skills/src");
819
734
  }
820
735
  try {
821
736
  const __filename3 = fileURLToPath3(import.meta.url);
822
- const __dirname4 = path4.dirname(__filename3);
823
- return path4.join(path4.resolve(__dirname4, "../../../.."), "packages/skills/src");
737
+ const __dirname3 = path3.dirname(__filename3);
738
+ return path3.join(path3.resolve(__dirname3, "../../../.."), "packages/skills/src");
824
739
  } catch {
825
- return path4.join(process.cwd(), "harness-skills");
740
+ return path3.join(process.cwd(), "harness-skills");
826
741
  }
827
742
  }
828
743
  var skillCmd = new Command6("skill").description("Manage skill artifacts");
829
744
  skillCmd.command("generate").description("Generate a harness adapter from a SKILL.md file").argument("<source>", "path to SKILL.md").action(async (source) => {
830
745
  const outputDir = getOutputDir();
831
- const manifest = await registerSkill(path4.resolve(source), outputDir);
746
+ const manifest = await registerSkill(path3.resolve(source), outputDir);
832
747
  console.log(`Generated adapter for skill: ${manifest.name}`);
833
- console.log(`Output directory: ${path4.join(outputDir, "generated")}`);
748
+ console.log(`Output directory: ${path3.join(outputDir, "generated")}`);
834
749
  });
835
750
 
836
751
  // ../../apps/cli/src/commands/agent.ts
@@ -840,7 +755,7 @@ agentCmd.command("run").description("Run a single agent task").requiredOption("-
840
755
  async (opts) => {
841
756
  const tags = ["agent"];
842
757
  if (opts.autoPublish) tags.push("publish");
843
- const task2 = await apiFetch("/tasks", {
758
+ const task = await apiFetch("/tasks", {
844
759
  method: "POST",
845
760
  headers: { "Content-Type": "application/json" },
846
761
  body: JSON.stringify({
@@ -851,8 +766,8 @@ agentCmd.command("run").description("Run a single agent task").requiredOption("-
851
766
  tags
852
767
  })
853
768
  });
854
- console.log(JSON.stringify(task2, null, 2));
855
- const result = await apiFetch(`/tasks/${task2.id}/run`, { method: "POST" });
769
+ console.log(JSON.stringify(task, null, 2));
770
+ const result = await apiFetch(`/tasks/${task.id}/run`, { method: "POST" });
856
771
  console.log(JSON.stringify(result, null, 2));
857
772
  }
858
773
  );
@@ -864,7 +779,7 @@ agentCmd.command("loop").description("Continuously run agent tasks").requiredOpt
864
779
  const title = opts.prompt ?? "Improve the Omega harness: fix a TODO, add a test, refactor a function, or improve documentation";
865
780
  const tags = ["agent", "self-improve"];
866
781
  if (opts.autoPublish) tags.push("publish");
867
- const task2 = await apiFetch("/tasks", {
782
+ const task = await apiFetch("/tasks", {
868
783
  method: "POST",
869
784
  headers: { "Content-Type": "application/json" },
870
785
  body: JSON.stringify({
@@ -875,8 +790,8 @@ agentCmd.command("loop").description("Continuously run agent tasks").requiredOpt
875
790
  tags
876
791
  })
877
792
  });
878
- console.log(`Created task ${task2.id}`);
879
- const result = await apiFetch(`/tasks/${task2.id}/run`, { method: "POST" });
793
+ console.log(`Created task ${task.id}`);
794
+ const result = await apiFetch(`/tasks/${task.id}/run`, { method: "POST" });
880
795
  console.log(JSON.stringify(result, null, 2));
881
796
  return result;
882
797
  };
@@ -904,9 +819,7 @@ agentCmd.command("diffs").description("Show diffs for a task").argument("<id>",
904
819
 
905
820
  // ../../apps/cli/src/commands/bench.ts
906
821
  import { Command as Command8 } from "commander";
907
- import fs8 from "node:fs/promises";
908
- import path11 from "node:path";
909
- import os from "node:os";
822
+ import path9 from "node:path";
910
823
 
911
824
  // ../bench/dist/api-client.js
912
825
  var ApiError = class extends Error {
@@ -923,15 +836,15 @@ async function apiFetch2(url, init) {
923
836
  }
924
837
  return res.json();
925
838
  }
926
- async function ensureProject2(apiUrl, name, path12) {
839
+ async function ensureProject2(apiUrl, name, path10) {
927
840
  const res = await fetch(`${apiUrl}/projects`, {
928
841
  method: "POST",
929
842
  headers: { "Content-Type": "application/json" },
930
- body: JSON.stringify({ name, path: path12 })
843
+ body: JSON.stringify({ name, path: path10 })
931
844
  });
932
845
  if (res.status === 409) {
933
- const projects = await fetch(`${apiUrl}/projects?path=${encodeURIComponent(path12)}`).then((r) => r.json());
934
- const existing = projects.find((p) => p.path === path12);
846
+ const projects = await fetch(`${apiUrl}/projects?path=${encodeURIComponent(path10)}`).then((r) => r.json());
847
+ const existing = projects.find((p) => p.path === path10);
935
848
  if (existing)
936
849
  return existing;
937
850
  }
@@ -948,9 +861,7 @@ async function createTask(apiUrl, projectId, title, options = {}) {
948
861
  title,
949
862
  description: options.description,
950
863
  complexity: options.complexity ?? "simple",
951
- tags: options.tags ?? ["benchmark"],
952
- provider: options.provider,
953
- model: options.model
864
+ tags: options.tags ?? ["benchmark"]
954
865
  })
955
866
  });
956
867
  }
@@ -963,9 +874,9 @@ async function getTask(apiUrl, taskId) {
963
874
  async function waitForTask(apiUrl, taskId, timeoutMs = 12e4) {
964
875
  const deadline = Date.now() + timeoutMs;
965
876
  while (Date.now() < deadline) {
966
- const task2 = await getTask(apiUrl, taskId);
967
- if (task2.status === "done" || task2.status === "failed")
968
- return task2;
877
+ const task = await getTask(apiUrl, taskId);
878
+ if (task.status === "done" || task.status === "failed")
879
+ return task;
969
880
  await new Promise((r) => setTimeout(r, 300));
970
881
  }
971
882
  return { status: "timeout", error: "Task did not finish in time" };
@@ -1003,169 +914,8 @@ function countSpans(node) {
1003
914
 
1004
915
  // ../bench/dist/runner.js
1005
916
  import fs from "node:fs/promises";
1006
- import path5 from "node:path";
917
+ import path4 from "node:path";
1007
918
  import { execSync } from "node:child_process";
1008
-
1009
- // ../bench/dist/analyse.js
1010
- function walkSpans(traceFlow, callback) {
1011
- if (!traceFlow)
1012
- return;
1013
- function visit(span) {
1014
- callback?.(span);
1015
- for (const child of span.children) {
1016
- visit(child);
1017
- }
1018
- }
1019
- for (const span of traceFlow.spans) {
1020
- visit(span);
1021
- }
1022
- }
1023
- function findSpans(traceFlow, predicate) {
1024
- const matches = [];
1025
- walkSpans(traceFlow, (span) => {
1026
- if (predicate(span))
1027
- matches.push(span);
1028
- });
1029
- return matches;
1030
- }
1031
- function spanError(span) {
1032
- if (span.status !== "error")
1033
- return void 0;
1034
- const err = span.attributes?.error;
1035
- return typeof err === "string" ? err : void 0;
1036
- }
1037
- function toolSpans(traceFlow) {
1038
- return findSpans(traceFlow, (s) => s.name.startsWith("agent.tool."));
1039
- }
1040
- function providerSpans(traceFlow) {
1041
- return findSpans(traceFlow, (s) => s.name === "provider.send");
1042
- }
1043
- function planSpan(traceFlow) {
1044
- return findSpans(traceFlow, (s) => s.name === "agent.plan").shift();
1045
- }
1046
- function validationSummary(result) {
1047
- if (!result.agentRun?.validationSummary)
1048
- return void 0;
1049
- try {
1050
- return JSON.parse(result.agentRun.validationSummary);
1051
- } catch {
1052
- return void 0;
1053
- }
1054
- }
1055
- function classifyFailure(result, traceFlow) {
1056
- const evidence = [];
1057
- if (result.status === "timeout") {
1058
- return {
1059
- category: "timeout",
1060
- rootCause: "Task did not finish within the timeout window.",
1061
- evidence: [`durationMs: ${String(result.durationMs)}`]
1062
- };
1063
- }
1064
- const validation = validationSummary(result);
1065
- if (validation?.allPassed === false) {
1066
- const failedSteps = [];
1067
- for (const key of ["lint", "test", "build"]) {
1068
- const step = validation[key];
1069
- if (step && !step.success) {
1070
- failedSteps.push(key);
1071
- }
1072
- }
1073
- return {
1074
- category: "validation_failure",
1075
- rootCause: `Validation failed: ${failedSteps.length > 0 ? failedSteps.join(", ") : "unspecified step"}.`,
1076
- evidence: failedSteps.map((s) => `${s} did not pass`)
1077
- };
1078
- }
1079
- const tools = toolSpans(traceFlow);
1080
- const failedTools = tools.filter((s) => s.status === "error");
1081
- if (failedTools.length > 0) {
1082
- const first = failedTools[0];
1083
- const toolName = first.name.replace("agent.tool.", "");
1084
- const err = spanError(first) ?? "unknown error";
1085
- evidence.push(`${toolName} failed: ${err}`);
1086
- if (toolName === "edit_file" || err.includes("old_string")) {
1087
- return {
1088
- category: "tool_misuse",
1089
- rootCause: "edit_file failed because the old_string did not match. The agent may not have read the file first or copied the exact text.",
1090
- evidence
1091
- };
1092
- }
1093
- return {
1094
- category: "tool_misuse",
1095
- rootCause: `Tool ${toolName} was invoked but failed.`,
1096
- evidence
1097
- };
1098
- }
1099
- const providers = providerSpans(traceFlow);
1100
- const failedProviders = providers.filter((s) => s.status === "error");
1101
- if (failedProviders.length > 0) {
1102
- const err = spanError(failedProviders[0]) ?? "provider error";
1103
- if (err.includes("JSON") || err.includes("parse") || err.includes("Unexpected token")) {
1104
- return {
1105
- category: "parse_error",
1106
- rootCause: "Provider response could not be parsed as the expected format.",
1107
- evidence: [err]
1108
- };
1109
- }
1110
- }
1111
- const plan = planSpan(traceFlow);
1112
- if (plan) {
1113
- const planSteps = plan.attributes?.planSteps;
1114
- const planStepsCount = typeof planSteps === "number" ? planSteps : 0;
1115
- if (planStepsCount === 0) {
1116
- return {
1117
- category: "plan_error",
1118
- rootCause: "The planner produced an empty plan with no actionable steps.",
1119
- evidence: ["planSteps: 0"]
1120
- };
1121
- }
1122
- const providerTurns = providers.length;
1123
- if (providerTurns > 10 && !result.evaluation.passed) {
1124
- return {
1125
- category: "plan_error",
1126
- rootCause: "The agent took many turns without finishing; the plan may be stuck or too vague.",
1127
- evidence: [`provider turns: ${String(providerTurns)}`, `plan steps: ${String(planStepsCount)}`]
1128
- };
1129
- }
1130
- }
1131
- const message = result.evaluation.message ?? result.agentRun?.resultStatus ?? "unknown";
1132
- return {
1133
- category: "unknown",
1134
- rootCause: `Failure cause could not be classified: ${message}.`,
1135
- evidence: [message]
1136
- };
1137
- }
1138
- function pickFocusResult(results, traceFlows) {
1139
- const failed = results.filter((r) => !r.evaluation.passed);
1140
- if (failed.length === 0)
1141
- return void 0;
1142
- const seenCategories = /* @__PURE__ */ new Set();
1143
- for (const result of failed) {
1144
- const analysis = result.failureAnalysis ?? classifyFailure(result, traceFlows.get(result.harnessTaskId));
1145
- if (!seenCategories.has(analysis.category)) {
1146
- seenCategories.add(analysis.category);
1147
- return result;
1148
- }
1149
- }
1150
- return failed[0];
1151
- }
1152
- function summariseFailures(results, traceFlows) {
1153
- const counts = /* @__PURE__ */ new Map();
1154
- for (const result of results) {
1155
- if (result.evaluation.passed)
1156
- continue;
1157
- const analysis = result.failureAnalysis ?? classifyFailure(result, traceFlows.get(result.harnessTaskId));
1158
- const entry = counts.get(analysis.category) ?? { count: 0, examples: [] };
1159
- entry.count++;
1160
- if (entry.examples.length < 3) {
1161
- entry.examples.push(`${result.task.name}: ${analysis.rootCause}`);
1162
- }
1163
- counts.set(analysis.category, entry);
1164
- }
1165
- return Array.from(counts.entries()).map(([category, data]) => ({ category, ...data })).sort((a, b) => b.count - a.count);
1166
- }
1167
-
1168
- // ../bench/dist/runner.js
1169
919
  function countAllSpans(traceFlow) {
1170
920
  if (!traceFlow)
1171
921
  return 0;
@@ -1183,15 +933,8 @@ function ensureGitRepo(repoPath) {
1183
933
  execSync("git add .", { cwd: repoPath, stdio: "ignore" });
1184
934
  execSync('git commit -m "bench init"', { cwd: repoPath, stdio: "ignore" });
1185
935
  }
1186
- function sumUsage(a, b) {
1187
- return {
1188
- promptTokens: (a.promptTokens ?? 0) + (b?.promptTokens ?? 0),
1189
- completionTokens: (a.completionTokens ?? 0) + (b?.completionTokens ?? 0),
1190
- totalTokens: (a.totalTokens ?? 0) + (b?.totalTokens ?? 0)
1191
- };
1192
- }
1193
936
  async function runBenchmark(tasks, options) {
1194
- const { apiUrl, suiteName, timeoutMs = 12e4, projectPrefix = "bench" } = options;
937
+ const { apiUrl, suiteName, timeoutMs = 12e4, projectPrefix = "bench", provider, model } = options;
1195
938
  const report = {
1196
939
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1197
940
  suite: suiteName,
@@ -1200,12 +943,11 @@ async function runBenchmark(tasks, options) {
1200
943
  failed: 0,
1201
944
  timeouts: 0,
1202
945
  totalDurationMs: 0,
1203
- totalUsage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
1204
946
  results: []
1205
947
  };
1206
- const baseDir = path5.join("/tmp", `omega-bench-${String(Date.now())}`);
948
+ const baseDir = path4.join("/tmp", `omega-bench-${String(Date.now())}`);
1207
949
  await fs.mkdir(baseDir, { recursive: true });
1208
- for (const task2 of tasks) {
950
+ for (const task of tasks) {
1209
951
  const start = Date.now();
1210
952
  let harnessTaskId = "";
1211
953
  let status = "failed";
@@ -1216,20 +958,27 @@ async function runBenchmark(tasks, options) {
1216
958
  let projectId = "";
1217
959
  let projectPath = "";
1218
960
  try {
1219
- projectPath = path5.join(baseDir, task2.id);
961
+ projectPath = path4.join(baseDir, task.id);
1220
962
  await fs.mkdir(projectPath, { recursive: true });
1221
- if (task2.setup) {
1222
- await task2.setup(projectPath);
963
+ if (task.setup) {
964
+ await task.setup(projectPath);
1223
965
  }
1224
966
  ensureGitRepo(projectPath);
1225
- const project = await ensureProject2(apiUrl, `${projectPrefix}-${task2.id}`, projectPath);
967
+ const project = await ensureProject2(apiUrl, `${projectPrefix}-${task.id}`, projectPath);
1226
968
  projectId = project.id;
1227
- const harnessTask = await createTask(apiUrl, project.id, task2.title, {
1228
- description: task2.description,
1229
- complexity: task2.complexity ?? "simple",
969
+ const harnessTask = await createTask(apiUrl, project.id, task.title, {
970
+ description: task.description,
971
+ complexity: task.complexity ?? "simple",
1230
972
  tags: ["benchmark", "agent"]
1231
973
  });
1232
974
  harnessTaskId = harnessTask.id;
975
+ if (provider || model) {
976
+ await fetch(`${apiUrl}/tasks/${harnessTaskId}`, {
977
+ method: "PATCH",
978
+ headers: { "Content-Type": "application/json" },
979
+ body: JSON.stringify({ provider, model })
980
+ });
981
+ }
1233
982
  await runTask(apiUrl, harnessTask.id);
1234
983
  const finished = await waitForTask(apiUrl, harnessTask.id, timeoutMs);
1235
984
  status = finished.status === "timeout" ? "timeout" : finished.status;
@@ -1238,7 +987,7 @@ async function runBenchmark(tasks, options) {
1238
987
  getDiffs(apiUrl, harnessTask.id),
1239
988
  getTraceFlow(apiUrl, harnessTask.id)
1240
989
  ]);
1241
- evaluation = await task2.evaluate({
990
+ evaluation = await task.evaluate({
1242
991
  apiUrl,
1243
992
  taskId: harnessTask.id,
1244
993
  projectPath,
@@ -1254,21 +1003,14 @@ async function runBenchmark(tasks, options) {
1254
1003
  };
1255
1004
  }
1256
1005
  const durationMs = Date.now() - start;
1257
- const usage = agentRun ? {
1258
- promptTokens: agentRun.promptTokens,
1259
- completionTokens: agentRun.completionTokens,
1260
- totalTokens: agentRun.totalTokens
1261
- } : void 0;
1262
1006
  const result = {
1263
- task: task2,
1007
+ task,
1264
1008
  harnessTaskId,
1265
1009
  durationMs,
1266
1010
  status,
1267
1011
  evaluation,
1268
1012
  agentRun,
1269
- spanCount: countAllSpans(traceFlow),
1270
- usage,
1271
- failureAnalysis: evaluation.passed ? void 0 : classifyFailure({ task: task2, harnessTaskId, durationMs, status, evaluation, agentRun, spanCount: countAllSpans(traceFlow), usage }, traceFlow)
1013
+ spanCount: countAllSpans(traceFlow)
1272
1014
  };
1273
1015
  if (status === "timeout")
1274
1016
  report.timeouts++;
@@ -1277,7 +1019,6 @@ async function runBenchmark(tasks, options) {
1277
1019
  else
1278
1020
  report.failed++;
1279
1021
  report.totalDurationMs += durationMs;
1280
- report.totalUsage = sumUsage(report.totalUsage, usage);
1281
1022
  report.results.push(result);
1282
1023
  options.onProgress?.(result);
1283
1024
  }
@@ -1286,7 +1027,7 @@ async function runBenchmark(tasks, options) {
1286
1027
 
1287
1028
  // ../bench/dist/report.js
1288
1029
  import fs2 from "node:fs/promises";
1289
- import path6 from "node:path";
1030
+ import path5 from "node:path";
1290
1031
  function nowIso() {
1291
1032
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1292
1033
  }
@@ -1299,21 +1040,16 @@ function resultLine(r, idx) {
1299
1040
  const symbol = r.evaluation.passed ? "\u2713" : "\u2717";
1300
1041
  const status = r.status === "timeout" ? "timeout" : r.status;
1301
1042
  const score = r.evaluation.score !== void 0 ? ` (score ${String(r.evaluation.score)})` : "";
1302
- const tokens = r.usage?.totalTokens !== void 0 ? ` (${String(r.usage.totalTokens)} tokens)` : "";
1303
- const category = r.failureAnalysis ? ` [${r.failureAnalysis.category}]` : "";
1304
1043
  const msg = r.evaluation.message ? ` \u2014 ${r.evaluation.message}` : "";
1305
- return `${String(idx + 1)}. ${symbol} ${r.task.name} [${status}]${category} ${formatDuration(r.durationMs)}${tokens}${score}${msg}`;
1306
- }
1307
- function usageLine(usage) {
1308
- return `Tokens: ${String(usage.totalTokens ?? 0)} total (${String(usage.promptTokens ?? 0)} prompt, ${String(usage.completionTokens ?? 0)} completion)`;
1044
+ return `${String(idx + 1)}. ${symbol} ${r.task.name} [${status}] ${formatDuration(r.durationMs)}${score}${msg}`;
1309
1045
  }
1310
1046
  async function writeReport(report, outputDir = ".omega/reports") {
1311
1047
  await fs2.mkdir(outputDir, { recursive: true });
1312
1048
  const ts = nowIso();
1313
- const jsonFile2 = path6.join(outputDir, `benchmark-${ts}.json`);
1314
- const mdFile = path6.join(outputDir, `benchmark-${ts}.md`);
1315
- await fs2.writeFile(jsonFile2, JSON.stringify(report, null, 2), "utf-8");
1316
- const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1049
+ const jsonFile = path5.join(outputDir, `benchmark-${ts}.json`);
1050
+ const mdFile = path5.join(outputDir, `benchmark-${ts}.md`);
1051
+ 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;
1317
1053
  const md = [
1318
1054
  "# Omega Benchmark Report",
1319
1055
  "",
@@ -1323,9 +1059,8 @@ async function writeReport(report, outputDir = ".omega/reports") {
1323
1059
  `- Passed: ${String(report.passed)}`,
1324
1060
  `- Failed: ${String(report.failed)}`,
1325
1061
  `- Timeouts: ${String(report.timeouts)}`,
1326
- `- Pass rate: ${String(passRate2)}%`,
1062
+ `- Pass rate: ${String(passRate)}%`,
1327
1063
  `- Total duration: ${formatDuration(report.totalDurationMs)}`,
1328
- `- ${usageLine(report.totalUsage)}`,
1329
1064
  "",
1330
1065
  "## Results",
1331
1066
  "",
@@ -1339,69 +1074,31 @@ async function writeReport(report, outputDir = ".omega/reports") {
1339
1074
  ""
1340
1075
  ].join("\n");
1341
1076
  await fs2.writeFile(mdFile, md, "utf-8");
1342
- return jsonFile2;
1077
+ return jsonFile;
1343
1078
  }
1344
1079
  function printSummary(report) {
1345
- const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1080
+ const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1346
1081
  console.log(`
1347
1082
  Benchmark: ${report.suite}`);
1348
1083
  console.log(`Total: ${String(report.total)} | Passed: ${String(report.passed)} | Failed: ${String(report.failed)} | Timeouts: ${String(report.timeouts)}`);
1349
- console.log(`Pass rate: ${String(passRate2)}%`);
1084
+ console.log(`Pass rate: ${String(passRate)}%`);
1350
1085
  console.log(`Duration: ${formatDuration(report.totalDurationMs)}`);
1351
- console.log(usageLine(report.totalUsage));
1352
1086
  for (const r of report.results) {
1353
1087
  console.log(resultLine(r, report.results.indexOf(r)));
1354
1088
  }
1355
1089
  }
1356
1090
 
1357
1091
  // ../bench/dist/suites/synthetic.js
1358
- import fs4 from "node:fs/promises";
1359
-
1360
- // ../bench/dist/suites/builder.js
1361
1092
  import fs3 from "node:fs/promises";
1362
- import path7 from "node:path";
1363
- function task(spec) {
1364
- return {
1365
- id: spec.id,
1366
- name: spec.name,
1367
- title: spec.title,
1368
- description: spec.description,
1369
- complexity: spec.complexity ?? "simple",
1370
- setup: spec.setup,
1371
- evaluate: spec.evaluate ?? expectValidationPassed()
1372
- };
1373
- }
1374
- function basePackage(extraScripts = {}) {
1375
- return {
1376
- name: "synthetic-bench-project",
1377
- version: "1.0.0",
1378
- type: "module",
1379
- scripts: {
1380
- lint: "node lint.js",
1381
- test: "node test.js",
1382
- build: "node build.js",
1383
- ...extraScripts
1384
- }
1385
- };
1386
- }
1387
- async function jsonFile(projectPath, file, data) {
1388
- await fs3.mkdir(path7.dirname(path7.join(projectPath, file)), { recursive: true });
1389
- await fs3.writeFile(path7.join(projectPath, file), JSON.stringify(data, null, 2), "utf-8");
1390
- }
1391
- async function codeFile(projectPath, file, content) {
1392
- await fs3.mkdir(path7.dirname(path7.join(projectPath, file)), { recursive: true });
1393
- await fs3.writeFile(path7.join(projectPath, file), content, "utf-8");
1093
+ import path6 from "node:path";
1094
+ async function writeJson(projectPath, file, data) {
1095
+ await fs3.writeFile(path6.join(projectPath, file), JSON.stringify(data, null, 2), "utf-8");
1394
1096
  }
1395
- async function nodeProject(projectPath, extraScripts = {}) {
1396
- await jsonFile(projectPath, "package.json", basePackage(extraScripts));
1397
- await codeFile(projectPath, "lint.js", `console.log('lint ok');
1398
- `);
1399
- await codeFile(projectPath, "test.js", `console.log('test ok');
1400
- `);
1401
- await codeFile(projectPath, "build.js", `console.log('build ok');
1402
- `);
1097
+ async function writeFile2(projectPath, file, content) {
1098
+ await fs3.mkdir(path6.dirname(path6.join(projectPath, file)), { recursive: true });
1099
+ await fs3.writeFile(path6.join(projectPath, file), content, "utf-8");
1403
1100
  }
1404
- function readValidation(ctx) {
1101
+ function parseValidation(ctx) {
1405
1102
  if (!ctx.agentRun?.validationSummary)
1406
1103
  return { allPassed: false };
1407
1104
  try {
@@ -1411,598 +1108,142 @@ function readValidation(ctx) {
1411
1108
  return { allPassed: false };
1412
1109
  }
1413
1110
  }
1414
- function expectValidationPassed() {
1415
- return (ctx) => {
1416
- const v = readValidation(ctx);
1417
- return {
1418
- passed: v.allPassed,
1419
- message: v.allPassed ? "Validation passed" : "Validation failed"
1420
- };
1421
- };
1422
- }
1423
- async function readFile2(projectPath, file) {
1424
- return fs3.readFile(path7.join(projectPath, file), "utf-8");
1425
- }
1426
- function expectFileContains(file, needle) {
1427
- return async (ctx) => {
1428
- const content = await readFile2(ctx.projectPath, file).catch(() => "");
1429
- const found = typeof needle === "string" ? content.includes(needle) : needle.test(content);
1430
- return {
1431
- passed: found,
1432
- message: found ? `${file} contains required content` : `${file} missing required content`
1433
- };
1111
+ function validate(ctx) {
1112
+ const v = parseValidation(ctx);
1113
+ return {
1114
+ passed: v.allPassed,
1115
+ message: v.allPassed ? "Validation passed" : "Validation failed"
1434
1116
  };
1435
1117
  }
1436
- function combined(...evaluators) {
1437
- return async (ctx) => {
1438
- for (const evaluator of evaluators) {
1439
- const result = await evaluator(ctx);
1440
- if (!result.passed)
1441
- return result;
1118
+ function basePackage() {
1119
+ return {
1120
+ name: "synthetic-bench-project",
1121
+ version: "1.0.0",
1122
+ type: "module",
1123
+ scripts: {
1124
+ lint: "node lint.js",
1125
+ test: "node test.js",
1126
+ build: "node build.js"
1442
1127
  }
1443
- return { passed: true, message: "All checks passed" };
1444
1128
  };
1445
1129
  }
1446
-
1447
- // ../bench/dist/suites/synthetic.js
1448
- var lintAlwaysPass = `console.log('lint ok');
1449
- `;
1450
- var buildAlwaysPass = `console.log('build ok');
1451
- `;
1452
- function jsTest(name, body) {
1453
- return `import assert from 'node:assert';
1454
- ${body}
1455
- console.log('${name} test passed');
1456
- `;
1457
- }
1458
- function syntheticSuite() {
1459
- return [
1460
- // Simple edits
1461
- task({
1462
- id: "greet-function",
1463
- name: "Add a greet function",
1464
- title: "Add greet.js with a greet function",
1465
- description: "Create a `greet.js` file that exports a function `greet(name)` returning `Hello, <name>!`.",
1466
- complexity: "simple",
1467
- setup: async (projectPath) => {
1468
- await nodeProject(projectPath);
1469
- await codeFile(projectPath, "test.js", jsTest("greet", `import { greet } from './greet.js';
1470
- assert.strictEqual(greet('World'), 'Hello, World!');`));
1471
- }
1472
- }),
1473
- task({
1474
- id: "update-config",
1475
- name: "Update a config value",
1476
- title: "Update timeout value in config.json",
1477
- description: "Change `config.json` so that `timeout` is `5000` instead of `1000`.",
1478
- complexity: "simple",
1479
- setup: async (projectPath) => {
1480
- await nodeProject(projectPath);
1481
- await jsonFile(projectPath, "config.json", { timeout: 1e3, retries: 3 });
1482
- await codeFile(projectPath, "test.js", `import { readFile } from 'node:fs/promises';
1483
- const cfg = JSON.parse(await readFile('./config.json', 'utf-8'));
1484
- if (cfg.timeout !== 5000) { console.error('timeout is ' + cfg.timeout); process.exit(1); }
1485
- console.log('config test passed');
1130
+ var TASKS = [
1131
+ {
1132
+ id: "greet-function",
1133
+ name: "Add a greet function",
1134
+ title: "Add greet.js with a greet function",
1135
+ description: "Create a `greet.js` file that exports a function `greet(name)` returning `Hello, <name>!`. The existing test expects this function.",
1136
+ complexity: "simple",
1137
+ setup: async (projectPath) => {
1138
+ await writeJson(projectPath, "package.json", basePackage());
1139
+ await writeFile2(projectPath, "test.js", `import { greet } from './greet.js';
1140
+ import assert from 'node:assert';
1141
+ assert.strictEqual(greet('World'), 'Hello, World!');
1142
+ console.log('greet test passed');
1486
1143
  `);
1487
- }
1488
- }),
1489
- task({
1490
- id: "add-readme",
1491
- name: "Add a README",
1492
- title: "Create README.md with a usage heading",
1493
- description: "Create a README.md file that contains a `# Usage` heading.",
1494
- complexity: "simple",
1495
- setup: async (projectPath) => {
1496
- await nodeProject(projectPath);
1497
- await codeFile(projectPath, "test.js", `import { readFile } from 'node:fs/promises';
1498
- const readme = await readFile('./README.md', 'utf-8').catch(() => '');
1499
- if (!readme.includes('# Usage')) { console.error('README missing Usage heading'); process.exit(1); }
1500
- console.log('readme test passed');
1144
+ await writeFile2(projectPath, "lint.js", `console.log('lint ok');
1501
1145
  `);
1502
- },
1503
- evaluate: expectFileContains("README.md", "# Usage")
1504
- }),
1505
- task({
1506
- id: "add-npm-script",
1507
- name: "Add an npm script",
1508
- title: "Add a `hello` script to package.json",
1509
- description: 'Add a `hello` script to package.json that prints "hello world".',
1510
- complexity: "simple",
1511
- setup: async (projectPath) => {
1512
- await nodeProject(projectPath);
1513
- await codeFile(projectPath, "test.js", `import pkg from './package.json' assert { type: 'json' };
1514
- if (pkg.scripts?.hello !== 'echo hello world') { console.error('missing hello script'); process.exit(1); }
1515
- console.log('script test passed');
1146
+ await writeFile2(projectPath, "build.js", `console.log('build ok');
1516
1147
  `);
1517
- }
1518
- }),
1519
- task({
1520
- id: "fix-typo",
1521
- name: "Fix a typo",
1522
- title: 'Fix spelling of "recieve" to "receive"',
1523
- description: 'The file src/message.js contains "recieve". Change it to "receive".',
1524
- complexity: "simple",
1525
- setup: async (projectPath) => {
1526
- await nodeProject(projectPath);
1527
- await codeFile(projectPath, "src/message.js", `export const message = 'Please recieve this letter';
1528
- `);
1529
- await codeFile(projectPath, "test.js", `import { readFile } from 'node:fs/promises';
1530
- const code = await readFile('./src/message.js', 'utf-8');
1531
- if (code.includes('recieve')) { console.error('typo remains'); process.exit(1); }
1532
- if (!code.includes('receive')) { console.error('correct spelling missing'); process.exit(1); }
1533
- console.log('typo test passed');
1534
- `);
1535
- }
1536
- }),
1537
- task({
1538
- id: "add-gitignore",
1539
- name: "Add a .gitignore",
1540
- title: "Ignore node_modules and .env",
1541
- description: "Create a .gitignore file that ignores node_modules and .env.",
1542
- complexity: "simple",
1543
- setup: async (projectPath) => {
1544
- await nodeProject(projectPath);
1545
- await codeFile(projectPath, "test.js", `import { readFile } from 'node:fs/promises';
1546
- const gitignore = await readFile('./.gitignore', 'utf-8').catch(() => '');
1547
- if (!gitignore.includes('node_modules') || !gitignore.includes('.env')) { console.error('.gitignore incomplete'); process.exit(1); }
1548
- console.log('gitignore test passed');
1549
- `);
1550
- }
1551
- }),
1552
- // Refactoring
1553
- task({
1554
- id: "extract-constant",
1555
- name: "Extract a magic constant",
1556
- title: "Extract MAX_RETRIES constant",
1557
- description: "Refactor `src/retry.js` so the magic number `3` is replaced by a top-level constant named `MAX_RETRIES`.",
1558
- complexity: "medium",
1559
- setup: async (projectPath) => {
1560
- await nodeProject(projectPath);
1561
- await codeFile(projectPath, "src/retry.js", `export function attempts() { return 3; }
1562
- `);
1563
- await codeFile(projectPath, "test.js", jsTest("constant", `import fs from 'node:fs/promises';
1564
- import { attempts } from './src/retry.js';
1565
- const code = await fs.readFile('./src/retry.js', 'utf-8');
1566
- if (!code.includes('const MAX_RETRIES')) { console.error('MAX_RETRIES constant not found'); process.exit(1); }
1567
- if (attempts() !== 3) { console.error('attempts() changed'); process.exit(1); }`));
1568
- }
1569
- }),
1570
- task({
1571
- id: "rename-variable",
1572
- name: "Rename a variable",
1573
- title: "Rename `usr` to `user` in src/index.js",
1574
- description: "Rename all occurrences of the variable `usr` to `user` in src/index.js while preserving behavior.",
1575
- complexity: "simple",
1576
- setup: async (projectPath) => {
1577
- await nodeProject(projectPath);
1578
- await codeFile(projectPath, "src/index.js", `export function welcome(usr) { return 'Welcome, ' + usr; }
1579
- `);
1580
- await codeFile(projectPath, "test.js", jsTest("rename", `import { readFile } from 'node:fs/promises';
1581
- import { welcome } from './src/index.js';
1582
- const code = await readFile('./src/index.js', 'utf-8');
1583
- if (code.includes('usr')) { console.error('old name remains'); process.exit(1); }
1584
- if (!code.includes('user')) { console.error('new name missing'); process.exit(1); }
1585
- assert.strictEqual(welcome('Ada'), 'Welcome, Ada');`));
1586
- }
1587
- }),
1588
- task({
1589
- id: "remove-dead-code",
1590
- name: "Remove dead code",
1591
- title: "Remove unused helper function",
1592
- description: "Remove the unused `unusedHelper` function from src/utils.js.",
1593
- complexity: "simple",
1594
- setup: async (projectPath) => {
1595
- await nodeProject(projectPath);
1596
- await codeFile(projectPath, "src/utils.js", `export function usedHelper() { return 1; }
1597
- function unusedHelper() { return 2; }
1148
+ },
1149
+ evaluate: (ctx) => Promise.resolve(validate(ctx))
1150
+ },
1151
+ {
1152
+ id: "fix-lint-warning",
1153
+ name: "Fix a lint warning",
1154
+ title: "Remove unused variable flagged by linter",
1155
+ description: "The custom linter flags any use of `var`. Update `src/index.js` so the lint script passes while keeping the greeting behavior.",
1156
+ complexity: "simple",
1157
+ setup: async (projectPath) => {
1158
+ await writeJson(projectPath, "package.json", basePackage());
1159
+ await writeFile2(projectPath, "src/index.js", `var greeting = 'Hello';
1160
+ export function hi(name) { return greeting + ', ' + name + '!'; }
1598
1161
  `);
1599
- await codeFile(projectPath, "test.js", jsTest("dead-code", `import { readFile } from 'node:fs/promises';
1600
- import { usedHelper } from './src/utils.js';
1601
- const code = await readFile('./src/utils.js', 'utf-8');
1602
- if (code.includes('unusedHelper')) { console.error('dead code remains'); process.exit(1); }
1603
- assert.strictEqual(usedHelper(), 1);`));
1604
- }
1605
- }),
1606
- task({
1607
- id: "split-function",
1608
- name: "Split a function",
1609
- title: "Split validateUser into smaller functions",
1610
- description: "Refactor src/validate.js so there is a separate `isEmailValid` function used by `validateUser`.",
1611
- complexity: "medium",
1612
- setup: async (projectPath) => {
1613
- await nodeProject(projectPath);
1614
- await codeFile(projectPath, "src/validate.js", `export function validateUser(email) { return email.includes('@'); }
1162
+ await writeFile2(projectPath, "test.js", `import { hi } from './src/index.js';
1163
+ import assert from 'node:assert';
1164
+ assert.strictEqual(hi('Omega'), 'Hello, Omega!');
1165
+ console.log('hi test passed');
1615
1166
  `);
1616
- await codeFile(projectPath, "test.js", jsTest("split", `import { readFile } from 'node:fs/promises';
1617
- import { validateUser } from './src/validate.js';
1618
- const code = await readFile('./src/validate.js', 'utf-8');
1619
- if (!code.includes('function isEmailValid')) { console.error('isEmailValid not found'); process.exit(1); }
1620
- assert.strictEqual(validateUser('a@b.com'), true);
1621
- assert.strictEqual(validateUser('bad'), false);`));
1622
- }
1623
- }),
1624
- task({
1625
- id: "convert-var-to-const",
1626
- name: "Convert var to const",
1627
- title: "Replace var with const/let",
1628
- description: "Update src/index.js to use const and let instead of var.",
1629
- complexity: "simple",
1630
- setup: async (projectPath) => {
1631
- await nodeProject(projectPath, {
1632
- lint: "node lint.js"
1633
- });
1634
- await codeFile(projectPath, "src/index.js", `var x = 1;
1635
- var y = 2;
1636
- export function sum() { return x + y; }
1637
- `);
1638
- await codeFile(projectPath, "lint.js", `import fs from 'node:fs/promises';
1167
+ await writeFile2(projectPath, "lint.js", `import fs from 'node:fs/promises';
1639
1168
  const code = await fs.readFile('./src/index.js', 'utf-8');
1640
- if (code.includes('var ')) { console.error('var keyword found'); process.exit(1); }
1169
+ if (code.includes('var ')) { console.error('lint failed: var keyword found'); process.exit(1); }
1641
1170
  console.log('lint ok');
1642
1171
  `);
1643
- await codeFile(projectPath, "test.js", jsTest("var", `import { sum } from './src/index.js';
1644
- assert.strictEqual(sum(), 3);`));
1645
- }
1646
- }),
1647
- // Bug fixes
1648
- task({
1649
- id: "fix-null-guard",
1650
- name: "Add null guard",
1651
- title: "Guard against null input in getLength",
1652
- description: "Fix src/strings.js so getLength returns 0 for null/undefined input instead of throwing.",
1653
- complexity: "simple",
1654
- setup: async (projectPath) => {
1655
- await nodeProject(projectPath);
1656
- await codeFile(projectPath, "src/strings.js", `export function getLength(value) { return value.length; }
1657
- `);
1658
- await codeFile(projectPath, "test.js", jsTest("null-guard", `import { getLength } from './src/strings.js';
1659
- assert.strictEqual(getLength(null), 0);
1660
- assert.strictEqual(getLength(undefined), 0);
1661
- assert.strictEqual(getLength('abc'), 3);`));
1662
- }
1663
- }),
1664
- task({
1665
- id: "fix-off-by-one",
1666
- name: "Fix off-by-one",
1667
- title: "Fix range to include end value",
1668
- description: "Fix src/range.js so range(1, 3) returns [1, 2, 3].",
1669
- complexity: "simple",
1670
- setup: async (projectPath) => {
1671
- await nodeProject(projectPath);
1672
- await codeFile(projectPath, "src/range.js", `export function range(start, end) { const out = []; for (let i = start; i < end; i++) out.push(i); return out; }
1673
- `);
1674
- await codeFile(projectPath, "test.js", jsTest("range", `import { range } from './src/range.js';
1675
- assert.deepStrictEqual(range(1, 3), [1, 2, 3]);`));
1676
- }
1677
- }),
1678
- task({
1679
- id: "fix-async-await",
1680
- name: "Fix async function",
1681
- title: "Await the fetch call in loadData",
1682
- description: "Fix src/api.js so loadData awaits the fetch response before returning JSON.",
1683
- complexity: "medium",
1684
- setup: async (projectPath) => {
1685
- await nodeProject(projectPath);
1686
- await codeFile(projectPath, "src/api.js", `export async function loadData() { return fetch('data.json').then((r) => r.json()); }
1687
- `);
1688
- await codeFile(projectPath, "test.js", jsTest("async", `import { readFile } from 'node:fs/promises';
1689
- import { loadData } from './src/api.js';
1690
- const code = await readFile('./src/api.js', 'utf-8');
1691
- if (!code.includes('await')) { console.error('missing await'); process.exit(1); }
1692
- const result = await loadData();
1693
- assert.strictEqual(result.ok, true);`));
1694
- await jsonFile(projectPath, "data.json", { ok: true });
1695
- },
1696
- evaluate: combined(expectFileContains("src/api.js", "await"), expectValidationPassed())
1697
- }),
1698
- task({
1699
- id: "fix-export",
1700
- name: "Fix missing export",
1701
- title: "Export the helper function",
1702
- description: "The function helper in src/lib.js is not exported. Export it so the test can import it.",
1703
- complexity: "simple",
1704
- setup: async (projectPath) => {
1705
- await nodeProject(projectPath);
1706
- await codeFile(projectPath, "src/lib.js", `function helper() { return 42; }
1707
- `);
1708
- await codeFile(projectPath, "test.js", jsTest("export", `import { helper } from './src/lib.js';
1709
- assert.strictEqual(helper(), 42);`));
1710
- }
1711
- }),
1712
- task({
1713
- id: "fix-import-path",
1714
- name: "Fix import path",
1715
- title: "Correct the import path in src/main.js",
1716
- description: "Fix the import path in src/main.js so it points to ./helpers.js.",
1717
- complexity: "simple",
1718
- setup: async (projectPath) => {
1719
- await nodeProject(projectPath);
1720
- await codeFile(projectPath, "src/helpers.js", `export function greet() { return 'hi'; }
1721
- `);
1722
- await codeFile(projectPath, "src/main.js", `import { greet } from './helper.js';
1723
- export { greet };
1172
+ await writeFile2(projectPath, "build.js", `console.log('build ok');
1724
1173
  `);
1725
- await codeFile(projectPath, "test.js", jsTest("import", `import { greet } from './src/main.js';
1726
- assert.strictEqual(greet(), 'hi');`));
1727
- }
1728
- }),
1729
- // Feature additions
1730
- task({
1731
- id: "add-cli-arg-parser",
1732
- name: "Add CLI arg parser",
1733
- title: "Add parseArgs function",
1734
- description: "Create src/cli.js exporting parseArgs that returns an object from process.argv style array.",
1735
- complexity: "medium",
1736
- setup: async (projectPath) => {
1737
- await nodeProject(projectPath);
1738
- await codeFile(projectPath, "test.js", jsTest("cli", `import { parseArgs } from './src/cli.js';
1739
- const args = parseArgs(['node', 'script.js', '--name', 'Ada']);
1740
- assert.strictEqual(args.name, 'Ada');`));
1741
- }
1742
- }),
1743
- task({
1744
- id: "add-validator",
1745
- name: "Add an email validator",
1746
- title: "Add isEmail function",
1747
- description: "Create src/validators.js exporting isEmail that returns true for valid emails.",
1748
- complexity: "simple",
1749
- setup: async (projectPath) => {
1750
- await nodeProject(projectPath);
1751
- await codeFile(projectPath, "test.js", jsTest("validator", `import { isEmail } from './src/validators.js';
1752
- assert.strictEqual(isEmail('a@b.com'), true);
1753
- assert.strictEqual(isEmail('not-an-email'), false);`));
1754
- }
1755
- }),
1756
- task({
1757
- id: "add-http-route",
1758
- name: "Add a simple route",
1759
- title: "Add /health route handler",
1760
- description: 'Add a /health route to src/server.js that responds with { status: "ok" }.',
1761
- complexity: "medium",
1762
- setup: async (projectPath) => {
1763
- await nodeProject(projectPath);
1764
- await codeFile(projectPath, "src/server.js", `export function createServer() { return { handle: (req) => ({ statusCode: 404, body: 'not found' }) }; }
1765
- `);
1766
- await codeFile(projectPath, "test.js", jsTest("route", `import { createServer } from './src/server.js';
1767
- const server = createServer();
1768
- const res = server.handle({ url: '/health', method: 'GET' });
1769
- assert.strictEqual(res.statusCode, 200);
1770
- assert.deepStrictEqual(JSON.parse(res.body), { status: 'ok' });`));
1771
- }
1772
- }),
1773
- task({
1774
- id: "add-sum-function",
1775
- name: "Add a sum utility",
1776
- title: "Add sum(arr) function",
1777
- description: "Create src/math.js exporting sum that adds all numbers in an array.",
1778
- complexity: "simple",
1779
- setup: async (projectPath) => {
1780
- await nodeProject(projectPath);
1781
- await codeFile(projectPath, "test.js", jsTest("sum", `import { sum } from './src/math.js';
1782
- assert.strictEqual(sum([1, 2, 3]), 6);
1783
- assert.strictEqual(sum([]), 0);`));
1784
- }
1785
- }),
1786
- task({
1787
- id: "add-logger",
1788
- name: "Add a logger",
1789
- title: "Add createLogger function",
1790
- description: "Create src/logger.js exporting createLogger that returns an object with info/error methods.",
1791
- complexity: "simple",
1792
- setup: async (projectPath) => {
1793
- await nodeProject(projectPath);
1794
- await codeFile(projectPath, "test.js", jsTest("logger", `import { createLogger } from './src/logger.js';
1795
- const logger = createLogger();
1796
- assert.strictEqual(typeof logger.info, 'function');
1797
- assert.strictEqual(typeof logger.error, 'function');`));
1798
- }
1799
- }),
1800
- // Tests
1801
- task({
1802
- id: "write-missing-test",
1803
- name: "Write a missing test",
1804
- title: "Write a test for double()",
1805
- description: "Create test.js that tests a src/math.js double() function and make it pass.",
1806
- complexity: "medium",
1807
- setup: async (projectPath) => {
1808
- await nodeProject(projectPath);
1809
- await codeFile(projectPath, "src/math.js", `export function double(n) { return n * 2; }
1810
- `);
1811
- await codeFile(projectPath, "test.js", `import assert from 'node:assert';
1812
- import { double } from './src/math.js';
1813
- assert.strictEqual(double(3), 6);
1814
- console.log('double test passed');
1815
- `);
1816
- }
1817
- }),
1818
- task({
1819
- id: "make-failing-test-pass",
1820
- name: "Make failing test pass",
1821
- title: "Implement capitalize()",
1822
- description: "Implement src/strings.js capitalize() so the provided test passes.",
1823
- complexity: "simple",
1824
- setup: async (projectPath) => {
1825
- await nodeProject(projectPath);
1826
- await codeFile(projectPath, "src/strings.js", `export function capitalize(s) { return s; }
1174
+ },
1175
+ evaluate: (ctx) => Promise.resolve(validate(ctx))
1176
+ },
1177
+ {
1178
+ id: "update-config",
1179
+ name: "Update a config value",
1180
+ title: "Update timeout value in config.json",
1181
+ description: "Change `config.json` so that `timeout` is `5000` instead of `1000`. The test will read the file and assert the value.",
1182
+ complexity: "simple",
1183
+ setup: async (projectPath) => {
1184
+ await writeJson(projectPath, "package.json", basePackage());
1185
+ await writeJson(projectPath, "config.json", { timeout: 1e3, retries: 3 });
1186
+ await writeFile2(projectPath, "test.js", `import { readFile } from 'node:fs/promises';
1187
+ const cfg = JSON.parse(await readFile('./config.json', 'utf-8'));
1188
+ if (cfg.timeout !== 5000) { console.error('timeout is ' + cfg.timeout); process.exit(1); }
1189
+ console.log('config test passed');
1827
1190
  `);
1828
- await codeFile(projectPath, "test.js", jsTest("capitalize", `import { capitalize } from './src/strings.js';
1829
- assert.strictEqual(capitalize('hello'), 'Hello');`));
1830
- }
1831
- }),
1832
- // Config/package
1833
- task({
1834
- id: "fix-package-type",
1835
- name: "Fix package type",
1836
- title: "Set package type to module",
1837
- description: 'Update package.json so "type" is "module".',
1838
- complexity: "simple",
1839
- setup: async (projectPath) => {
1840
- await jsonFile(projectPath, "package.json", {
1841
- name: "synthetic-bench-project",
1842
- version: "1.0.0",
1843
- type: "commonjs",
1844
- scripts: { lint: "node lint.js", test: "node test.js", build: "node build.js" }
1845
- });
1846
- await codeFile(projectPath, "lint.js", lintAlwaysPass);
1847
- await codeFile(projectPath, "test.js", `console.log('test ok');
1191
+ await writeFile2(projectPath, "lint.js", `console.log('lint ok');
1848
1192
  `);
1849
- await codeFile(projectPath, "build.js", buildAlwaysPass);
1850
- },
1851
- evaluate: combined(async (ctx) => {
1852
- const pkg = JSON.parse(await fs4.readFile(`${ctx.projectPath}/package.json`, "utf-8"));
1853
- return {
1854
- passed: pkg.type === "module",
1855
- message: pkg.type === "module" ? "package type is module" : "package type not module"
1856
- };
1857
- }, expectValidationPassed())
1858
- }),
1859
- task({
1860
- id: "update-dependency",
1861
- name: "Update dependency version",
1862
- title: "Bump lodash to 4.17.21",
1863
- description: "Update package.json so lodash dependency version is 4.17.21.",
1864
- complexity: "simple",
1865
- setup: async (projectPath) => {
1866
- await jsonFile(projectPath, "package.json", {
1867
- name: "synthetic-bench-project",
1868
- version: "1.0.0",
1869
- type: "module",
1870
- dependencies: { lodash: "4.17.20" },
1871
- scripts: { lint: "node lint.js", test: "node test.js", build: "node build.js" }
1872
- });
1873
- await codeFile(projectPath, "lint.js", lintAlwaysPass);
1874
- await codeFile(projectPath, "test.js", `import pkg from './package.json' assert { type: 'json' };
1875
- if (pkg.dependencies?.lodash !== '4.17.21') { console.error('lodash not updated'); process.exit(1); }
1876
- console.log('dependency test passed');
1193
+ await writeFile2(projectPath, "build.js", `console.log('build ok');
1877
1194
  `);
1878
- await codeFile(projectPath, "build.js", buildAlwaysPass);
1879
- }
1880
- }),
1881
- // Multi-file
1882
- task({
1883
- id: "extract-shared-utility",
1884
- name: "Extract shared utility",
1885
- title: "Move formatDate to src/date.js and update imports",
1886
- description: "Extract the formatDate function from src/a.js and src/b.js into src/date.js, then update imports.",
1887
- complexity: "medium",
1888
- setup: async (projectPath) => {
1889
- await nodeProject(projectPath);
1890
- await codeFile(projectPath, "src/a.js", `function formatDate(d) { return d.toISOString().split('T')[0]; }
1891
- export function runA(d) { return formatDate(d); }
1195
+ },
1196
+ evaluate: (ctx) => Promise.resolve(validate(ctx))
1197
+ },
1198
+ {
1199
+ id: "extract-constant",
1200
+ name: "Extract a magic constant",
1201
+ title: "Extract MAX_RETRIES constant",
1202
+ description: "Refactor `src/retry.js` so the magic number `3` is replaced by a top-level constant named `MAX_RETRIES`. The test checks for the constant.",
1203
+ complexity: "medium",
1204
+ setup: async (projectPath) => {
1205
+ await writeJson(projectPath, "package.json", basePackage());
1206
+ await writeFile2(projectPath, "src/retry.js", `export function attempts() { return 3; }
1892
1207
  `);
1893
- await codeFile(projectPath, "src/b.js", `function formatDate(d) { return d.toISOString().split('T')[0]; }
1894
- export function runB(d) { return formatDate(d); }
1208
+ await writeFile2(projectPath, "test.js", `import fs from 'node:fs/promises';
1209
+ import { attempts } from './src/retry.js';
1210
+ const code = await fs.readFile('./src/retry.js', 'utf-8');
1211
+ if (!code.includes('const MAX_RETRIES')) { console.error('MAX_RETRIES constant not found'); process.exit(1); }
1212
+ if (attempts() !== 3) { console.error('attempts() changed'); process.exit(1); }
1213
+ console.log('constant test passed');
1895
1214
  `);
1896
- await codeFile(projectPath, "test.js", jsTest("shared", `import { readFile } from 'node:fs/promises';
1897
- import { runA } from './src/a.js';
1898
- import { runB } from './src/b.js';
1899
- const a = await readFile('./src/a.js', 'utf-8');
1900
- const b = await readFile('./src/b.js', 'utf-8');
1901
- if (!a.includes('./date.js') || !b.includes('./date.js')) { console.error('imports not updated'); process.exit(1); }
1902
- const d = new Date('2024-01-15');
1903
- assert.strictEqual(runA(d), '2024-01-15');
1904
- assert.strictEqual(runB(d), '2024-01-15');`));
1905
- }
1906
- }),
1907
- task({
1908
- id: "fix-circular-import",
1909
- name: "Fix circular import",
1910
- title: "Break circular dependency between a.js and b.js",
1911
- description: "Refactor src/a.js and src/b.js to remove the circular dependency while keeping both exports working.",
1912
- complexity: "complex",
1913
- setup: async (projectPath) => {
1914
- await nodeProject(projectPath);
1915
- await codeFile(projectPath, "src/a.js", `import { valueB } from './b.js';
1916
- export const valueA = 'A' + valueB;
1215
+ await writeFile2(projectPath, "lint.js", `console.log('lint ok');
1917
1216
  `);
1918
- await codeFile(projectPath, "src/b.js", `import { valueA } from './a.js';
1919
- export const valueB = 'B' + valueA;
1217
+ await writeFile2(projectPath, "build.js", `console.log('build ok');
1920
1218
  `);
1921
- await codeFile(projectPath, "test.js", jsTest("circular", `import { valueA, valueB } from './src/a.js';
1922
- assert.ok(typeof valueA === 'string');
1923
- assert.ok(typeof valueB === 'string');`));
1924
- }
1925
- }),
1926
- // Documentation
1927
- task({
1928
- id: "update-readme-example",
1929
- name: "Update README example",
1930
- title: "Add a usage example to README.md",
1931
- description: "Edit README.md to include a code block example showing how to use greet().",
1932
- complexity: "simple",
1933
- setup: async (projectPath) => {
1934
- await nodeProject(projectPath);
1935
- await codeFile(projectPath, "README.md", `# Project
1936
-
1937
- A demo project.
1219
+ },
1220
+ evaluate: (ctx) => Promise.resolve(validate(ctx))
1221
+ },
1222
+ {
1223
+ id: "noop-validation",
1224
+ name: "Run validation on a clean project",
1225
+ title: "Confirm the project already passes validation",
1226
+ description: "This project already passes lint, test and build. Use the publish tool to confirm validation passes and finish.",
1227
+ complexity: "simple",
1228
+ setup: async (projectPath) => {
1229
+ await writeJson(projectPath, "package.json", basePackage());
1230
+ await writeFile2(projectPath, "test.js", `console.log('test ok');
1938
1231
  `);
1939
- await codeFile(projectPath, "test.js", [
1940
- "import { readFile } from 'node:fs/promises';",
1941
- "const readme = await readFile('./README.md', 'utf-8');",
1942
- "if (!readme.includes('```')) { console.error('README missing code block'); process.exit(1); }",
1943
- "if (!readme.includes('greet')) { console.error('README missing greet example'); process.exit(1); }",
1944
- "console.log('readme test passed');",
1945
- ""
1946
- ].join("\n"));
1947
- }
1948
- }),
1949
- // Validation / noop
1950
- task({
1951
- id: "noop-validation",
1952
- name: "Run validation on a clean project",
1953
- title: "Confirm the project already passes validation",
1954
- description: "This project already passes lint, test and build. Use the publish tool to confirm validation passes and finish.",
1955
- complexity: "simple",
1956
- setup: async (projectPath) => {
1957
- await nodeProject(projectPath);
1958
- }
1959
- }),
1960
- // Lint / format
1961
- task({
1962
- id: "fix-lint-warning",
1963
- name: "Fix a lint warning",
1964
- title: "Remove unused variable flagged by linter",
1965
- description: "The custom linter flags any use of `var`. Update src/index.js so the lint script passes while keeping the greeting behavior.",
1966
- complexity: "simple",
1967
- setup: async (projectPath) => {
1968
- await nodeProject(projectPath, {
1969
- lint: "node lint.js"
1970
- });
1971
- await codeFile(projectPath, "src/index.js", `var greeting = 'Hello';
1972
- export function hi(name) { return greeting + ', ' + name + '!'; }
1232
+ await writeFile2(projectPath, "lint.js", `console.log('lint ok');
1973
1233
  `);
1974
- await codeFile(projectPath, "lint.js", `import fs from 'node:fs/promises';
1975
- const code = await fs.readFile('./src/index.js', 'utf-8');
1976
- if (code.includes('var ')) { console.error('lint failed: var keyword found'); process.exit(1); }
1977
- console.log('lint ok');
1234
+ await writeFile2(projectPath, "build.js", `console.log('build ok');
1978
1235
  `);
1979
- await codeFile(projectPath, "test.js", jsTest("hi", `import { hi } from './src/index.js';
1980
- assert.strictEqual(hi('Omega'), 'Hello, Omega!');`));
1981
- }
1982
- }),
1983
- // Complex
1984
- task({
1985
- id: "implement-todo-store",
1986
- name: "Implement a todo store",
1987
- title: "Implement TodoStore with add/complete/list",
1988
- description: "Create src/todo.js exporting TodoStore class with add(text), complete(id), and list() methods.",
1989
- complexity: "complex",
1990
- setup: async (projectPath) => {
1991
- await nodeProject(projectPath);
1992
- await codeFile(projectPath, "test.js", jsTest("todo", `import { TodoStore } from './src/todo.js';
1993
- const store = new TodoStore();
1994
- const id = store.add('Buy milk');
1995
- assert.deepStrictEqual(store.list(), [{ id, text: 'Buy milk', done: false }]);
1996
- store.complete(id);
1997
- assert.strictEqual(store.list()[0].done, true);`));
1998
- }
1999
- })
2000
- ];
1236
+ },
1237
+ evaluate: (ctx) => Promise.resolve(validate(ctx))
1238
+ }
1239
+ ];
1240
+ function syntheticSuite() {
1241
+ return TASKS;
2001
1242
  }
2002
1243
 
2003
1244
  // ../bench/dist/adapters/deepswe.js
2004
- import fs5 from "node:fs/promises";
2005
- import path8 from "node:path";
1245
+ import fs4 from "node:fs/promises";
1246
+ import path7 from "node:path";
2006
1247
  import { execFile } from "node:child_process";
2007
1248
  import { promisify } from "node:util";
2008
1249
  var execFileAsync = promisify(execFile);
@@ -2044,18 +1285,18 @@ function mulberry32(seed) {
2044
1285
  };
2045
1286
  }
2046
1287
  async function readTask(taskDir) {
2047
- const tomlRaw = await fs5.readFile(path8.join(taskDir, "task.toml"), "utf-8");
2048
- const instructionRaw = await fs5.readFile(path8.join(taskDir, "instruction.md"), "utf-8");
1288
+ const tomlRaw = await fs4.readFile(path7.join(taskDir, "task.toml"), "utf-8");
1289
+ const instructionRaw = await fs4.readFile(path7.join(taskDir, "instruction.md"), "utf-8");
2049
1290
  return { toml: parseToml(tomlRaw), instruction: instructionRaw };
2050
1291
  }
2051
1292
  async function cloneRepo(repoUrl, commit, targetPath) {
2052
- await fs5.mkdir(path8.dirname(targetPath), { recursive: true });
1293
+ await fs4.mkdir(path7.dirname(targetPath), { recursive: true });
2053
1294
  await execFileAsync("git", ["clone", repoUrl, targetPath], { timeout: 12e4 });
2054
1295
  await execFileAsync("git", ["-C", targetPath, "checkout", commit], { timeout: 6e4 });
2055
1296
  }
2056
1297
  async function loadDeepSWESuite(options) {
2057
- const entries = await fs5.readdir(options.tasksDir, { withFileTypes: true });
2058
- const taskDirs = entries.filter((e) => e.isDirectory()).map((e) => path8.join(options.tasksDir, e.name));
1298
+ const entries = await fs4.readdir(options.tasksDir, { withFileTypes: true });
1299
+ const taskDirs = entries.filter((e) => e.isDirectory()).map((e) => path7.join(options.tasksDir, e.name));
2059
1300
  let loaded = [];
2060
1301
  for (const dir of taskDirs) {
2061
1302
  try {
@@ -2066,7 +1307,7 @@ async function loadDeepSWESuite(options) {
2066
1307
  }
2067
1308
  if (options.taskIds && options.taskIds.length > 0) {
2068
1309
  loaded = loaded.filter((t) => {
2069
- const id = t.toml.metadata?.task_id ?? path8.basename(t.dir);
1310
+ const id = t.toml.metadata?.task_id ?? path7.basename(t.dir);
2070
1311
  return options.taskIds?.includes(id);
2071
1312
  });
2072
1313
  }
@@ -2077,7 +1318,7 @@ async function loadDeepSWESuite(options) {
2077
1318
  }
2078
1319
  const tasks = [];
2079
1320
  for (const { dir, toml, instruction } of loaded) {
2080
- const id = toml.metadata?.task_id ?? path8.basename(dir);
1321
+ const id = toml.metadata?.task_id ?? path7.basename(dir);
2081
1322
  const title = toml.metadata?.display_title ?? toml.metadata?.original_title ?? id;
2082
1323
  const repo = toml.metadata?.repository_url;
2083
1324
  const commit = toml.metadata?.base_commit_hash;
@@ -2108,110 +1349,65 @@ async function loadDeepSWESuite(options) {
2108
1349
  }
2109
1350
 
2110
1351
  // ../bench/dist/optimise.js
2111
- import fs6 from "node:fs/promises";
2112
- import path9 from "node:path";
1352
+ import fs5 from "node:fs/promises";
1353
+ import path8 from "node:path";
2113
1354
  async function findLatestReport(outputDir) {
2114
- const entries = await fs6.readdir(outputDir).catch(() => []);
1355
+ const entries = await fs5.readdir(outputDir).catch(() => []);
2115
1356
  const files = entries.filter((f) => f.startsWith("benchmark-") && f.endsWith(".json")).sort().reverse();
2116
1357
  if (files.length === 0)
2117
1358
  return void 0;
2118
- const raw = await fs6.readFile(path9.join(outputDir, files[0]), "utf-8");
1359
+ const raw = await fs5.readFile(path8.join(outputDir, files[0]), "utf-8");
2119
1360
  return JSON.parse(raw);
2120
1361
  }
2121
- function extractToolErrors(traceFlow) {
2122
- if (!traceFlow)
2123
- return [];
2124
- const errors = [];
2125
- function visit(span) {
2126
- if (span.name.startsWith("agent.tool.") && span.status === "error") {
2127
- const tool = span.name.replace("agent.tool.", "");
2128
- const message = typeof span.attributes?.error === "string" ? span.attributes.error : "unknown error";
2129
- errors.push(`- ${tool}: ${message}`);
2130
- }
2131
- for (const child of span.children) {
2132
- visit(child);
2133
- }
2134
- }
2135
- for (const root of traceFlow.spans) {
2136
- visit(root);
2137
- }
2138
- return errors.slice(0, 5);
1362
+ function summariseTraceFlow(report, maxLines = 60) {
1363
+ const lines = report.split("\n");
1364
+ if (lines.length <= maxLines)
1365
+ return report;
1366
+ return lines.slice(0, maxLines).join("\n") + "\n... (truncated)";
2139
1367
  }
2140
1368
  function buildOptimisePrompt(report, failedResult, traceFlowText) {
2141
- const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1369
+ const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1370
+ const failed = report.results.filter((r) => !r.evaluation.passed);
2142
1371
  const lines = [];
2143
- lines.push("Task: Improve the Omega agent prompts in packages/agent/src/prompts.ts");
1372
+ lines.push("# Prompt optimisation task");
2144
1373
  lines.push("");
2145
- lines.push(`The latest benchmark run achieved ${String(passRate2)}% pass rate (${String(report.passed)}/${String(report.total)}).`);
1374
+ lines.push(`The latest benchmark run achieved ${String(passRate)}% pass rate (${String(report.passed)}/${String(report.total)}).`);
2146
1375
  lines.push("");
2147
- lines.push("## Failure summary");
2148
- const traceFlows = /* @__PURE__ */ new Map();
2149
- const failureSummary = summariseFailures(report.results, traceFlows);
2150
- for (const summary of failureSummary) {
2151
- lines.push(`- ${summary.category}: ${String(summary.count)} task(s)`);
2152
- for (const example of summary.examples) {
2153
- lines.push(` - ${example}`);
2154
- }
1376
+ lines.push("## Failed tasks");
1377
+ for (const r of failed.slice(0, 5)) {
1378
+ lines.push(`- ${r.task.name}: ${r.evaluation.message ?? "no message"}`);
2155
1379
  }
2156
1380
  lines.push("");
2157
1381
  if (failedResult) {
2158
- const analysis = failedResult.failureAnalysis ?? classifyFailure(failedResult, void 0);
2159
- lines.push(`## Focus failure: ${failedResult.task.name}`);
2160
- lines.push(`Category: ${analysis.category}`);
2161
- lines.push(`Root cause: ${analysis.rootCause}`);
2162
- if (analysis.evidence.length > 0) {
2163
- lines.push("Evidence:");
2164
- for (const ev of analysis.evidence) {
2165
- lines.push(`- ${ev}`);
2166
- }
2167
- }
1382
+ lines.push(`## Focus task: ${failedResult.task.name}`);
1383
+ lines.push(`Status: ${failedResult.status}`);
1384
+ lines.push(`Evaluation: ${failedResult.evaluation.message ?? "none"}`);
2168
1385
  lines.push("");
2169
1386
  }
2170
1387
  if (traceFlowText) {
2171
- const traceFlow = JSON.parse(traceFlowText);
2172
- const toolErrors = extractToolErrors(traceFlow);
2173
- if (toolErrors.length > 0) {
2174
- lines.push("## Failed tool calls");
2175
- for (const err of toolErrors) {
2176
- lines.push(err);
2177
- }
2178
- lines.push("");
2179
- }
1388
+ lines.push("## Trace flow snapshot");
1389
+ lines.push("```json");
1390
+ lines.push(summariseTraceFlow(traceFlowText));
1391
+ lines.push("```");
1392
+ lines.push("");
2180
1393
  }
2181
1394
  lines.push("## Instructions");
2182
- lines.push("1. Read packages/agent/src/prompts.ts.");
2183
- lines.push("2. Make a small, targeted change to the system prompt or text-tools prompt that fixes the focus failure.");
2184
- if (failedResult) {
2185
- const analysis = failedResult.failureAnalysis ?? classifyFailure(failedResult, void 0);
2186
- const categoryActions = {
2187
- timeout: "Add a stronger instruction to finish promptly and avoid long reasoning chains.",
2188
- validation_failure: "Add clearer instructions to run validation commands (pnpm lint, pnpm test) and fix errors before finishing.",
2189
- tool_misuse: "Add clearer instructions and examples for using edit_file and write_file correctly, including reading the file first.",
2190
- parse_error: "Add clearer instructions to respond with valid JSON tool_calls and no markdown wrappers.",
2191
- plan_error: "Add stronger instructions to create a concise plan and execute it without over-thinking or repeating actions.",
2192
- unknown: "Add stronger general instructions to read the task, act with tools, validate, and finish."
2193
- };
2194
- lines.push(`3. Concrete focus: ${categoryActions[analysis.category]}`);
2195
- }
2196
- lines.push("4. Run `pnpm lint` and `pnpm test` after edits.");
2197
- lines.push("5. Finish with a summary of what changed and why it should help.");
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.");
1396
+ lines.push("Rules:");
1397
+ lines.push("- Keep changes minimal and targeted.");
1398
+ lines.push("- Do not break existing tool signatures or system behaviour.");
1399
+ lines.push("- Run `pnpm lint` and `pnpm test` after edits.");
1400
+ lines.push("- Finish with a summary of what changed and why it should help.");
2198
1401
  return lines.join("\n");
2199
1402
  }
2200
1403
  async function loadOptimisationContext(apiUrl, outputDir) {
2201
1404
  const report = await findLatestReport(outputDir);
2202
1405
  if (!report)
2203
1406
  return void 0;
2204
- const traceFlows = /* @__PURE__ */ new Map();
2205
- for (const result of report.results) {
2206
- if (result.evaluation.passed)
2207
- continue;
2208
- const traceFlow = result.harnessTaskId ? await getTraceFlow(apiUrl, result.harnessTaskId) : void 0;
2209
- traceFlows.set(result.harnessTaskId, traceFlow ?? void 0);
2210
- }
2211
- const failedResult = pickFocusResult(report.results, traceFlows);
1407
+ const failedResult = report.results.find((r) => !r.evaluation.passed);
2212
1408
  let traceFlowText;
2213
1409
  if (failedResult?.harnessTaskId) {
2214
- const traceFlow = traceFlows.get(failedResult.harnessTaskId);
1410
+ const traceFlow = await getTraceFlow(apiUrl, failedResult.harnessTaskId);
2215
1411
  if (traceFlow) {
2216
1412
  traceFlowText = JSON.stringify(traceFlow, null, 2);
2217
1413
  }
@@ -2236,92 +1432,6 @@ async function submitOptimiseTask(apiUrl, projectId, prompt) {
2236
1432
  return res.json();
2237
1433
  }
2238
1434
 
2239
- // ../bench/dist/ab.js
2240
- function passRate(report) {
2241
- return report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
2242
- }
2243
- function findResult(report, taskId) {
2244
- return report.results.find((r) => r.task.id === taskId);
2245
- }
2246
- function buildAbReport(baseline, candidate, baselineReport, candidateReport) {
2247
- const baselineRate = passRate(baselineReport);
2248
- const candidateRate = passRate(candidateReport);
2249
- const taskIds = /* @__PURE__ */ new Set();
2250
- for (const r of baselineReport.results)
2251
- taskIds.add(r.task.id);
2252
- for (const r of candidateReport.results)
2253
- taskIds.add(r.task.id);
2254
- const perTask = Array.from(taskIds).map((taskId) => {
2255
- const base = findResult(baselineReport, taskId);
2256
- const cand = findResult(candidateReport, taskId);
2257
- return {
2258
- taskId,
2259
- taskName: base?.task.name ?? cand?.task.name ?? taskId,
2260
- baselinePassed: base?.evaluation.passed ?? false,
2261
- candidatePassed: cand?.evaluation.passed ?? false,
2262
- baselineTokens: base?.usage?.totalTokens,
2263
- candidateTokens: cand?.usage?.totalTokens
2264
- };
2265
- });
2266
- return {
2267
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2268
- baseline,
2269
- candidate,
2270
- baselineReport,
2271
- candidateReport,
2272
- delta: {
2273
- passRate: candidateRate - baselineRate,
2274
- passedDelta: candidateReport.passed - baselineReport.passed,
2275
- durationMsDelta: candidateReport.totalDurationMs - baselineReport.totalDurationMs,
2276
- promptTokensDelta: (candidateReport.totalUsage.promptTokens ?? 0) - (baselineReport.totalUsage.promptTokens ?? 0),
2277
- completionTokensDelta: (candidateReport.totalUsage.completionTokens ?? 0) - (baselineReport.totalUsage.completionTokens ?? 0),
2278
- totalTokensDelta: (candidateReport.totalUsage.totalTokens ?? 0) - (baselineReport.totalUsage.totalTokens ?? 0)
2279
- },
2280
- perTask
2281
- };
2282
- }
2283
- function deltaSymbol(value) {
2284
- if (value > 0)
2285
- return "+";
2286
- if (value < 0)
2287
- return "";
2288
- return "";
2289
- }
2290
- function formatDelta(value) {
2291
- return `${deltaSymbol(value)}${String(value)}`;
2292
- }
2293
- function formatAbReport(report) {
2294
- const lines = [];
2295
- lines.push("# Omega A/B Benchmark Report");
2296
- lines.push("");
2297
- lines.push(`- Timestamp: ${report.timestamp}`);
2298
- lines.push(`- Baseline: ${report.baseline.name}`);
2299
- lines.push(`- Candidate: ${report.candidate.name}`);
2300
- lines.push("");
2301
- lines.push("## Summary");
2302
- lines.push(`| Metric | Baseline | Candidate | Delta |`);
2303
- lines.push(`|---|---|---|---|`);
2304
- lines.push(`| Pass rate | ${String(passRate(report.baselineReport))}% | ${String(passRate(report.candidateReport))}% | ${formatDelta(report.delta.passRate)}% |`);
2305
- lines.push(`| Passed | ${String(report.baselineReport.passed)}/${String(report.baselineReport.total)} | ${String(report.candidateReport.passed)}/${String(report.candidateReport.total)} | ${formatDelta(report.delta.passedDelta)} |`);
2306
- lines.push(`| Duration | ${formatDuration2(report.baselineReport.totalDurationMs)} | ${formatDuration2(report.candidateReport.totalDurationMs)} | ${formatDuration2(report.delta.durationMsDelta)} |`);
2307
- lines.push(`| Tokens | ${String(report.baselineReport.totalUsage.totalTokens ?? 0)} | ${String(report.candidateReport.totalUsage.totalTokens ?? 0)} | ${formatDelta(report.delta.totalTokensDelta)} |`);
2308
- lines.push("");
2309
- lines.push("## Per-task comparison");
2310
- lines.push(`| Task | Baseline | Candidate | Token delta |`);
2311
- lines.push(`|---|---|---|---|`);
2312
- for (const t of report.perTask) {
2313
- const tokenDelta = t.baselineTokens !== void 0 && t.candidateTokens !== void 0 ? formatDelta(t.candidateTokens - t.baselineTokens) : "-";
2314
- lines.push(`| ${t.taskName} | ${t.baselinePassed ? "pass" : "fail"} | ${t.candidatePassed ? "pass" : "fail"} | ${tokenDelta} |`);
2315
- }
2316
- lines.push("");
2317
- return lines.join("\n");
2318
- }
2319
- function formatDuration2(ms) {
2320
- if (ms < 1e3)
2321
- return `${String(Math.round(ms))}ms`;
2322
- return `${(ms / 1e3).toFixed(1)}s`;
2323
- }
2324
-
2325
1435
  // ../../apps/cli/src/commands/bench.ts
2326
1436
  async function waitForApi2(apiUrl, maxMs = 1e4) {
2327
1437
  const deadline = Date.now() + maxMs;
@@ -2337,10 +1447,10 @@ async function waitForApi2(apiUrl, maxMs = 1e4) {
2337
1447
  }
2338
1448
  function currentProject(apiUrl) {
2339
1449
  const cwd = process.cwd();
2340
- const name = path11.basename(cwd);
1450
+ const name = path9.basename(cwd);
2341
1451
  return ensureProject2(apiUrl, `bench-${name}`, cwd);
2342
1452
  }
2343
- 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").action(async (opts) => {
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) => {
2344
1454
  const apiUrl = getApiUrl();
2345
1455
  await waitForApi2(apiUrl);
2346
1456
  const timeoutMs = Number(opts.timeout);
@@ -2374,6 +1484,8 @@ var runCmd = new Command8("run").description("Run a benchmark suite").option("--
2374
1484
  apiUrl,
2375
1485
  suiteName,
2376
1486
  timeoutMs,
1487
+ provider: opts.provider,
1488
+ model: opts.model,
2377
1489
  onProgress: (result) => {
2378
1490
  const symbol = result.evaluation.passed ? "\u2713" : "\u2717";
2379
1491
  console.log(`${symbol} ${result.task.name} [${result.status}] ${String(result.durationMs)}ms`);
@@ -2392,254 +1504,17 @@ var optimiseCmd = new Command8("optimise").description("Create a self-improve ta
2392
1504
  console.log("No benchmark report found. Run `omega bench run` first.");
2393
1505
  return;
2394
1506
  }
2395
- const { loadCurrentPrompts: loadCurrentPrompts2 } = await Promise.resolve().then(() => (init_prompt_versioning(), prompt_versioning_exports));
2396
- const current = await loadCurrentPrompts2();
2397
- const versionRes = await fetch(`${apiUrl}/prompt-versions`, {
2398
- method: "POST",
2399
- headers: { "Content-Type": "application/json" },
2400
- body: JSON.stringify({
2401
- name: current.name,
2402
- sourcePath: current.sourcePath,
2403
- systemPrompt: current.systemPrompt,
2404
- textToolsPrompt: current.textToolsPrompt,
2405
- hash: current.hash,
2406
- metadata: { planningPrompt: current.planningPrompt }
2407
- })
2408
- });
2409
- if (versionRes.ok) {
2410
- const version = await versionRes.json();
2411
- console.log(`Saved baseline prompt version: ${version.name} (${version.id})`);
2412
- } else {
2413
- console.warn("Could not save prompt version:", await versionRes.text());
2414
- }
2415
1507
  const project = await currentProject(apiUrl);
2416
1508
  const prompt = buildOptimisePrompt(
2417
1509
  context.report,
2418
1510
  context.failedResult,
2419
1511
  context.traceFlowText
2420
1512
  );
2421
- const task2 = await submitOptimiseTask(apiUrl, project.id, prompt);
2422
- console.log(`Created self-improve task ${task2.id}`);
2423
- console.log(`Run \`omega task run ${task2.id}\` to execute it.`);
2424
- });
2425
- async function loadPromptSpec(source) {
2426
- if (source === "current") {
2427
- const { readPromptsSource: readPromptsSource2 } = await Promise.resolve().then(() => (init_prompt_versioning(), prompt_versioning_exports));
2428
- const prompts = await readPromptsSource2();
2429
- return { name: "current", ...prompts };
2430
- }
2431
- const resolved = path11.resolve(source);
2432
- const raw = await fs8.readFile(resolved, "utf-8");
2433
- const parsed = JSON.parse(raw);
2434
- if (!parsed.name || !parsed.systemPrompt || !parsed.textToolsPrompt) {
2435
- throw new Error("Prompt file must include name, systemPrompt, and textToolsPrompt");
2436
- }
2437
- return parsed;
2438
- }
2439
- async function writePromptFile(spec) {
2440
- const tmpDir = await fs8.mkdtemp(path11.join(os.tmpdir(), "omega-prompt-"));
2441
- const file = path11.join(tmpDir, "prompts.json");
2442
- await fs8.writeFile(file, JSON.stringify(spec, null, 2), "utf-8");
2443
- return file;
2444
- }
2445
- async function runBenchmarkWithPrompts(tasks, spec, options) {
2446
- const promptFile = await writePromptFile(spec);
2447
- const env = {
2448
- ...process.env,
2449
- OMEGA_SYSTEM_PROMPT_FILE: promptFile,
2450
- OMEGA_TEXT_TOOLS_PROMPT_FILE: promptFile
2451
- };
2452
- let apiUrl = options.apiUrl;
2453
- let server;
2454
- if (!apiUrl) {
2455
- const started = await startHarnessServer(env);
2456
- apiUrl = started.apiUrl;
2457
- server = started.process;
2458
- }
2459
- try {
2460
- await waitForApi2(apiUrl, 3e4);
2461
- const report = await runBenchmark(tasks, {
2462
- apiUrl,
2463
- suiteName: options.suiteName,
2464
- timeoutMs: options.timeoutMs,
2465
- onProgress: (result) => {
2466
- const symbol = result.evaluation.passed ? "\u2713" : "\u2717";
2467
- console.log(`${symbol} ${result.task.name} [${result.status}] ${String(result.durationMs)}ms`);
2468
- }
2469
- });
2470
- return { report, apiUrl, server };
2471
- } catch (err) {
2472
- server?.kill();
2473
- throw err;
2474
- }
2475
- }
2476
- 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("--n-tasks <n>", "limit number of tasks", parseInt).option("--timeout <ms>", "per-task timeout in ms", "120000").option("--output-dir <dir>", "report output directory", ".omega/reports").action(async (opts) => {
2477
- if (!opts.candidate) {
2478
- throw new Error("--candidate is required");
2479
- }
2480
- const timeoutMs = Number(opts.timeout);
2481
- const existingApiUrl = getApiUrl();
2482
- let tasks;
2483
- let suiteName;
2484
- if (opts.suite === "deep-swe") {
2485
- throw new Error("DeepSWE A/B not yet supported; use --suite synthetic");
2486
- } else if (opts.suite === "synthetic") {
2487
- tasks = syntheticSuite();
2488
- if (opts.nTasks !== void 0 && opts.nTasks > 0) {
2489
- tasks = tasks.slice(0, opts.nTasks);
2490
- }
2491
- suiteName = "synthetic";
2492
- } else {
2493
- throw new Error(`Unknown suite: ${opts.suite}`);
2494
- }
2495
- const baselineSpec = await loadPromptSpec(opts.baseline);
2496
- const candidateSpec = await loadPromptSpec(opts.candidate);
2497
- console.log(`Baseline: ${baselineSpec.name}`);
2498
- console.log(`Candidate: ${candidateSpec.name}`);
2499
- console.log(`Running ${String(tasks.length)} tasks for baseline...`);
2500
- const { report: baselineReport, server: baseServer } = await runBenchmarkWithPrompts(tasks, baselineSpec, {
2501
- apiUrl: existingApiUrl,
2502
- suiteName: `${suiteName}-baseline`,
2503
- timeoutMs
2504
- });
2505
- baseServer?.kill();
2506
- console.log(`Running ${String(tasks.length)} tasks for candidate...`);
2507
- const { report: candidateReport, server: candServer } = await runBenchmarkWithPrompts(tasks, candidateSpec, {
2508
- apiUrl: existingApiUrl,
2509
- suiteName: `${suiteName}-candidate`,
2510
- timeoutMs
2511
- });
2512
- candServer?.kill();
2513
- const abReport = buildAbReport(baselineSpec, candidateSpec, baselineReport, candidateReport);
2514
- await fs8.mkdir(opts.outputDir, { recursive: true });
2515
- const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2516
- const jsonFile2 = path11.join(opts.outputDir, `ab-${ts}.json`);
2517
- const mdFile = path11.join(opts.outputDir, `ab-${ts}.md`);
2518
- await fs8.writeFile(jsonFile2, JSON.stringify(abReport, null, 2), "utf-8");
2519
- await fs8.writeFile(mdFile, formatAbReport(abReport), "utf-8");
2520
- console.log("\n" + formatAbReport(abReport));
2521
- console.log(`
2522
- A/B report written to ${jsonFile2}`);
2523
- });
2524
- async function savePromptVersion(apiUrl, name) {
2525
- const { loadCurrentPrompts: loadCurrentPrompts2 } = await Promise.resolve().then(() => (init_prompt_versioning(), prompt_versioning_exports));
2526
- const current = await loadCurrentPrompts2();
2527
- const versionRes = await fetch(`${apiUrl}/prompt-versions`, {
2528
- method: "POST",
2529
- headers: { "Content-Type": "application/json" },
2530
- body: JSON.stringify({
2531
- name: name ?? current.name,
2532
- sourcePath: current.sourcePath,
2533
- systemPrompt: current.systemPrompt,
2534
- textToolsPrompt: current.textToolsPrompt,
2535
- hash: current.hash,
2536
- metadata: { planningPrompt: current.planningPrompt }
2537
- })
2538
- });
2539
- if (!versionRes.ok) {
2540
- console.warn("Could not save prompt version:", await versionRes.text());
2541
- return void 0;
2542
- }
2543
- return await versionRes.json();
2544
- }
2545
- async function runLoopIteration(apiUrl, projectId, tasks, suiteName, timeoutMs, outputDir, provider, model) {
2546
- const beforeVersion = await savePromptVersion(apiUrl, `loop-before-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
2547
- if (beforeVersion) {
2548
- console.log(`Saved before-loop prompt version: ${beforeVersion.name} (${beforeVersion.id})`);
2549
- }
2550
- console.log(`
2551
- Running ${String(tasks.length)} benchmark tasks...`);
2552
- const report = await runBenchmark(tasks, {
2553
- apiUrl,
2554
- suiteName,
2555
- timeoutMs,
2556
- onProgress: (result) => {
2557
- const symbol = result.evaluation.passed ? "\u2713" : "\u2717";
2558
- console.log(`${symbol} ${result.task.name} [${result.status}] ${String(result.durationMs)}ms`);
2559
- }
2560
- });
2561
- await writeReport(report, outputDir);
2562
- printSummary(report);
2563
- const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
2564
- if (passRate2 === 100) {
2565
- console.log("\nAll tasks passed. Loop complete.");
2566
- return { report, improved: false };
2567
- }
2568
- const context = await loadOptimisationContext(apiUrl, outputDir);
2569
- if (!context) {
2570
- throw new Error("Benchmark report could not be loaded for optimisation");
2571
- }
2572
- const prompt = buildOptimisePrompt(context.report, context.failedResult, context.traceFlowText);
2573
- const task2 = await submitOptimiseTask(apiUrl, projectId, prompt);
2574
- if (provider || model) {
2575
- await fetch(`${apiUrl}/tasks/${task2.id}`, {
2576
- method: "PATCH",
2577
- headers: { "Content-Type": "application/json" },
2578
- body: JSON.stringify({ provider, model })
2579
- });
2580
- }
2581
- console.log(`
2582
- Created self-improve task ${task2.id}${provider ? ` (provider: ${provider})` : ""}${model ? ` (model: ${model})` : ""}`);
2583
- console.log("Running agent...");
2584
- await runTask(apiUrl, task2.id);
2585
- const finished = await waitForTask(apiUrl, task2.id, timeoutMs * 5);
2586
- console.log(`Agent finished: ${finished.status}`);
2587
- if (finished.error) {
2588
- console.error(`Agent error: ${finished.error}`);
2589
- }
2590
- const afterVersion = await savePromptVersion(apiUrl, `loop-after-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
2591
- if (afterVersion) {
2592
- console.log(`Saved after-loop prompt version: ${afterVersion.name} (${afterVersion.id})`);
2593
- }
2594
- const improved = report.passed < context.report.passed + (context.report.results.some((r) => r.evaluation.passed) ? 0 : 0);
2595
- return { report, improved };
2596
- }
2597
- 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").option("--n-tasks <n>", "limit number of tasks", parseInt).option("--iterations <n>", "max iterations", "3").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 <name>", "model to use for self-improve tasks").action(async (opts) => {
2598
- const apiUrl = getApiUrl();
2599
- await waitForApi2(apiUrl);
2600
- const project = await currentProject(apiUrl);
2601
- const timeoutMs = Number(opts.timeout);
2602
- const maxIterations = Number(opts.iterations);
2603
- let tasks;
2604
- let suiteName;
2605
- if (opts.suite === "deep-swe") {
2606
- if (!opts.path) throw new Error("--path is required for deep-swe");
2607
- tasks = await loadDeepSWESuite({
2608
- tasksDir: opts.path,
2609
- nTasks: opts.nTasks
2610
- });
2611
- suiteName = "deep-swe";
2612
- } else if (opts.suite === "synthetic") {
2613
- tasks = syntheticSuite();
2614
- if (opts.nTasks !== void 0 && opts.nTasks > 0) {
2615
- tasks = tasks.slice(0, opts.nTasks);
2616
- }
2617
- suiteName = "synthetic";
2618
- } else {
2619
- throw new Error(`Unknown suite: ${opts.suite}`);
2620
- }
2621
- if (tasks.length === 0) {
2622
- console.log("No benchmark tasks to run.");
2623
- return;
2624
- }
2625
- for (let i = 1; i <= maxIterations; i++) {
2626
- console.log(`
2627
- === Iteration ${String(i)}/${String(maxIterations)} ===`);
2628
- const { report } = await runLoopIteration(
2629
- apiUrl,
2630
- project.id,
2631
- tasks,
2632
- suiteName,
2633
- timeoutMs,
2634
- opts.outputDir,
2635
- opts.provider,
2636
- opts.model
2637
- );
2638
- const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
2639
- if (passRate2 === 100) break;
2640
- }
1513
+ const task = await submitOptimiseTask(apiUrl, project.id, prompt);
1514
+ console.log(`Created self-improve task ${task.id}`);
1515
+ console.log(`Run \`omega task run ${task.id}\` to execute it.`);
2641
1516
  });
2642
- var benchCmd = new Command8("bench").description("Run benchmarks and optimise prompts").addCommand(runCmd).addCommand(abCmd).addCommand(optimiseCmd).addCommand(loopCmd);
1517
+ var benchCmd = new Command8("bench").description("Run benchmarks and optimise prompts").addCommand(runCmd).addCommand(optimiseCmd);
2643
1518
 
2644
1519
  // ../../apps/cli/src/index.ts
2645
1520
  program2.name("harness").description("Omega harness CLI").version("0.1.0").option("--api <url>", "API base URL", "http://localhost:4000");