@castlemilk/omega 0.6.11 → 0.6.13

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 (167) hide show
  1. package/dist/cli.js +1391 -254
  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 +3 -2
  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 +76 -2
  10. package/dist/server/dist/routes/metrics.js.map +1 -1
  11. package/dist/server/dist/routes/prompt-versions.d.ts +4 -0
  12. package/dist/server/dist/routes/prompt-versions.d.ts.map +1 -0
  13. package/dist/server/dist/routes/prompt-versions.js +40 -0
  14. package/dist/server/dist/routes/prompt-versions.js.map +1 -0
  15. package/dist/server/dist/routes/tasks.d.ts.map +1 -1
  16. package/dist/server/dist/routes/tasks.js +4 -0
  17. package/dist/server/dist/routes/tasks.js.map +1 -1
  18. package/dist/server/node_modules/.package-lock.json +3 -3
  19. package/dist/server/node_modules/@grpc/proto-loader/build/bin/proto-loader-gen-types.js +0 -0
  20. package/dist/server/node_modules/@omega/agent/dist/executor.d.ts.map +1 -1
  21. package/dist/server/node_modules/@omega/agent/dist/executor.js +83 -10
  22. package/dist/server/node_modules/@omega/agent/dist/executor.js.map +1 -1
  23. package/dist/server/node_modules/@omega/agent/dist/index.d.ts +1 -0
  24. package/dist/server/node_modules/@omega/agent/dist/index.d.ts.map +1 -1
  25. package/dist/server/node_modules/@omega/agent/dist/index.js +1 -0
  26. package/dist/server/node_modules/@omega/agent/dist/index.js.map +1 -1
  27. package/dist/server/node_modules/@omega/agent/dist/planner.d.ts +5 -0
  28. package/dist/server/node_modules/@omega/agent/dist/planner.d.ts.map +1 -1
  29. package/dist/server/node_modules/@omega/agent/dist/planner.js +31 -5
  30. package/dist/server/node_modules/@omega/agent/dist/planner.js.map +1 -1
  31. package/dist/server/node_modules/@omega/agent/dist/prompt-versioning.d.ts +22 -0
  32. package/dist/server/node_modules/@omega/agent/dist/prompt-versioning.d.ts.map +1 -0
  33. package/dist/server/node_modules/@omega/agent/dist/prompt-versioning.js +53 -0
  34. package/dist/server/node_modules/@omega/agent/dist/prompt-versioning.js.map +1 -0
  35. package/dist/server/node_modules/@omega/agent/dist/prompts.d.ts +2 -2
  36. package/dist/server/node_modules/@omega/agent/dist/prompts.d.ts.map +1 -1
  37. package/dist/server/node_modules/@omega/agent/dist/prompts.js +44 -7
  38. package/dist/server/node_modules/@omega/agent/dist/prompts.js.map +1 -1
  39. package/dist/server/node_modules/@omega/agent/package.json +4 -0
  40. package/dist/server/node_modules/@omega/core/dist/provider.d.ts +12 -2
  41. package/dist/server/node_modules/@omega/core/dist/provider.d.ts.map +1 -1
  42. package/dist/server/node_modules/@omega/db/dist/index.d.ts +1 -0
  43. package/dist/server/node_modules/@omega/db/dist/index.d.ts.map +1 -1
  44. package/dist/server/node_modules/@omega/db/dist/index.js +1 -0
  45. package/dist/server/node_modules/@omega/db/dist/index.js.map +1 -1
  46. package/dist/server/node_modules/@omega/db/dist/prompt-versions.d.ts +19 -0
  47. package/dist/server/node_modules/@omega/db/dist/prompt-versions.d.ts.map +1 -0
  48. package/dist/server/node_modules/@omega/db/dist/prompt-versions.js +32 -0
  49. package/dist/server/node_modules/@omega/db/dist/prompt-versions.js.map +1 -0
  50. package/dist/server/node_modules/@omega/db/dist/seed.js +6 -6
  51. package/dist/server/node_modules/@omega/db/dist/seed.js.map +1 -1
  52. package/dist/server/node_modules/@omega/db/generated/client/edge.js +19 -4
  53. package/dist/server/node_modules/@omega/db/generated/client/index-browser.js +16 -1
  54. package/dist/server/node_modules/@omega/db/generated/client/index.d.ts +1511 -93
  55. package/dist/server/node_modules/@omega/db/generated/client/index.js +19 -4
  56. package/dist/server/node_modules/@omega/db/generated/client/libquery_engine-darwin-arm64.dylib.node +0 -0
  57. package/dist/server/node_modules/@omega/db/generated/client/package.json +1 -1
  58. package/dist/server/node_modules/@omega/db/generated/client/schema.prisma +14 -0
  59. package/dist/server/node_modules/@omega/db/generated/client/wasm.js +19 -4
  60. package/dist/server/node_modules/@omega/db/prisma/migrations/20260703010000_add_agent_run_usage/migration.sql +4 -0
  61. package/dist/server/node_modules/@omega/db/prisma/migrations/20260703020000_add_prompt_versions/migration.sql +16 -0
  62. package/dist/server/node_modules/@omega/providers/dist/anthropic.d.ts +2 -2
  63. package/dist/server/node_modules/@omega/providers/dist/anthropic.d.ts.map +1 -1
  64. package/dist/server/node_modules/@omega/providers/dist/anthropic.js +16 -1
  65. package/dist/server/node_modules/@omega/providers/dist/anthropic.js.map +1 -1
  66. package/dist/server/node_modules/@omega/providers/dist/gemini.d.ts +2 -2
  67. package/dist/server/node_modules/@omega/providers/dist/gemini.d.ts.map +1 -1
  68. package/dist/server/node_modules/@omega/providers/dist/gemini.js +14 -1
  69. package/dist/server/node_modules/@omega/providers/dist/gemini.js.map +1 -1
  70. package/dist/server/node_modules/@omega/providers/dist/kimi.d.ts +3 -4
  71. package/dist/server/node_modules/@omega/providers/dist/kimi.d.ts.map +1 -1
  72. package/dist/server/node_modules/@omega/providers/dist/kimi.js +6 -4
  73. package/dist/server/node_modules/@omega/providers/dist/kimi.js.map +1 -1
  74. package/dist/server/node_modules/@omega/providers/dist/ollama.d.ts +2 -2
  75. package/dist/server/node_modules/@omega/providers/dist/ollama.d.ts.map +1 -1
  76. package/dist/server/node_modules/@omega/providers/dist/ollama.js +13 -1
  77. package/dist/server/node_modules/@omega/providers/dist/ollama.js.map +1 -1
  78. package/dist/server/node_modules/@omega/providers/dist/openai.d.ts +3 -3
  79. package/dist/server/node_modules/@omega/providers/dist/openai.d.ts.map +1 -1
  80. package/dist/server/node_modules/@omega/providers/dist/openai.js +28 -5
  81. package/dist/server/node_modules/@omega/providers/dist/openai.js.map +1 -1
  82. package/dist/server/node_modules/@omega/providers/dist/providers.test.js +13 -9
  83. package/dist/server/node_modules/@omega/providers/dist/providers.test.js.map +1 -1
  84. package/dist/server/node_modules/mime/cli.js +0 -0
  85. package/dist/server/node_modules/mime/src/build.js +0 -0
  86. package/dist/server/node_modules/protobufjs/dist/light/protobuf.js +2 -2
  87. package/dist/server/node_modules/protobufjs/dist/light/protobuf.min.js +2 -2
  88. package/dist/server/node_modules/protobufjs/dist/minimal/protobuf.js +2 -2
  89. package/dist/server/node_modules/protobufjs/dist/minimal/protobuf.min.js +2 -2
  90. package/dist/server/node_modules/protobufjs/dist/protobuf.js +5 -2
  91. package/dist/server/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  92. package/dist/server/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  93. package/dist/server/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  94. package/dist/server/node_modules/protobufjs/package.json +1 -1
  95. package/dist/server/node_modules/protobufjs/src/parse.js +3 -0
  96. package/dist/server/node_modules/wrap-ansi/index.js +0 -0
  97. package/dist/server/pglite-data/base/5/1247 +0 -0
  98. package/dist/server/pglite-data/base/5/1249 +0 -0
  99. package/dist/server/pglite-data/base/5/1249_fsm +0 -0
  100. package/dist/server/pglite-data/base/5/1259 +0 -0
  101. package/dist/server/pglite-data/base/5/1259_fsm +0 -0
  102. package/dist/server/pglite-data/base/5/1259_vm +0 -0
  103. package/dist/server/pglite-data/base/5/16384 +0 -0
  104. package/dist/server/pglite-data/base/5/16392 +0 -0
  105. package/dist/server/pglite-data/base/5/16394 +0 -0
  106. package/dist/server/pglite-data/base/5/16404 +0 -0
  107. package/dist/server/pglite-data/base/5/16406 +0 -0
  108. package/dist/server/pglite-data/base/5/16421 +0 -0
  109. package/dist/server/pglite-data/base/5/16480 +0 -0
  110. package/dist/server/pglite-data/base/5/16494 +0 -0
  111. package/dist/server/pglite-data/base/5/16510 +0 -0
  112. package/dist/server/pglite-data/base/5/16515 +0 -0
  113. package/dist/server/pglite-data/base/5/{24591 → 16554} +0 -0
  114. package/dist/server/pglite-data/base/5/{24592 → 16555} +0 -0
  115. package/dist/server/pglite-data/base/5/{24588 → 16557} +0 -0
  116. package/dist/server/pglite-data/base/5/{24589 → 16558} +0 -0
  117. package/dist/server/pglite-data/base/5/16564 +0 -0
  118. package/dist/server/pglite-data/base/5/16575 +0 -0
  119. package/dist/server/pglite-data/base/5/16576 +0 -0
  120. package/dist/server/pglite-data/base/5/16577 +0 -0
  121. package/dist/server/pglite-data/base/5/16579 +0 -0
  122. package/dist/server/pglite-data/base/5/2579 +0 -0
  123. package/dist/server/pglite-data/base/5/2604 +0 -0
  124. package/dist/server/pglite-data/base/5/2606 +0 -0
  125. package/dist/server/pglite-data/base/5/2608 +0 -0
  126. package/dist/server/pglite-data/base/5/2608_fsm +0 -0
  127. package/dist/server/pglite-data/base/5/2610 +0 -0
  128. package/dist/server/pglite-data/base/5/2620 +0 -0
  129. package/dist/server/pglite-data/base/5/2656 +0 -0
  130. package/dist/server/pglite-data/base/5/2657 +0 -0
  131. package/dist/server/pglite-data/base/5/2658 +0 -0
  132. package/dist/server/pglite-data/base/5/2659 +0 -0
  133. package/dist/server/pglite-data/base/5/2662 +0 -0
  134. package/dist/server/pglite-data/base/5/2663 +0 -0
  135. package/dist/server/pglite-data/base/5/2664 +0 -0
  136. package/dist/server/pglite-data/base/5/2665 +0 -0
  137. package/dist/server/pglite-data/base/5/2666 +0 -0
  138. package/dist/server/pglite-data/base/5/2667 +0 -0
  139. package/dist/server/pglite-data/base/5/2673 +0 -0
  140. package/dist/server/pglite-data/base/5/2674 +0 -0
  141. package/dist/server/pglite-data/base/5/2678 +0 -0
  142. package/dist/server/pglite-data/base/5/2679 +0 -0
  143. package/dist/server/pglite-data/base/5/2699 +0 -0
  144. package/dist/server/pglite-data/base/5/2701 +0 -0
  145. package/dist/server/pglite-data/base/5/2702 +0 -0
  146. package/dist/server/pglite-data/base/5/2703 +0 -0
  147. package/dist/server/pglite-data/base/5/2704 +0 -0
  148. package/dist/server/pglite-data/base/5/3455 +0 -0
  149. package/dist/server/pglite-data/base/5/pg_internal.init +0 -0
  150. package/dist/server/pglite-data/global/pg_control +0 -0
  151. package/dist/server/pglite-data/global/pg_internal.init +0 -0
  152. package/dist/server/pglite-data/pg_wal/000000010000000000000001 +0 -0
  153. package/dist/server/pglite-data/pg_xact/0000 +0 -0
  154. package/dist/server/pglite-data/postmaster.pid +2 -2
  155. package/dist/server/src/app.ts +2 -0
  156. package/dist/server/src/lib/run-task.ts +3 -2
  157. package/dist/server/src/routes/metrics.ts +81 -2
  158. package/dist/server/src/routes/prompt-versions.ts +46 -0
  159. package/dist/server/src/routes/tasks.ts +6 -2
  160. package/dist/web/dist/assets/index-BVjHH_u_.js +40 -0
  161. package/dist/web/dist/assets/index-BkzWwMOJ.js +40 -0
  162. package/dist/web/dist/assets/index-D4rY_mY0.css +1 -0
  163. package/dist/web/dist/assets/index-DHQbiQAM.css +1 -0
  164. package/dist/web/dist/index.html +2 -2
  165. package/package.json +14 -13
  166. /package/dist/server/pglite-data/base/5/{24576 → 16542} +0 -0
  167. /package/dist/server/pglite-data/base/5/{24587 → 16553} +0 -0
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 task of data) {
70
- current[task.id] = task;
71
- const prev = previousTasks.current[task.id];
69
+ for (const task2 of data) {
70
+ current[task2.id] = task2;
71
+ const prev = previousTasks.current[task2.id];
72
72
  if (!prev) {
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");
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");
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((task) => {
235
- const providerText = task.provider ? `${task.provider}/${task.model ?? ""}` : "-";
234
+ visible.map((task2) => {
235
+ const providerText = task2.provider ? `${task2.provider}/${task2.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(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() }) }),
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() }) }),
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(task.title, titleMax) }) })
243
- ] }, task.id);
242
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { children: truncate(task2.title, titleMax) }) })
243
+ ] }, task2.id);
244
244
  })
245
245
  ]
246
246
  }
@@ -318,6 +318,76 @@ 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
+
321
391
  // ../../apps/cli/src/index.ts
322
392
  import { program as program2 } from "commander";
323
393
 
@@ -328,10 +398,10 @@ import { Command } from "commander";
328
398
  import { program } from "commander";
329
399
  function getApiUrl() {
330
400
  const opts = program.opts();
331
- return opts.api ?? "http://localhost:4000";
401
+ return opts.api ?? process.env.HARNESS_API_URL ?? "http://localhost:4000";
332
402
  }
333
- async function apiFetch(path10, init) {
334
- const url = `${getApiUrl()}${path10}`;
403
+ async function apiFetch(path12, init) {
404
+ const url = `${getApiUrl()}${path12}`;
335
405
  const res = await fetch(url, init);
336
406
  const data = await res.json().catch(() => ({}));
337
407
  if (!res.ok) {
@@ -446,7 +516,7 @@ var taskFeedCmd = new Command2("feed").description("Feed a task into the harness
446
516
  // ../../apps/cli/src/commands/task.ts
447
517
  var taskCmd = new Command3("task").description("Manage tasks");
448
518
  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) => {
449
- const task = await apiFetch("/tasks", {
519
+ const task2 = await apiFetch("/tasks", {
450
520
  method: "POST",
451
521
  headers: { "Content-Type": "application/json" },
452
522
  body: JSON.stringify({
@@ -457,7 +527,7 @@ taskCmd.command("create").description("Create a task").requiredOption("--project
457
527
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : []
458
528
  })
459
529
  });
460
- console.log(JSON.stringify(task, null, 2));
530
+ console.log(JSON.stringify(task2, null, 2));
461
531
  });
462
532
  taskCmd.command("list").description("List tasks").option("--project <id>", "filter by project id").action(async (opts) => {
463
533
  const query = opts.project ? `?projectId=${opts.project}` : "";
@@ -472,14 +542,17 @@ taskCmd.addCommand(taskFeedCmd);
472
542
 
473
543
  // ../../apps/cli/src/commands/ui.ts
474
544
  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
475
551
  import { spawn } from "child_process";
476
552
  import { existsSync as existsSync2 } from "fs";
477
553
  import net from "net";
478
554
  import path2 from "path";
479
555
  import { fileURLToPath as fileURLToPath2 } from "url";
480
- import open from "open";
481
- import { render } from "ink";
482
- import React2 from "react";
483
556
  var __filename2 = fileURLToPath2(import.meta.url);
484
557
  var __dirname2 = path2.dirname(__filename2);
485
558
  var DEFAULT_PORT = 4e3;
@@ -489,7 +562,7 @@ var MAX_GRPC_PORT = 50061;
489
562
  function findAvailablePort(preferred = DEFAULT_PORT, max = MAX_PORT) {
490
563
  return new Promise((resolve, reject) => {
491
564
  if (preferred > max) {
492
- reject(new Error(`No available ports between ${DEFAULT_PORT.toString()} and ${max.toString()}`));
565
+ reject(new Error(`No available ports between ${preferred.toString()} and ${max.toString()}`));
493
566
  return;
494
567
  }
495
568
  const tester = net.createServer();
@@ -511,11 +584,11 @@ function findAvailablePort(preferred = DEFAULT_PORT, max = MAX_PORT) {
511
584
  });
512
585
  }
513
586
  function startBundledServer(env) {
514
- const realServerPath = path2.resolve(__dirname2, "server/dist/index.js");
587
+ const realServerPath = path2.resolve(__dirname2, "../../server/dist/index.js");
515
588
  if (existsSync2(realServerPath)) {
516
589
  return spawn(process.execPath, [realServerPath], { stdio: "inherit", env });
517
590
  }
518
- const legacyServerPath = path2.resolve(__dirname2, "server.js");
591
+ const legacyServerPath = path2.resolve(__dirname2, "../../server.js");
519
592
  if (existsSync2(legacyServerPath)) {
520
593
  return spawn(process.execPath, [legacyServerPath], { stdio: "inherit", env });
521
594
  }
@@ -537,9 +610,29 @@ async function waitForApi(apiUrl, maxMs = 3e4) {
537
610
  }
538
611
  throw new Error("Server did not become ready in time");
539
612
  }
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
540
633
  async function ensureProject(apiUrl) {
541
634
  const cwd = process.cwd();
542
- const name = path2.basename(cwd);
635
+ const name = path3.basename(cwd);
543
636
  try {
544
637
  const listRes = await fetch(`${apiUrl}/projects`);
545
638
  const projects = await listRes.json();
@@ -565,25 +658,16 @@ async function ensureProject(apiUrl) {
565
658
  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) => {
566
659
  const defaultApiUrl = process.env.HARNESS_API_URL ?? `http://localhost:${DEFAULT_PORT.toString()}`;
567
660
  let server;
661
+ let serverStarted = false;
568
662
  const alreadyRunning = await isApiReady(defaultApiUrl);
569
663
  const useTui = options.tui;
570
664
  let apiUrl = defaultApiUrl;
571
665
  if (!alreadyRunning) {
572
- const port = await findAvailablePort();
573
- const grpcPort = await findAvailablePort(DEFAULT_GRPC_PORT, MAX_GRPC_PORT);
574
- apiUrl = `http://localhost:${port.toString()}`;
666
+ const started = await startHarnessServer(process.env);
667
+ server = started.process;
668
+ apiUrl = started.apiUrl;
669
+ serverStarted = true;
575
670
  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
- });
587
671
  server.on("exit", (code) => {
588
672
  process.exit(code ?? 0);
589
673
  });
@@ -607,7 +691,7 @@ var uiCmd = new Command4("ui").description("Open the harness web UI").option("--
607
691
  const { TuiApp: TuiApp2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
608
692
  const { waitUntilExit } = render(React2.createElement(TuiApp2));
609
693
  await waitUntilExit();
610
- if (server && !alreadyRunning) {
694
+ if (serverStarted && server) {
611
695
  server.kill();
612
696
  }
613
697
  }
@@ -628,7 +712,7 @@ var consoleCmd = new Command5("console").description("Open the harness TUI conso
628
712
 
629
713
  // ../../apps/cli/src/commands/skill.ts
630
714
  import { Command as Command6 } from "commander";
631
- import path3 from "path";
715
+ import path4 from "path";
632
716
  import { fileURLToPath as fileURLToPath3 } from "url";
633
717
 
634
718
  // ../skills/src/parse.ts
@@ -695,7 +779,8 @@ export class ${className} {
695
779
 
696
780
  async execute(args: Record<string, unknown>): Promise<string> {
697
781
  const prompt = ${instructionsLiteral} + '\\n\\nArgs:\\n' + JSON.stringify(args, null, 2);
698
- return this.provider.send(prompt, { system: ${descriptionLiteral} });
782
+ const response = await this.provider.send(prompt, { system: ${descriptionLiteral} });
783
+ return response.content;
699
784
  }
700
785
  }
701
786
  `;
@@ -730,22 +815,22 @@ async function registerSkill(sourcePath, outputDir) {
730
815
  // ../../apps/cli/src/commands/skill.ts
731
816
  function getOutputDir() {
732
817
  if (process.env.OMEGA_HARNESS_ROOT) {
733
- return path3.join(path3.resolve(process.env.OMEGA_HARNESS_ROOT), "packages/skills/src");
818
+ return path4.join(path4.resolve(process.env.OMEGA_HARNESS_ROOT), "packages/skills/src");
734
819
  }
735
820
  try {
736
821
  const __filename3 = fileURLToPath3(import.meta.url);
737
- const __dirname3 = path3.dirname(__filename3);
738
- return path3.join(path3.resolve(__dirname3, "../../../.."), "packages/skills/src");
822
+ const __dirname4 = path4.dirname(__filename3);
823
+ return path4.join(path4.resolve(__dirname4, "../../../.."), "packages/skills/src");
739
824
  } catch {
740
- return path3.join(process.cwd(), "harness-skills");
825
+ return path4.join(process.cwd(), "harness-skills");
741
826
  }
742
827
  }
743
828
  var skillCmd = new Command6("skill").description("Manage skill artifacts");
744
829
  skillCmd.command("generate").description("Generate a harness adapter from a SKILL.md file").argument("<source>", "path to SKILL.md").action(async (source) => {
745
830
  const outputDir = getOutputDir();
746
- const manifest = await registerSkill(path3.resolve(source), outputDir);
831
+ const manifest = await registerSkill(path4.resolve(source), outputDir);
747
832
  console.log(`Generated adapter for skill: ${manifest.name}`);
748
- console.log(`Output directory: ${path3.join(outputDir, "generated")}`);
833
+ console.log(`Output directory: ${path4.join(outputDir, "generated")}`);
749
834
  });
750
835
 
751
836
  // ../../apps/cli/src/commands/agent.ts
@@ -755,7 +840,7 @@ agentCmd.command("run").description("Run a single agent task").requiredOption("-
755
840
  async (opts) => {
756
841
  const tags = ["agent"];
757
842
  if (opts.autoPublish) tags.push("publish");
758
- const task = await apiFetch("/tasks", {
843
+ const task2 = await apiFetch("/tasks", {
759
844
  method: "POST",
760
845
  headers: { "Content-Type": "application/json" },
761
846
  body: JSON.stringify({
@@ -766,8 +851,8 @@ agentCmd.command("run").description("Run a single agent task").requiredOption("-
766
851
  tags
767
852
  })
768
853
  });
769
- console.log(JSON.stringify(task, null, 2));
770
- const result = await apiFetch(`/tasks/${task.id}/run`, { method: "POST" });
854
+ console.log(JSON.stringify(task2, null, 2));
855
+ const result = await apiFetch(`/tasks/${task2.id}/run`, { method: "POST" });
771
856
  console.log(JSON.stringify(result, null, 2));
772
857
  }
773
858
  );
@@ -779,7 +864,7 @@ agentCmd.command("loop").description("Continuously run agent tasks").requiredOpt
779
864
  const title = opts.prompt ?? "Improve the Omega harness: fix a TODO, add a test, refactor a function, or improve documentation";
780
865
  const tags = ["agent", "self-improve"];
781
866
  if (opts.autoPublish) tags.push("publish");
782
- const task = await apiFetch("/tasks", {
867
+ const task2 = await apiFetch("/tasks", {
783
868
  method: "POST",
784
869
  headers: { "Content-Type": "application/json" },
785
870
  body: JSON.stringify({
@@ -790,8 +875,8 @@ agentCmd.command("loop").description("Continuously run agent tasks").requiredOpt
790
875
  tags
791
876
  })
792
877
  });
793
- console.log(`Created task ${task.id}`);
794
- const result = await apiFetch(`/tasks/${task.id}/run`, { method: "POST" });
878
+ console.log(`Created task ${task2.id}`);
879
+ const result = await apiFetch(`/tasks/${task2.id}/run`, { method: "POST" });
795
880
  console.log(JSON.stringify(result, null, 2));
796
881
  return result;
797
882
  };
@@ -819,7 +904,9 @@ agentCmd.command("diffs").description("Show diffs for a task").argument("<id>",
819
904
 
820
905
  // ../../apps/cli/src/commands/bench.ts
821
906
  import { Command as Command8 } from "commander";
822
- import path9 from "node:path";
907
+ import fs8 from "node:fs/promises";
908
+ import path11 from "node:path";
909
+ import os from "node:os";
823
910
 
824
911
  // ../bench/dist/api-client.js
825
912
  var ApiError = class extends Error {
@@ -836,15 +923,15 @@ async function apiFetch2(url, init) {
836
923
  }
837
924
  return res.json();
838
925
  }
839
- async function ensureProject2(apiUrl, name, path10) {
926
+ async function ensureProject2(apiUrl, name, path12) {
840
927
  const res = await fetch(`${apiUrl}/projects`, {
841
928
  method: "POST",
842
929
  headers: { "Content-Type": "application/json" },
843
- body: JSON.stringify({ name, path: path10 })
930
+ body: JSON.stringify({ name, path: path12 })
844
931
  });
845
932
  if (res.status === 409) {
846
- const projects = await fetch(`${apiUrl}/projects?path=${encodeURIComponent(path10)}`).then((r) => r.json());
847
- const existing = projects.find((p) => p.path === path10);
933
+ const projects = await fetch(`${apiUrl}/projects?path=${encodeURIComponent(path12)}`).then((r) => r.json());
934
+ const existing = projects.find((p) => p.path === path12);
848
935
  if (existing)
849
936
  return existing;
850
937
  }
@@ -861,7 +948,9 @@ async function createTask(apiUrl, projectId, title, options = {}) {
861
948
  title,
862
949
  description: options.description,
863
950
  complexity: options.complexity ?? "simple",
864
- tags: options.tags ?? ["benchmark"]
951
+ tags: options.tags ?? ["benchmark"],
952
+ provider: options.provider,
953
+ model: options.model
865
954
  })
866
955
  });
867
956
  }
@@ -874,9 +963,9 @@ async function getTask(apiUrl, taskId) {
874
963
  async function waitForTask(apiUrl, taskId, timeoutMs = 12e4) {
875
964
  const deadline = Date.now() + timeoutMs;
876
965
  while (Date.now() < deadline) {
877
- const task = await getTask(apiUrl, taskId);
878
- if (task.status === "done" || task.status === "failed")
879
- return task;
966
+ const task2 = await getTask(apiUrl, taskId);
967
+ if (task2.status === "done" || task2.status === "failed")
968
+ return task2;
880
969
  await new Promise((r) => setTimeout(r, 300));
881
970
  }
882
971
  return { status: "timeout", error: "Task did not finish in time" };
@@ -914,8 +1003,169 @@ function countSpans(node) {
914
1003
 
915
1004
  // ../bench/dist/runner.js
916
1005
  import fs from "node:fs/promises";
917
- import path4 from "node:path";
1006
+ import path5 from "node:path";
918
1007
  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
919
1169
  function countAllSpans(traceFlow) {
920
1170
  if (!traceFlow)
921
1171
  return 0;
@@ -933,6 +1183,13 @@ function ensureGitRepo(repoPath) {
933
1183
  execSync("git add .", { cwd: repoPath, stdio: "ignore" });
934
1184
  execSync('git commit -m "bench init"', { cwd: repoPath, stdio: "ignore" });
935
1185
  }
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
+ }
936
1193
  async function runBenchmark(tasks, options) {
937
1194
  const { apiUrl, suiteName, timeoutMs = 12e4, projectPrefix = "bench" } = options;
938
1195
  const report = {
@@ -943,11 +1200,12 @@ async function runBenchmark(tasks, options) {
943
1200
  failed: 0,
944
1201
  timeouts: 0,
945
1202
  totalDurationMs: 0,
1203
+ totalUsage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
946
1204
  results: []
947
1205
  };
948
- const baseDir = path4.join("/tmp", `omega-bench-${String(Date.now())}`);
1206
+ const baseDir = path5.join("/tmp", `omega-bench-${String(Date.now())}`);
949
1207
  await fs.mkdir(baseDir, { recursive: true });
950
- for (const task of tasks) {
1208
+ for (const task2 of tasks) {
951
1209
  const start = Date.now();
952
1210
  let harnessTaskId = "";
953
1211
  let status = "failed";
@@ -958,17 +1216,17 @@ async function runBenchmark(tasks, options) {
958
1216
  let projectId = "";
959
1217
  let projectPath = "";
960
1218
  try {
961
- projectPath = path4.join(baseDir, task.id);
1219
+ projectPath = path5.join(baseDir, task2.id);
962
1220
  await fs.mkdir(projectPath, { recursive: true });
963
- if (task.setup) {
964
- await task.setup(projectPath);
1221
+ if (task2.setup) {
1222
+ await task2.setup(projectPath);
965
1223
  }
966
1224
  ensureGitRepo(projectPath);
967
- const project = await ensureProject2(apiUrl, `${projectPrefix}-${task.id}`, projectPath);
1225
+ const project = await ensureProject2(apiUrl, `${projectPrefix}-${task2.id}`, projectPath);
968
1226
  projectId = project.id;
969
- const harnessTask = await createTask(apiUrl, project.id, task.title, {
970
- description: task.description,
971
- complexity: task.complexity ?? "simple",
1227
+ const harnessTask = await createTask(apiUrl, project.id, task2.title, {
1228
+ description: task2.description,
1229
+ complexity: task2.complexity ?? "simple",
972
1230
  tags: ["benchmark", "agent"]
973
1231
  });
974
1232
  harnessTaskId = harnessTask.id;
@@ -980,7 +1238,7 @@ async function runBenchmark(tasks, options) {
980
1238
  getDiffs(apiUrl, harnessTask.id),
981
1239
  getTraceFlow(apiUrl, harnessTask.id)
982
1240
  ]);
983
- evaluation = await task.evaluate({
1241
+ evaluation = await task2.evaluate({
984
1242
  apiUrl,
985
1243
  taskId: harnessTask.id,
986
1244
  projectPath,
@@ -996,14 +1254,21 @@ async function runBenchmark(tasks, options) {
996
1254
  };
997
1255
  }
998
1256
  const durationMs = Date.now() - start;
1257
+ const usage = agentRun ? {
1258
+ promptTokens: agentRun.promptTokens,
1259
+ completionTokens: agentRun.completionTokens,
1260
+ totalTokens: agentRun.totalTokens
1261
+ } : void 0;
999
1262
  const result = {
1000
- task,
1263
+ task: task2,
1001
1264
  harnessTaskId,
1002
1265
  durationMs,
1003
1266
  status,
1004
1267
  evaluation,
1005
1268
  agentRun,
1006
- spanCount: countAllSpans(traceFlow)
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)
1007
1272
  };
1008
1273
  if (status === "timeout")
1009
1274
  report.timeouts++;
@@ -1012,6 +1277,7 @@ async function runBenchmark(tasks, options) {
1012
1277
  else
1013
1278
  report.failed++;
1014
1279
  report.totalDurationMs += durationMs;
1280
+ report.totalUsage = sumUsage(report.totalUsage, usage);
1015
1281
  report.results.push(result);
1016
1282
  options.onProgress?.(result);
1017
1283
  }
@@ -1020,7 +1286,7 @@ async function runBenchmark(tasks, options) {
1020
1286
 
1021
1287
  // ../bench/dist/report.js
1022
1288
  import fs2 from "node:fs/promises";
1023
- import path5 from "node:path";
1289
+ import path6 from "node:path";
1024
1290
  function nowIso() {
1025
1291
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1026
1292
  }
@@ -1033,16 +1299,21 @@ function resultLine(r, idx) {
1033
1299
  const symbol = r.evaluation.passed ? "\u2713" : "\u2717";
1034
1300
  const status = r.status === "timeout" ? "timeout" : r.status;
1035
1301
  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}]` : "";
1036
1304
  const msg = r.evaluation.message ? ` \u2014 ${r.evaluation.message}` : "";
1037
- return `${String(idx + 1)}. ${symbol} ${r.task.name} [${status}] ${formatDuration(r.durationMs)}${score}${msg}`;
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)`;
1038
1309
  }
1039
1310
  async function writeReport(report, outputDir = ".omega/reports") {
1040
1311
  await fs2.mkdir(outputDir, { recursive: true });
1041
1312
  const ts = nowIso();
1042
- const jsonFile = path5.join(outputDir, `benchmark-${ts}.json`);
1043
- const mdFile = path5.join(outputDir, `benchmark-${ts}.md`);
1044
- await fs2.writeFile(jsonFile, JSON.stringify(report, null, 2), "utf-8");
1045
- const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
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;
1046
1317
  const md = [
1047
1318
  "# Omega Benchmark Report",
1048
1319
  "",
@@ -1052,8 +1323,9 @@ async function writeReport(report, outputDir = ".omega/reports") {
1052
1323
  `- Passed: ${String(report.passed)}`,
1053
1324
  `- Failed: ${String(report.failed)}`,
1054
1325
  `- Timeouts: ${String(report.timeouts)}`,
1055
- `- Pass rate: ${String(passRate)}%`,
1326
+ `- Pass rate: ${String(passRate2)}%`,
1056
1327
  `- Total duration: ${formatDuration(report.totalDurationMs)}`,
1328
+ `- ${usageLine(report.totalUsage)}`,
1057
1329
  "",
1058
1330
  "## Results",
1059
1331
  "",
@@ -1067,31 +1339,69 @@ async function writeReport(report, outputDir = ".omega/reports") {
1067
1339
  ""
1068
1340
  ].join("\n");
1069
1341
  await fs2.writeFile(mdFile, md, "utf-8");
1070
- return jsonFile;
1342
+ return jsonFile2;
1071
1343
  }
1072
1344
  function printSummary(report) {
1073
- const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1345
+ const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1074
1346
  console.log(`
1075
1347
  Benchmark: ${report.suite}`);
1076
1348
  console.log(`Total: ${String(report.total)} | Passed: ${String(report.passed)} | Failed: ${String(report.failed)} | Timeouts: ${String(report.timeouts)}`);
1077
- console.log(`Pass rate: ${String(passRate)}%`);
1349
+ console.log(`Pass rate: ${String(passRate2)}%`);
1078
1350
  console.log(`Duration: ${formatDuration(report.totalDurationMs)}`);
1351
+ console.log(usageLine(report.totalUsage));
1079
1352
  for (const r of report.results) {
1080
1353
  console.log(resultLine(r, report.results.indexOf(r)));
1081
1354
  }
1082
1355
  }
1083
1356
 
1084
1357
  // ../bench/dist/suites/synthetic.js
1358
+ import fs4 from "node:fs/promises";
1359
+
1360
+ // ../bench/dist/suites/builder.js
1085
1361
  import fs3 from "node:fs/promises";
1086
- import path6 from "node:path";
1087
- async function writeJson(projectPath, file, data) {
1088
- await fs3.writeFile(path6.join(projectPath, file), JSON.stringify(data, null, 2), "utf-8");
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
+ };
1089
1373
  }
1090
- async function writeFile2(projectPath, file, content) {
1091
- await fs3.mkdir(path6.dirname(path6.join(projectPath, file)), { recursive: true });
1092
- await fs3.writeFile(path6.join(projectPath, file), content, "utf-8");
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");
1394
+ }
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
+ `);
1093
1403
  }
1094
- function parseValidation(ctx) {
1404
+ function readValidation(ctx) {
1095
1405
  if (!ctx.agentRun?.validationSummary)
1096
1406
  return { allPassed: false };
1097
1407
  try {
@@ -1101,142 +1411,598 @@ function parseValidation(ctx) {
1101
1411
  return { allPassed: false };
1102
1412
  }
1103
1413
  }
1104
- function validate(ctx) {
1105
- const v = parseValidation(ctx);
1106
- return {
1107
- passed: v.allPassed,
1108
- message: v.allPassed ? "Validation passed" : "Validation failed"
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
+ };
1109
1421
  };
1110
1422
  }
1111
- function basePackage() {
1112
- return {
1113
- name: "synthetic-bench-project",
1114
- version: "1.0.0",
1115
- type: "module",
1116
- scripts: {
1117
- lint: "node lint.js",
1118
- test: "node test.js",
1119
- build: "node build.js"
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
+ };
1434
+ };
1435
+ }
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;
1120
1442
  }
1443
+ return { passed: true, message: "All checks passed" };
1121
1444
  };
1122
1445
  }
1123
- var TASKS = [
1124
- {
1125
- id: "greet-function",
1126
- name: "Add a greet function",
1127
- title: "Add greet.js with a greet function",
1128
- description: "Create a `greet.js` file that exports a function `greet(name)` returning `Hello, <name>!`. The existing test expects this function.",
1129
- complexity: "simple",
1130
- setup: async (projectPath) => {
1131
- await writeJson(projectPath, "package.json", basePackage());
1132
- await writeFile2(projectPath, "test.js", `import { greet } from './greet.js';
1133
- import assert from 'node:assert';
1134
- assert.strictEqual(greet('World'), 'Hello, World!');
1135
- console.log('greet test passed');
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');
1136
1486
  `);
1137
- await writeFile2(projectPath, "lint.js", `console.log('lint ok');
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');
1138
1501
  `);
1139
- await writeFile2(projectPath, "build.js", `console.log('build ok');
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');
1140
1516
  `);
1141
- },
1142
- evaluate: (ctx) => Promise.resolve(validate(ctx))
1143
- },
1144
- {
1145
- id: "fix-lint-warning",
1146
- name: "Fix a lint warning",
1147
- title: "Remove unused variable flagged by linter",
1148
- description: "The custom linter flags any use of `var`. Update `src/index.js` so the lint script passes while keeping the greeting behavior.",
1149
- complexity: "simple",
1150
- setup: async (projectPath) => {
1151
- await writeJson(projectPath, "package.json", basePackage());
1152
- await writeFile2(projectPath, "src/index.js", `var greeting = 'Hello';
1153
- export function hi(name) { return greeting + ', ' + name + '!'; }
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; }
1598
+ `);
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('@'); }
1154
1615
  `);
1155
- await writeFile2(projectPath, "test.js", `import { hi } from './src/index.js';
1156
- import assert from 'node:assert';
1157
- assert.strictEqual(hi('Omega'), 'Hello, Omega!');
1158
- console.log('hi test passed');
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; }
1159
1637
  `);
1160
- await writeFile2(projectPath, "lint.js", `import fs from 'node:fs/promises';
1638
+ await codeFile(projectPath, "lint.js", `import fs from 'node:fs/promises';
1161
1639
  const code = await fs.readFile('./src/index.js', 'utf-8');
1162
- if (code.includes('var ')) { console.error('lint failed: var keyword found'); process.exit(1); }
1640
+ if (code.includes('var ')) { console.error('var keyword found'); process.exit(1); }
1163
1641
  console.log('lint ok');
1164
1642
  `);
1165
- await writeFile2(projectPath, "build.js", `console.log('build ok');
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; }
1166
1657
  `);
1167
- },
1168
- evaluate: (ctx) => Promise.resolve(validate(ctx))
1169
- },
1170
- {
1171
- id: "update-config",
1172
- name: "Update a config value",
1173
- title: "Update timeout value in config.json",
1174
- description: "Change `config.json` so that `timeout` is `5000` instead of `1000`. The test will read the file and assert the value.",
1175
- complexity: "simple",
1176
- setup: async (projectPath) => {
1177
- await writeJson(projectPath, "package.json", basePackage());
1178
- await writeJson(projectPath, "config.json", { timeout: 1e3, retries: 3 });
1179
- await writeFile2(projectPath, "test.js", `import { readFile } from 'node:fs/promises';
1180
- const cfg = JSON.parse(await readFile('./config.json', 'utf-8'));
1181
- if (cfg.timeout !== 5000) { console.error('timeout is ' + cfg.timeout); process.exit(1); }
1182
- console.log('config test passed');
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; }
1183
1673
  `);
1184
- await writeFile2(projectPath, "lint.js", `console.log('lint ok');
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()); }
1185
1687
  `);
1186
- await writeFile2(projectPath, "build.js", `console.log('build ok');
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; }
1187
1707
  `);
1188
- },
1189
- evaluate: (ctx) => Promise.resolve(validate(ctx))
1190
- },
1191
- {
1192
- id: "extract-constant",
1193
- name: "Extract a magic constant",
1194
- title: "Extract MAX_RETRIES constant",
1195
- 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.",
1196
- complexity: "medium",
1197
- setup: async (projectPath) => {
1198
- await writeJson(projectPath, "package.json", basePackage());
1199
- await writeFile2(projectPath, "src/retry.js", `export function attempts() { return 3; }
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'; }
1200
1721
  `);
1201
- await writeFile2(projectPath, "test.js", `import fs from 'node:fs/promises';
1202
- import { attempts } from './src/retry.js';
1203
- const code = await fs.readFile('./src/retry.js', 'utf-8');
1204
- if (!code.includes('const MAX_RETRIES')) { console.error('MAX_RETRIES constant not found'); process.exit(1); }
1205
- if (attempts() !== 3) { console.error('attempts() changed'); process.exit(1); }
1206
- console.log('constant test passed');
1722
+ await codeFile(projectPath, "src/main.js", `import { greet } from './helper.js';
1723
+ export { greet };
1724
+ `);
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' }) }; }
1207
1765
  `);
1208
- await writeFile2(projectPath, "lint.js", `console.log('lint ok');
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; }
1209
1810
  `);
1210
- await writeFile2(projectPath, "build.js", `console.log('build ok');
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');
1211
1815
  `);
1212
- },
1213
- evaluate: (ctx) => Promise.resolve(validate(ctx))
1214
- },
1215
- {
1216
- id: "noop-validation",
1217
- name: "Run validation on a clean project",
1218
- title: "Confirm the project already passes validation",
1219
- description: "This project already passes lint, test and build. Use the publish tool to confirm validation passes and finish.",
1220
- complexity: "simple",
1221
- setup: async (projectPath) => {
1222
- await writeJson(projectPath, "package.json", basePackage());
1223
- await writeFile2(projectPath, "test.js", `console.log('test ok');
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; }
1224
1827
  `);
1225
- await writeFile2(projectPath, "lint.js", `console.log('lint ok');
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');
1226
1848
  `);
1227
- await writeFile2(projectPath, "build.js", `console.log('build ok');
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');
1228
1877
  `);
1229
- },
1230
- evaluate: (ctx) => Promise.resolve(validate(ctx))
1231
- }
1232
- ];
1233
- function syntheticSuite() {
1234
- return TASKS;
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); }
1892
+ `);
1893
+ await codeFile(projectPath, "src/b.js", `function formatDate(d) { return d.toISOString().split('T')[0]; }
1894
+ export function runB(d) { return formatDate(d); }
1895
+ `);
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;
1917
+ `);
1918
+ await codeFile(projectPath, "src/b.js", `import { valueA } from './a.js';
1919
+ export const valueB = 'B' + valueA;
1920
+ `);
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.
1938
+ `);
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 + '!'; }
1973
+ `);
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');
1978
+ `);
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
+ ];
1235
2001
  }
1236
2002
 
1237
2003
  // ../bench/dist/adapters/deepswe.js
1238
- import fs4 from "node:fs/promises";
1239
- import path7 from "node:path";
2004
+ import fs5 from "node:fs/promises";
2005
+ import path8 from "node:path";
1240
2006
  import { execFile } from "node:child_process";
1241
2007
  import { promisify } from "node:util";
1242
2008
  var execFileAsync = promisify(execFile);
@@ -1278,18 +2044,18 @@ function mulberry32(seed) {
1278
2044
  };
1279
2045
  }
1280
2046
  async function readTask(taskDir) {
1281
- const tomlRaw = await fs4.readFile(path7.join(taskDir, "task.toml"), "utf-8");
1282
- const instructionRaw = await fs4.readFile(path7.join(taskDir, "instruction.md"), "utf-8");
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");
1283
2049
  return { toml: parseToml(tomlRaw), instruction: instructionRaw };
1284
2050
  }
1285
2051
  async function cloneRepo(repoUrl, commit, targetPath) {
1286
- await fs4.mkdir(path7.dirname(targetPath), { recursive: true });
2052
+ await fs5.mkdir(path8.dirname(targetPath), { recursive: true });
1287
2053
  await execFileAsync("git", ["clone", repoUrl, targetPath], { timeout: 12e4 });
1288
2054
  await execFileAsync("git", ["-C", targetPath, "checkout", commit], { timeout: 6e4 });
1289
2055
  }
1290
2056
  async function loadDeepSWESuite(options) {
1291
- const entries = await fs4.readdir(options.tasksDir, { withFileTypes: true });
1292
- const taskDirs = entries.filter((e) => e.isDirectory()).map((e) => path7.join(options.tasksDir, e.name));
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));
1293
2059
  let loaded = [];
1294
2060
  for (const dir of taskDirs) {
1295
2061
  try {
@@ -1300,7 +2066,7 @@ async function loadDeepSWESuite(options) {
1300
2066
  }
1301
2067
  if (options.taskIds && options.taskIds.length > 0) {
1302
2068
  loaded = loaded.filter((t) => {
1303
- const id = t.toml.metadata?.task_id ?? path7.basename(t.dir);
2069
+ const id = t.toml.metadata?.task_id ?? path8.basename(t.dir);
1304
2070
  return options.taskIds?.includes(id);
1305
2071
  });
1306
2072
  }
@@ -1311,7 +2077,7 @@ async function loadDeepSWESuite(options) {
1311
2077
  }
1312
2078
  const tasks = [];
1313
2079
  for (const { dir, toml, instruction } of loaded) {
1314
- const id = toml.metadata?.task_id ?? path7.basename(dir);
2080
+ const id = toml.metadata?.task_id ?? path8.basename(dir);
1315
2081
  const title = toml.metadata?.display_title ?? toml.metadata?.original_title ?? id;
1316
2082
  const repo = toml.metadata?.repository_url;
1317
2083
  const commit = toml.metadata?.base_commit_hash;
@@ -1342,65 +2108,110 @@ async function loadDeepSWESuite(options) {
1342
2108
  }
1343
2109
 
1344
2110
  // ../bench/dist/optimise.js
1345
- import fs5 from "node:fs/promises";
1346
- import path8 from "node:path";
2111
+ import fs6 from "node:fs/promises";
2112
+ import path9 from "node:path";
1347
2113
  async function findLatestReport(outputDir) {
1348
- const entries = await fs5.readdir(outputDir).catch(() => []);
2114
+ const entries = await fs6.readdir(outputDir).catch(() => []);
1349
2115
  const files = entries.filter((f) => f.startsWith("benchmark-") && f.endsWith(".json")).sort().reverse();
1350
2116
  if (files.length === 0)
1351
2117
  return void 0;
1352
- const raw = await fs5.readFile(path8.join(outputDir, files[0]), "utf-8");
2118
+ const raw = await fs6.readFile(path9.join(outputDir, files[0]), "utf-8");
1353
2119
  return JSON.parse(raw);
1354
2120
  }
1355
- function summariseTraceFlow(report, maxLines = 60) {
1356
- const lines = report.split("\n");
1357
- if (lines.length <= maxLines)
1358
- return report;
1359
- return lines.slice(0, maxLines).join("\n") + "\n... (truncated)";
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);
1360
2139
  }
1361
2140
  function buildOptimisePrompt(report, failedResult, traceFlowText) {
1362
- const passRate = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1363
- const failed = report.results.filter((r) => !r.evaluation.passed);
2141
+ const passRate2 = report.total > 0 ? Math.round(report.passed / report.total * 100) : 0;
1364
2142
  const lines = [];
1365
- lines.push("# Prompt optimisation task");
2143
+ lines.push("Task: Improve the Omega agent prompts in packages/agent/src/prompts.ts");
1366
2144
  lines.push("");
1367
- lines.push(`The latest benchmark run achieved ${String(passRate)}% pass rate (${String(report.passed)}/${String(report.total)}).`);
2145
+ lines.push(`The latest benchmark run achieved ${String(passRate2)}% pass rate (${String(report.passed)}/${String(report.total)}).`);
1368
2146
  lines.push("");
1369
- lines.push("## Failed tasks");
1370
- for (const r of failed.slice(0, 5)) {
1371
- lines.push(`- ${r.task.name}: ${r.evaluation.message ?? "no message"}`);
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
+ }
1372
2155
  }
1373
2156
  lines.push("");
1374
2157
  if (failedResult) {
1375
- lines.push(`## Focus task: ${failedResult.task.name}`);
1376
- lines.push(`Status: ${failedResult.status}`);
1377
- lines.push(`Evaluation: ${failedResult.evaluation.message ?? "none"}`);
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
+ }
1378
2168
  lines.push("");
