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