Skip to content

Commit b44a243

Browse files
committed
feat: implement meta-harness functionality with harness loader, presets, and validation
- Add harnessLoader.ts for loading and validating harness candidates. - Introduce metaHarness.ts for managing meta-harness execution and context building. - Create presets.ts to define built-in harness candidates with policies. - Define types in types.ts for harness candidates and validation results. - Implement tests for harness loader, harness applier, and meta-harness functionality. - Ensure proper handling of file operations and context management in tests.
1 parent dfdc152 commit b44a243

17 files changed

Lines changed: 2050 additions & 2 deletions

src/cli/args.ts

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export type CliAction =
55
| { kind: "web"; host?: string; port?: number }
66
| { kind: "compare-analysis"; runId: string; limit: number; judge: boolean }
77
| { kind: "eval-harness"; runIds: string[]; limit: number; outputPath?: string; noHistory?: boolean }
8+
| { kind: "evolve"; maxCycles: number; target: "skills" | "prompts" | "all"; dryRun: boolean }
9+
| { kind: "meta-harness"; runs: number; nodes: ("analyze_results" | "review")[]; noApply: boolean; dryRun: boolean }
810
| { kind: "help" }
911
| { kind: "version" }
1012
| { kind: "error"; message: string };
@@ -156,10 +158,113 @@ export function resolveCliAction(args: string[]): CliAction {
156158
return { kind: "eval-harness", runIds, limit, outputPath, noHistory };
157159
}
158160

161+
if (first === "evolve") {
162+
let maxCycles = 3;
163+
let target: "skills" | "prompts" | "all" = "all";
164+
let dryRun = false;
165+
for (let index = 1; index < args.length; index += 1) {
166+
const token = args[index];
167+
if (token === "--max-cycles") {
168+
const value = args[index + 1];
169+
if (!value) {
170+
return { kind: "error", message: "Missing value for --max-cycles." };
171+
}
172+
const parsed = Number(value);
173+
if (!Number.isFinite(parsed) || parsed <= 0) {
174+
return { kind: "error", message: `Invalid max cycle count: ${value}` };
175+
}
176+
maxCycles = Math.floor(parsed);
177+
index += 1;
178+
continue;
179+
}
180+
if (token === "--target") {
181+
const value = args[index + 1];
182+
if (!value) {
183+
return { kind: "error", message: "Missing value for --target." };
184+
}
185+
if (value !== "skills" && value !== "prompts" && value !== "all") {
186+
return {
187+
kind: "error",
188+
message: `Unsupported evolve target: ${value}. Expected one of skills, prompts, all.`
189+
};
190+
}
191+
target = value;
192+
index += 1;
193+
continue;
194+
}
195+
if (token === "--dry-run") {
196+
dryRun = true;
197+
continue;
198+
}
199+
return {
200+
kind: "error",
201+
message: `Unsupported evolve argument: ${token}`
202+
};
203+
}
204+
return { kind: "evolve", maxCycles, target, dryRun };
205+
}
206+
207+
if (first === "meta-harness") {
208+
let runs = 5;
209+
const nodes: ("analyze_results" | "review")[] = [];
210+
let noApply = false;
211+
let dryRun = false;
212+
for (let index = 1; index < args.length; index += 1) {
213+
const token = args[index];
214+
if (token === "--runs") {
215+
const value = args[index + 1];
216+
if (!value) {
217+
return { kind: "error", message: "Missing value for --runs." };
218+
}
219+
const parsed = Number(value);
220+
if (!Number.isFinite(parsed) || parsed <= 0) {
221+
return { kind: "error", message: `Invalid run count: ${value}` };
222+
}
223+
runs = Math.floor(parsed);
224+
index += 1;
225+
continue;
226+
}
227+
if (token === "--node") {
228+
const value = args[index + 1];
229+
if (!value) {
230+
return { kind: "error", message: "Missing value for --node." };
231+
}
232+
if (value !== "analyze_results" && value !== "review") {
233+
return {
234+
kind: "error",
235+
message: `Unsupported meta-harness node: ${value}. Expected analyze_results or review.`
236+
};
237+
}
238+
nodes.push(value);
239+
index += 1;
240+
continue;
241+
}
242+
if (token === "--no-apply") {
243+
noApply = true;
244+
continue;
245+
}
246+
if (token === "--dry-run") {
247+
dryRun = true;
248+
continue;
249+
}
250+
return {
251+
kind: "error",
252+
message: `Unsupported meta-harness argument: ${token}`
253+
};
254+
}
255+
return {
256+
kind: "meta-harness",
257+
runs,
258+
nodes: nodes.length > 0 ? nodes : ["analyze_results", "review"],
259+
noApply,
260+
dryRun
261+
};
262+
}
263+
159264
return {
160265
kind: "error",
161266
message:
162-
"Unsupported CLI arguments. Run `autolabos`, `autolabos web`, `autolabos compare-analysis`, `autolabos eval-harness`, or use slash commands inside the TUI."
267+
"Unsupported CLI arguments. Run `autolabos`, `autolabos web`, `autolabos compare-analysis`, `autolabos eval-harness`, `autolabos evolve`, `autolabos meta-harness`, or use slash commands inside the TUI."
163268
};
164269
}
165270