1379
2169
  }
1380
2170
  if (traceFlowText) {
1381
- lines.push("## Trace flow snapshot");
1382
- lines.push("```json");
1383
- lines.push(summariseTraceFlow(traceFlowText));
1384
- lines.push("```");
1385
- lines.push("");
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
+ }
1386
2180
  }
1387
2181
  lines.push("## Instructions");
1388
- 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.");
1389
- lines.push("Rules:");
1390
- lines.push("- Keep changes minimal and targeted.");
1391
- lines.push("- Do not break existing tool signatures or system behaviour.");
1392
- lines.push("- Run `pnpm lint` and `pnpm test` after edits.");
1393
- lines.push("- Finish with a summary of what changed and why it should help.");
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.");
1394
2198
  return lines.join("\n");
1395
2199
  }
1396
2200
  async function loadOptimisationContext(apiUrl, outputDir) {
1397
2201
  const report = await findLatestReport(outputDir);
1398
2202
  if (!report)
1399
2203
  return void 0;
1400
- const failedResult = report.results.find((r) => !r.evaluation.passed);
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);
1401
2212
  let traceFlowText;
1402
2213
  if (failedResult?.harnessTaskId) {
1403
- const traceFlow = await getTraceFlow(apiUrl, failedResult.harnessTaskId);
2214
+ const traceFlow = traceFlows.get(failedResult.harnessTaskId);
1404
2215
  if (traceFlow) {
1405
2216
  traceFlowText = JSON.stringify(traceFlow, null, 2);
1406
2217
  }
@@ -1425,6 +2236,92 @@ async function submitOptimiseTask(apiUrl, projectId, prompt) {
1425
2236
  return res.json();
1426
2237
  }
1427
2238
 
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
+
1428
2325
  // ../../apps/cli/src/commands/bench.ts
1429
2326
  async function waitForApi2(apiUrl, maxMs = 1e4) {
1430
2327
  const deadline = Date.now() + maxMs;
@@ -1440,10 +2337,10 @@ async function waitForApi2(apiUrl, maxMs = 1e4) {
1440
2337
  }
1441
2338
  function currentProject(apiUrl) {
1442
2339
  const cwd = process.cwd();
1443
- const name = path9.basename(cwd);
2340
+ const name = path11.basename(cwd);
1444
2341
  return ensureProject2(apiUrl, `bench-${name}`, cwd);
1445
2342
  }
1446
- 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").action(async (opts) => {
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) => {
1447
2344
  const apiUrl = getApiUrl();
1448
2345
  await waitForApi2(apiUrl);
1449
2346
  const timeoutMs = Number(opts.timeout);
@@ -1461,6 +2358,9 @@ var runCmd = new Command8("run").description("Run a benchmark suite").option("--
1461
2358
  suiteName = "deep-swe";
1462
2359
  } else if (opts.suite === "synthetic") {
1463
2360
  tasks = syntheticSuite();
2361
+ if (opts.nTasks !== void 0 && opts.nTasks > 0) {
2362
+ tasks = tasks.slice(0, opts.nTasks);
2363
+ }
1464
2364
  suiteName = "synthetic";
1465
2365
  } else {
1466
2366
  throw new Error(`Unknown suite: ${opts.suite}`);
@@ -1492,17 +2392,254 @@ var optimiseCmd = new Command8("optimise").description("Create a self-improve ta
1492
2392
  console.log("No benchmark report found. Run `omega bench run` first.");
1493
2393
  return;
1494
2394
  }
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
+ }
1495
2415
  const project = await currentProject(apiUrl);
1496
2416
  const prompt = buildOptimisePrompt(
1497
2417
  context.report,
1498
2418
  context.failedResult,
1499
2419
  context.traceFlowText
1500
2420
  );
1501
- const task = await submitOptimiseTask(apiUrl, project.id, prompt);
1502
- console.log(`Created self-improve task ${task.id}`);
1503
- console.log(`Run \`omega task run ${task.id}\` to execute it.`);
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
+ }
1504
2641
  });
1505
- var benchCmd = new Command8("bench").description("Run benchmarks and optimise prompts").addCommand(runCmd).addCommand(optimiseCmd);
2642
+ var benchCmd = new Command8("bench").description("Run benchmarks and optimise prompts").addCommand(runCmd).addCommand(abCmd).addCommand(optimiseCmd).addCommand(loopCmd);
1506
2643
 
1507
2644
  // ../../apps/cli/src/index.ts
1508
2645
  program2.name("harness").description("Omega harness CLI").version("0.1.0").option("--api <url>", "API base URL", "http://localhost:4000");