src/cli/evolveRun.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { runEvolveLoop } from "../core/evolution/evolveRun.js";
2+
3+
export async function runEvolveCli(input: {
4+
cwd: string;
5+
maxCycles: number;
6+
target: "skills" | "prompts" | "all";
7+
dryRun?: boolean;
8+
}): Promise<void> {
9+
const report = await runEvolveLoop({
10+
cwd: input.cwd,
11+
maxCycles: input.maxCycles,
12+
target: input.target,
13+
dryRun: input.dryRun
14+
});
15+
process.stdout.write(`${report.lines.join("\n")}\n`);
16+
}

src/cli/main.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { resolveCliAction } from "./args.js";
44
import { runAutoLabOSWebServer } from "../web/server.js";
55
import { runCompareAnalysisCli } from "./compareAnalysis.js";
66
import { runEvalHarnessCli } from "./evalHarness.js";
7+
import { runEvolveCli } from "./evolveRun.js";
8+
import { runMetaHarnessCli } from "./metaHarness.js";
79

810
function printHelp(): void {
911
process.stdout.write([
@@ -18,6 +20,8 @@ function printHelp(): void {
1820
" autolabos web [--host 127.0.0.1] [--port 4317]",
1921
" autolabos compare-analysis --run <run-id> [--limit 3] [--no-judge]",
2022
" autolabos eval-harness [--run <run-id>] [--limit 10] [--output outputs/eval-harness/latest.json] [--no-history]",
23+
" autolabos evolve [--max-cycles 3] [--target skills|prompts|all] [--dry-run]",
24+
" autolabos meta-harness [--runs 5] [--node analyze_results|review] [--no-apply] [--dry-run]",
2125
" autolabos --help",
2226
" autolabos --version"
2327
].join("\n") + "\n");
@@ -72,6 +76,27 @@ async function main(): Promise<void> {
7276
return;
7377
}
7478

79+
if (action.kind === "evolve") {
80+
await runEvolveCli({
81+
cwd: process.cwd(),
82+
maxCycles: action.maxCycles,
83+
target: action.target,
84+
dryRun: action.dryRun
85+
});
86+
return;
87+
}
88+
89+
if (action.kind === "meta-harness") {
90+
await runMetaHarnessCli({
91+
cwd: process.cwd(),
92+
runs: action.runs,
93+
nodes: action.nodes,
94+
noApply: action.noApply,
95+
dryRun: action.dryRun
96+
});
97+
return;
98+
}
99+
75100
await runAutoLabOSApp({
76101
packageName: action.kind === "run" ? action.packageName : undefined
77102
});

src/cli/metaHarness.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { runMetaHarness, type MetaHarnessNode } from "../core/metaHarness/metaHarness.js";
2+
3+
export async function runMetaHarnessCli(input: {
4+
cwd: string;
5+
runs: number;
6+
nodes: MetaHarnessNode[];
7+
noApply?: boolean;
8+
dryRun?: boolean;
9+
}): Promise<void> {
10+
const result = await runMetaHarness({
11+
cwd: input.cwd,
12+
runs: input.runs,
13+
nodes: input.nodes,
14+
noApply: input.noApply,
15+
dryRun: input.dryRun
16+
});
17+
process.stdout.write(`${result.lines.join("\n")}\n`);
18+
}

0 commit comments

Comments
 (0)