agentic-flow 2.0.14 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +376 -186
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/agent-booster/index.d.ts +14 -0
  5. package/dist/agent-booster/index.d.ts.map +1 -0
  6. package/dist/agent-booster/index.js +25 -0
  7. package/dist/agent-booster/index.js.map +1 -0
  8. package/dist/harness/provenance.d.ts +72 -0
  9. package/dist/harness/provenance.d.ts.map +1 -0
  10. package/dist/harness/provenance.js +92 -0
  11. package/dist/harness/provenance.js.map +1 -0
  12. package/dist/mcp/standalone-stdio.js +4 -0
  13. package/dist/mcp/standalone-stdio.js.map +1 -1
  14. package/dist/mcp/tools/harness-tools.d.ts +28 -0
  15. package/dist/mcp/tools/harness-tools.d.ts.map +1 -0
  16. package/dist/mcp/tools/harness-tools.js +90 -0
  17. package/dist/mcp/tools/harness-tools.js.map +1 -0
  18. package/dist/optimizations/configuration-tuning.d.ts +1 -1
  19. package/dist/repair/cli.d.ts +24 -0
  20. package/dist/repair/cli.d.ts.map +1 -0
  21. package/dist/repair/cli.js +69 -0
  22. package/dist/repair/cli.js.map +1 -0
  23. package/dist/repair/darwin-repair.d.ts +80 -0
  24. package/dist/repair/darwin-repair.d.ts.map +1 -0
  25. package/dist/repair/darwin-repair.js +81 -0
  26. package/dist/repair/darwin-repair.js.map +1 -0
  27. package/dist/router/cost-optimal-router.d.ts +89 -0
  28. package/dist/router/cost-optimal-router.d.ts.map +1 -0
  29. package/dist/router/cost-optimal-router.js +94 -0
  30. package/dist/router/cost-optimal-router.js.map +1 -0
  31. package/dist/router/router.d.ts +29 -0
  32. package/dist/router/router.d.ts.map +1 -1
  33. package/dist/router/router.js +83 -0
  34. package/dist/router/router.js.map +1 -1
  35. package/dist/router/types.d.ts +1 -1
  36. package/dist/router/types.d.ts.map +1 -1
  37. package/dist/router/types.js.map +1 -1
  38. package/dist/transport/quic-loader.d.ts +33 -0
  39. package/dist/transport/quic-loader.d.ts.map +1 -1
  40. package/dist/transport/quic-loader.js +95 -0
  41. package/dist/transport/quic-loader.js.map +1 -1
  42. package/package.json +17 -3
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Harness MCP tools (ADR-075, Track A): expose Darwin harness evolution/repair
3
+ * and provenance over MCP, mirroring the orchestration-side `metaharness_*` tools
4
+ * already present in claude-flow.
5
+ *
6
+ * Each tool validates its own args with its zod schema (so `execute` can be
7
+ * unit-tested by calling it with a plain object), and returns a JSON string —
8
+ * the FastMCP content convention used elsewhere in this server.
9
+ *
10
+ * @see docs/adr/ADR-075-metaharness-harness-evolution-and-provenance.md
11
+ */
12
+ import { z } from 'zod';
13
+ import { repair } from '../../repair/darwin-repair.js';
14
+ import { buildManifest, verifySignedManifest } from '../../harness/provenance.js';
15
+ const repairParams = z.object({
16
+ repoRoot: z.string().describe('Path to the repo to evolve/repair'),
17
+ generations: z.number().int().positive().optional().describe('Generations to run (default 3)'),
18
+ children: z.number().int().positive().optional().describe('Children per parent per generation (default 4)'),
19
+ seed: z.number().int().optional().describe('Deterministic seed (default 0)'),
20
+ sandboxMode: z
21
+ .enum(['real', 'mock', 'agent'])
22
+ .optional()
23
+ .describe("Evaluation substrate: 'real' (test-driven, default), 'mock' (deterministic/Docker-free), 'agent'"),
24
+ });
25
+ export const harnessRepairTool = {
26
+ name: 'harness_repair',
27
+ description: 'Evolve/repair a repo with Darwin Mode (ADR-074): freeze the model, evolve the harness; keep only variants that measurably improve under a frozen scorer + safety gate. Default sandbox "real" gates on the repo tests; "mock" is deterministic and Docker-free.',
28
+ parameters: repairParams,
29
+ execute: async (raw) => {
30
+ const a = repairParams.parse(raw);
31
+ const res = await repair({
32
+ repoRoot: a.repoRoot,
33
+ generations: a.generations,
34
+ childrenPerGeneration: a.children,
35
+ seed: a.seed,
36
+ sandboxMode: a.sandboxMode,
37
+ });
38
+ return JSON.stringify({
39
+ improved: res.improved,
40
+ winnerId: res.winnerId,
41
+ winnerLineage: res.winnerLineage,
42
+ baselineScore: res.baselineScore,
43
+ winnerScore: res.winnerScore,
44
+ deltaOverBaseline: res.deltaOverBaseline,
45
+ generations: res.generations,
46
+ variantsEvaluated: res.variantsEvaluated,
47
+ }, null, 2);
48
+ },
49
+ };
50
+ const manifestParams = z.object({
51
+ files: z.array(z.string()).min(1).describe('Files to include in the provenance manifest'),
52
+ createdAt: z.string().optional().describe('Optional ISO timestamp to embed in the manifest'),
53
+ });
54
+ export const harnessManifestTool = {
55
+ name: 'harness_manifest',
56
+ description: 'Build a provenance manifest (sha256 per file) over harness/agent config files (ADR-075). Sign it locally with the agentic-flow/harness provenance API to produce an Ed25519 witness.',
57
+ parameters: manifestParams,
58
+ execute: async (raw) => {
59
+ const a = manifestParams.parse(raw);
60
+ return JSON.stringify(buildManifest(a.files, { createdAt: a.createdAt }), null, 2);
61
+ },
62
+ };
63
+ const signedManifestSchema = z.object({
64
+ manifest: z.object({
65
+ version: z.literal(1),
66
+ createdAt: z.string().optional(),
67
+ entries: z.array(z.object({ path: z.string(), sha256: z.string() })),
68
+ }),
69
+ signature: z.string(),
70
+ publicKey: z.string(),
71
+ });
72
+ const verifyParams = z.object({
73
+ signed: signedManifestSchema.describe('A signed manifest bundle { manifest, signature, publicKey }'),
74
+ });
75
+ export const harnessVerifyTool = {
76
+ name: 'harness_verify',
77
+ description: 'Verify a signed harness manifest (Ed25519) and report on-disk drift vs the signed digests (ADR-075). Returns { signatureValid, filesIntact, drift }.',
78
+ parameters: verifyParams,
79
+ execute: async (raw) => {
80
+ const a = verifyParams.parse(raw);
81
+ return JSON.stringify(verifySignedManifest(a.signed), null, 2);
82
+ },
83
+ };
84
+ export const HARNESS_TOOLS = [harnessRepairTool, harnessManifestTool, harnessVerifyTool];
85
+ /** Register all harness tools on a FastMCP-compatible server. */
86
+ export function registerHarnessTools(server) {
87
+ for (const tool of HARNESS_TOOLS)
88
+ server.addTool(tool);
89
+ }
90
+ //# sourceMappingURL=harness-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-tools.js","sourceRoot":"","sources":["../../../src/mcp/tools/harness-tools.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Harness MCP tools (ADR-075, Track A): expose Darwin harness evolution/repair\n * and provenance over MCP, mirroring the orchestration-side `metaharness_*` tools\n * already present in claude-flow.\n *\n * Each tool validates its own args with its zod schema (so `execute` can be\n * unit-tested by calling it with a plain object), and returns a JSON string —\n * the FastMCP content convention used elsewhere in this server.\n *\n * @see docs/adr/ADR-075-metaharness-harness-evolution-and-provenance.md\n */\n\nimport { z } from 'zod';\nimport { repair } from '../../repair/darwin-repair.js';\nimport { buildManifest, verifySignedManifest, type SignedManifest } from '../../harness/provenance.js';\n\n/** Minimal FastMCP-compatible tool descriptor (subset we use + test). */\nexport interface HarnessTool {\n name: string;\n description: string;\n parameters: z.ZodTypeAny;\n execute: (args: unknown) => Promise<string>;\n}\n\nconst repairParams = z.object({\n repoRoot: z.string().describe('Path to the repo to evolve/repair'),\n generations: z.number().int().positive().optional().describe('Generations to run (default 3)'),\n children: z.number().int().positive().optional().describe('Children per parent per generation (default 4)'),\n seed: z.number().int().optional().describe('Deterministic seed (default 0)'),\n sandboxMode: z\n .enum(['real', 'mock', 'agent'])\n .optional()\n .describe(\"Evaluation substrate: 'real' (test-driven, default), 'mock' (deterministic/Docker-free), 'agent'\"),\n});\n\nexport const harnessRepairTool: HarnessTool = {\n name: 'harness_repair',\n description:\n 'Evolve/repair a repo with Darwin Mode (ADR-074): freeze the model, evolve the harness; keep only variants that measurably improve under a frozen scorer + safety gate. Default sandbox \"real\" gates on the repo tests; \"mock\" is deterministic and Docker-free.',\n parameters: repairParams,\n execute: async (raw: unknown): Promise<string> => {\n const a = repairParams.parse(raw);\n const res = await repair({\n repoRoot: a.repoRoot,\n generations: a.generations,\n childrenPerGeneration: a.children,\n seed: a.seed,\n sandboxMode: a.sandboxMode,\n });\n return JSON.stringify(\n {\n improved: res.improved,\n winnerId: res.winnerId,\n winnerLineage: res.winnerLineage,\n baselineScore: res.baselineScore,\n winnerScore: res.winnerScore,\n deltaOverBaseline: res.deltaOverBaseline,\n generations: res.generations,\n variantsEvaluated: res.variantsEvaluated,\n },\n null,\n 2,\n );\n },\n};\n\nconst manifestParams = z.object({\n files: z.array(z.string()).min(1).describe('Files to include in the provenance manifest'),\n createdAt: z.string().optional().describe('Optional ISO timestamp to embed in the manifest'),\n});\n\nexport const harnessManifestTool: HarnessTool = {\n name: 'harness_manifest',\n description:\n 'Build a provenance manifest (sha256 per file) over harness/agent config files (ADR-075). Sign it locally with the agentic-flow/harness provenance API to produce an Ed25519 witness.',\n parameters: manifestParams,\n execute: async (raw: unknown): Promise<string> => {\n const a = manifestParams.parse(raw);\n return JSON.stringify(buildManifest(a.files, { createdAt: a.createdAt }), null, 2);\n },\n};\n\nconst signedManifestSchema = z.object({\n manifest: z.object({\n version: z.literal(1),\n createdAt: z.string().optional(),\n entries: z.array(z.object({ path: z.string(), sha256: z.string() })),\n }),\n signature: z.string(),\n publicKey: z.string(),\n});\n\nconst verifyParams = z.object({\n signed: signedManifestSchema.describe('A signed manifest bundle { manifest, signature, publicKey }'),\n});\n\nexport const harnessVerifyTool: HarnessTool = {\n name: 'harness_verify',\n description:\n 'Verify a signed harness manifest (Ed25519) and report on-disk drift vs the signed digests (ADR-075). Returns { signatureValid, filesIntact, drift }.',\n parameters: verifyParams,\n execute: async (raw: unknown): Promise<string> => {\n const a = verifyParams.parse(raw);\n return JSON.stringify(verifySignedManifest(a.signed as SignedManifest), null, 2);\n },\n};\n\nexport const HARNESS_TOOLS: readonly HarnessTool[] = [harnessRepairTool, harnessManifestTool, harnessVerifyTool];\n\n/** Register all harness tools on a FastMCP-compatible server. */\nexport function registerHarnessTools(server: { addTool: (tool: HarnessTool) => void }): void {\n for (const tool of HARNESS_TOOLS) server.addTool(tool);\n}\n"]}
@@ -122,7 +122,7 @@ export declare class ConfigurationTuning {
122
122
  entries: number;
123
123
  };
124
124
  topology: {
125
- mode: "mesh" | "hierarchical" | "ring" | "star" | "auto";
125
+ mode: "auto" | "mesh" | "hierarchical" | "ring" | "star";
126
126
  changes: number;
127
127
  improvement: string;
128
128
  };
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `agentic-flow-repair` — thin CLI over {@link repair} (ADR-074).
4
+ *
5
+ * Usage:
6
+ * node dist/repair/cli.js <repoRoot> [--generations N] [--children N]
7
+ * [--seed N] [--mock | --agent]
8
+ *
9
+ * Default substrate is 'real' (Test-Driven Repair): the repo's own test command
10
+ * gates every promotion. `--mock` is the deterministic, Docker-free smoke path.
11
+ * The full SWE-bench-Lite TDR product (issue checkout + Docker grading) is run
12
+ * via Darwin's own `metaharness-darwin` CLI — see ADR-074.
13
+ */
14
+ import { type SandboxMode } from './darwin-repair.js';
15
+ interface CliArgs {
16
+ repoRoot: string;
17
+ generations: number;
18
+ children: number;
19
+ seed: number;
20
+ sandboxMode: SandboxMode;
21
+ }
22
+ export declare function parseArgs(argv: string[]): CliArgs;
23
+ export {};
24
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/repair/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE9D,UAAU,OAAO;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAoBjD"}
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `agentic-flow-repair` — thin CLI over {@link repair} (ADR-074).
4
+ *
5
+ * Usage:
6
+ * node dist/repair/cli.js <repoRoot> [--generations N] [--children N]
7
+ * [--seed N] [--mock | --agent]
8
+ *
9
+ * Default substrate is 'real' (Test-Driven Repair): the repo's own test command
10
+ * gates every promotion. `--mock` is the deterministic, Docker-free smoke path.
11
+ * The full SWE-bench-Lite TDR product (issue checkout + Docker grading) is run
12
+ * via Darwin's own `metaharness-darwin` CLI — see ADR-074.
13
+ */
14
+ import { repair } from './darwin-repair.js';
15
+ export function parseArgs(argv) {
16
+ const args = {
17
+ repoRoot: '.',
18
+ generations: 3,
19
+ children: 4,
20
+ seed: 0,
21
+ sandboxMode: 'real',
22
+ };
23
+ const positional = [];
24
+ for (let i = 0; i < argv.length; i++) {
25
+ const a = argv[i];
26
+ if (a === '--generations')
27
+ args.generations = Number(argv[++i]);
28
+ else if (a === '--children')
29
+ args.children = Number(argv[++i]);
30
+ else if (a === '--seed')
31
+ args.seed = Number(argv[++i]);
32
+ else if (a === '--mock')
33
+ args.sandboxMode = 'mock';
34
+ else if (a === '--agent')
35
+ args.sandboxMode = 'agent';
36
+ else if (!a.startsWith('-'))
37
+ positional.push(a);
38
+ }
39
+ if (positional[0])
40
+ args.repoRoot = positional[0];
41
+ return args;
42
+ }
43
+ async function main() {
44
+ const a = parseArgs(process.argv.slice(2));
45
+ const res = await repair({
46
+ repoRoot: a.repoRoot,
47
+ generations: a.generations,
48
+ childrenPerGeneration: a.children,
49
+ seed: a.seed,
50
+ sandboxMode: a.sandboxMode,
51
+ });
52
+ console.log(`\nDarwin Repair — ${a.repoRoot} (${a.sandboxMode})`);
53
+ console.log(` baseline finalScore : ${res.baselineScore.toFixed(3)}`);
54
+ if (res.winnerId) {
55
+ const sign = res.deltaOverBaseline >= 0 ? '+' : '';
56
+ console.log(` winner : ${res.winnerId} (Δ ${sign}${res.deltaOverBaseline.toFixed(3)})`);
57
+ }
58
+ console.log(` lineage : ${res.winnerLineage.join(' → ') || '(baseline only)'}`);
59
+ console.log(` variants evaluated : ${res.variantsEvaluated} over ${res.generations} generation(s)`);
60
+ console.log(res.improved ? ' ✅ improved over baseline\n' : ' — no promoted improvement over baseline\n');
61
+ }
62
+ // Run only when invoked directly (ESM-safe; no CommonJS require.main).
63
+ if (import.meta.url === `file://${process.argv[1]}`) {
64
+ main().catch((err) => {
65
+ console.error('repair failed:', err);
66
+ process.exit(1);
67
+ });
68
+ }
69
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/repair/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * `agentic-flow-repair` — thin CLI over {@link repair} (ADR-074).\n *\n * Usage:\n * node dist/repair/cli.js <repoRoot> [--generations N] [--children N]\n * [--seed N] [--mock | --agent]\n *\n * Default substrate is 'real' (Test-Driven Repair): the repo's own test command\n * gates every promotion. `--mock` is the deterministic, Docker-free smoke path.\n * The full SWE-bench-Lite TDR product (issue checkout + Docker grading) is run\n * via Darwin's own `metaharness-darwin` CLI — see ADR-074.\n */\n\nimport { repair, type SandboxMode } from './darwin-repair.js';\n\ninterface CliArgs {\n repoRoot: string;\n generations: number;\n children: number;\n seed: number;\n sandboxMode: SandboxMode;\n}\n\nexport function parseArgs(argv: string[]): CliArgs {\n const args: CliArgs = {\n repoRoot: '.',\n generations: 3,\n children: 4,\n seed: 0,\n sandboxMode: 'real',\n };\n const positional: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === '--generations') args.generations = Number(argv[++i]);\n else if (a === '--children') args.children = Number(argv[++i]);\n else if (a === '--seed') args.seed = Number(argv[++i]);\n else if (a === '--mock') args.sandboxMode = 'mock';\n else if (a === '--agent') args.sandboxMode = 'agent';\n else if (!a.startsWith('-')) positional.push(a);\n }\n if (positional[0]) args.repoRoot = positional[0];\n return args;\n}\n\nasync function main(): Promise<void> {\n const a = parseArgs(process.argv.slice(2));\n const res = await repair({\n repoRoot: a.repoRoot,\n generations: a.generations,\n childrenPerGeneration: a.children,\n seed: a.seed,\n sandboxMode: a.sandboxMode,\n });\n\n console.log(`\\nDarwin Repair — ${a.repoRoot} (${a.sandboxMode})`);\n console.log(` baseline finalScore : ${res.baselineScore.toFixed(3)}`);\n if (res.winnerId) {\n const sign = res.deltaOverBaseline >= 0 ? '+' : '';\n console.log(` winner : ${res.winnerId} (Δ ${sign}${res.deltaOverBaseline.toFixed(3)})`);\n }\n console.log(` lineage : ${res.winnerLineage.join(' → ') || '(baseline only)'}`);\n console.log(` variants evaluated : ${res.variantsEvaluated} over ${res.generations} generation(s)`);\n console.log(res.improved ? ' ✅ improved over baseline\\n' : ' — no promoted improvement over baseline\\n');\n}\n\n// Run only when invoked directly (ESM-safe; no CommonJS require.main).\nif (import.meta.url === `file://${process.argv[1]}`) {\n main().catch((err) => {\n console.error('repair failed:', err);\n process.exit(1);\n });\n}\n"]}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Darwin Repair — autonomous harness evolution / Test-Driven Repair (ADR-074).
3
+ *
4
+ * A typed wrapper over `@metaharness/darwin`'s `evolve()`: freeze the model and
5
+ * evolve the harness around it (planner / context / reviewer / retry / tool /
6
+ * memory / score policy), keeping only variants that *measurably* improve under
7
+ * a frozen, reproducible scorer + safety gate.
8
+ *
9
+ * Modes (via `sandboxMode`):
10
+ * - 'real' (default) — Test-Driven Repair: the repo's own test command is the
11
+ * oracle, run in Darwin's shell-free, env-scrubbed sandbox.
12
+ * - 'mock' — deterministic, surface-driven loop. No repo test, no Docker,
13
+ * no network — used for hermetic smoke tests of the pipeline.
14
+ * - 'agent' — runs the variant's real surface code (Node ≥ 22).
15
+ *
16
+ * NOTE: the headline SWE-bench-Lite TDR *product* (≈68.3% with-test) additionally
17
+ * needs the official `swebench` Docker harness for issue checkout + grading — see
18
+ * ADR-074 for that deployment path. This wrapper exposes the runnable `evolve()`
19
+ * core that does not require Docker.
20
+ *
21
+ * @see docs/adr/ADR-074-metaharness-darwin-test-driven-repair.md
22
+ */
23
+ import { type EvolutionResult } from '@metaharness/darwin';
24
+ export type SandboxMode = 'real' | 'mock' | 'agent';
25
+ export interface RepairOptions {
26
+ /** Path to the repo to repair/evolve. */
27
+ repoRoot: string;
28
+ /** Work tree for Darwin artifacts. Default `<repoRoot>/.metaharness`. */
29
+ workRoot?: string;
30
+ /** Generations to run. Default 3. */
31
+ generations?: number;
32
+ /** Children produced per parent per generation. Default 4. */
33
+ childrenPerGeneration?: number;
34
+ /** Max variants evaluated concurrently. Default 4. */
35
+ concurrency?: number;
36
+ /** Minimum finalScore margin a child must beat its parent by. Default 0.05. */
37
+ promotionDelta?: number;
38
+ /** Deterministic seed. Default 0. */
39
+ seed?: number;
40
+ /** Fixed scoring tasks (the variant cannot edit these). Defaults to REPAIR_TASKS. */
41
+ tasks?: string[];
42
+ /** Evaluation substrate. Default 'real' (Test-Driven Repair). */
43
+ sandboxMode?: SandboxMode;
44
+ /** Per-variant test-command wall-clock budget (ms). Default Darwin's 120000. */
45
+ taskTimeoutMs?: number;
46
+ }
47
+ export interface RepairResult {
48
+ /** True iff a child beat the baseline and was promoted. */
49
+ improved: boolean;
50
+ /** Winner variant id (lineage tail), or null when nothing beat the baseline. */
51
+ winnerId: string | null;
52
+ /** baseline → … → winner ids. */
53
+ winnerLineage: string[];
54
+ /** Baseline finalScore (0 if unevaluated). */
55
+ baselineScore: number;
56
+ /** Winner finalScore, or null. */
57
+ winnerScore: number | null;
58
+ /** winnerScore − baselineScore. */
59
+ deltaOverBaseline: number;
60
+ generations: number;
61
+ /** Total variants in the archive (baseline + descendants). */
62
+ variantsEvaluated: number;
63
+ /** Full Darwin result for callers that need the archive/traces. */
64
+ raw: EvolutionResult;
65
+ }
66
+ /** Default scoring tasks for a repair run. */
67
+ export declare const REPAIR_TASKS: readonly string[];
68
+ /**
69
+ * Run an autonomous repair/evolution pass over a repo and return a friendly
70
+ * summary. Defaults to Test-Driven Repair ('real' sandbox): the repo's own
71
+ * tests gate every promotion.
72
+ */
73
+ export declare function repair(opts: RepairOptions): Promise<RepairResult>;
74
+ /** Reusable repair runner with bound defaults (e.g. a fixed sandbox mode). */
75
+ export declare class DarwinRepair {
76
+ private readonly defaults;
77
+ constructor(defaults?: Partial<RepairOptions>);
78
+ repair(opts: RepairOptions): Promise<RepairResult>;
79
+ }
80
+ //# sourceMappingURL=darwin-repair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"darwin-repair.d.ts","sourceRoot":"","sources":["../../src/repair/darwin-repair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAgC,KAAK,eAAe,EAAsB,MAAM,qBAAqB,CAAC;AAE7G,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,iEAAiE;IACjE,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,QAAQ,EAAE,OAAO,CAAC;IAClB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iCAAiC;IACjC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mCAAmC;IACnC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,GAAG,EAAE,eAAe,CAAC;CACtB;AAED,8CAA8C;AAC9C,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAIzC,CAAC;AAMF;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAkCvE;AAED,8EAA8E;AAC9E,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,GAAE,OAAO,CAAC,aAAa,CAAM;IAElE,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAGnD"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Darwin Repair — autonomous harness evolution / Test-Driven Repair (ADR-074).
3
+ *
4
+ * A typed wrapper over `@metaharness/darwin`'s `evolve()`: freeze the model and
5
+ * evolve the harness around it (planner / context / reviewer / retry / tool /
6
+ * memory / score policy), keeping only variants that *measurably* improve under
7
+ * a frozen, reproducible scorer + safety gate.
8
+ *
9
+ * Modes (via `sandboxMode`):
10
+ * - 'real' (default) — Test-Driven Repair: the repo's own test command is the
11
+ * oracle, run in Darwin's shell-free, env-scrubbed sandbox.
12
+ * - 'mock' — deterministic, surface-driven loop. No repo test, no Docker,
13
+ * no network — used for hermetic smoke tests of the pipeline.
14
+ * - 'agent' — runs the variant's real surface code (Node ≥ 22).
15
+ *
16
+ * NOTE: the headline SWE-bench-Lite TDR *product* (≈68.3% with-test) additionally
17
+ * needs the official `swebench` Docker harness for issue checkout + grading — see
18
+ * ADR-074 for that deployment path. This wrapper exposes the runnable `evolve()`
19
+ * core that does not require Docker.
20
+ *
21
+ * @see docs/adr/ADR-074-metaharness-darwin-test-driven-repair.md
22
+ */
23
+ import { resolve } from 'node:path';
24
+ import { evolve } from '@metaharness/darwin';
25
+ /** Default scoring tasks for a repair run. */
26
+ export const REPAIR_TASKS = [
27
+ 'run repository test suite',
28
+ 'verify generated harness safety',
29
+ 'check trace quality',
30
+ ];
31
+ function finalScoreOf(record) {
32
+ return record?.score?.finalScore ?? null;
33
+ }
34
+ /**
35
+ * Run an autonomous repair/evolution pass over a repo and return a friendly
36
+ * summary. Defaults to Test-Driven Repair ('real' sandbox): the repo's own
37
+ * tests gate every promotion.
38
+ */
39
+ export async function repair(opts) {
40
+ const repoRoot = resolve(opts.repoRoot);
41
+ const config = {
42
+ repoRoot,
43
+ workRoot: opts.workRoot ? resolve(opts.workRoot) : resolve(repoRoot, '.metaharness'),
44
+ generations: opts.generations ?? 3,
45
+ childrenPerGeneration: opts.childrenPerGeneration ?? 4,
46
+ tasks: opts.tasks ? [...opts.tasks] : [...REPAIR_TASKS],
47
+ promotionDelta: opts.promotionDelta ?? 0.05,
48
+ concurrency: opts.concurrency ?? 4,
49
+ seed: opts.seed ?? 0,
50
+ sandboxMode: opts.sandboxMode ?? 'real',
51
+ ...(opts.taskTimeoutMs ? { taskTimeoutMs: opts.taskTimeoutMs } : {}),
52
+ };
53
+ const result = await evolve(config);
54
+ const baselineScore = finalScoreOf(result.baseline) ?? 0;
55
+ const winnerScore = finalScoreOf(result.winner);
56
+ const delta = (winnerScore ?? baselineScore) - baselineScore;
57
+ // winnerLineage is baseline → … → winner, so the tail is the winner id.
58
+ const winnerId = result.winner ? result.winnerLineage[result.winnerLineage.length - 1] ?? null : null;
59
+ return {
60
+ improved: result.winner != null && delta > 0,
61
+ winnerId,
62
+ winnerLineage: result.winnerLineage,
63
+ baselineScore,
64
+ winnerScore,
65
+ deltaOverBaseline: delta,
66
+ generations: result.generations,
67
+ variantsEvaluated: result.records.length,
68
+ raw: result,
69
+ };
70
+ }
71
+ /** Reusable repair runner with bound defaults (e.g. a fixed sandbox mode). */
72
+ export class DarwinRepair {
73
+ defaults;
74
+ constructor(defaults = {}) {
75
+ this.defaults = defaults;
76
+ }
77
+ repair(opts) {
78
+ return repair({ ...this.defaults, ...opts });
79
+ }
80
+ }
81
+ //# sourceMappingURL=darwin-repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"darwin-repair.js","sourceRoot":"","sources":["../../src/repair/darwin-repair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAkE,MAAM,qBAAqB,CAAC;AA+C7G,8CAA8C;AAC9C,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C,2BAA2B;IAC3B,iCAAiC;IACjC,qBAAqB;CACtB,CAAC;AAEF,SAAS,YAAY,CAAC,MAA4B;IAChD,OAAO,MAAM,EAAE,KAAK,EAAE,UAAU,IAAI,IAAI,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAmB;IAC9C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,MAAM,GAAoB;QAC9B,QAAQ;QACR,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC;QACpF,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC;QAClC,qBAAqB,EAAE,IAAI,CAAC,qBAAqB,IAAI,CAAC;QACtD,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;QACvD,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI;QAC3C,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC;QAClC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC;QACpB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,MAAM;QACvC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrE,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAEpC,MAAM,aAAa,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,CAAC,WAAW,IAAI,aAAa,CAAC,GAAG,aAAa,CAAC;IAC7D,wEAAwE;IACxE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAEtG,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,KAAK,GAAG,CAAC;QAC5C,QAAQ;QACR,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,aAAa;QACb,WAAW;QACX,iBAAiB,EAAE,KAAK;QACxB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,iBAAiB,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;QACxC,GAAG,EAAE,MAAM;KACZ,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,WAAmC,EAAE;QAArC,aAAQ,GAAR,QAAQ,CAA6B;IAAG,CAAC;IAEtE,MAAM,CAAC,IAAmB;QACxB,OAAO,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;CACF","sourcesContent":["/**\n * Darwin Repair — autonomous harness evolution / Test-Driven Repair (ADR-074).\n *\n * A typed wrapper over `@metaharness/darwin`'s `evolve()`: freeze the model and\n * evolve the harness around it (planner / context / reviewer / retry / tool /\n * memory / score policy), keeping only variants that *measurably* improve under\n * a frozen, reproducible scorer + safety gate.\n *\n * Modes (via `sandboxMode`):\n * - 'real' (default) — Test-Driven Repair: the repo's own test command is the\n * oracle, run in Darwin's shell-free, env-scrubbed sandbox.\n * - 'mock' — deterministic, surface-driven loop. No repo test, no Docker,\n * no network — used for hermetic smoke tests of the pipeline.\n * - 'agent' — runs the variant's real surface code (Node ≥ 22).\n *\n * NOTE: the headline SWE-bench-Lite TDR *product* (≈68.3% with-test) additionally\n * needs the official `swebench` Docker harness for issue checkout + grading — see\n * ADR-074 for that deployment path. This wrapper exposes the runnable `evolve()`\n * core that does not require Docker.\n *\n * @see docs/adr/ADR-074-metaharness-darwin-test-driven-repair.md\n */\n\nimport { resolve } from 'node:path';\nimport { evolve, type EvolutionConfig, type EvolutionResult, type ArchiveRecord } from '@metaharness/darwin';\n\nexport type SandboxMode = 'real' | 'mock' | 'agent';\n\nexport interface RepairOptions {\n /** Path to the repo to repair/evolve. */\n repoRoot: string;\n /** Work tree for Darwin artifacts. Default `<repoRoot>/.metaharness`. */\n workRoot?: string;\n /** Generations to run. Default 3. */\n generations?: number;\n /** Children produced per parent per generation. Default 4. */\n childrenPerGeneration?: number;\n /** Max variants evaluated concurrently. Default 4. */\n concurrency?: number;\n /** Minimum finalScore margin a child must beat its parent by. Default 0.05. */\n promotionDelta?: number;\n /** Deterministic seed. Default 0. */\n seed?: number;\n /** Fixed scoring tasks (the variant cannot edit these). Defaults to REPAIR_TASKS. */\n tasks?: string[];\n /** Evaluation substrate. Default 'real' (Test-Driven Repair). */\n sandboxMode?: SandboxMode;\n /** Per-variant test-command wall-clock budget (ms). Default Darwin's 120000. */\n taskTimeoutMs?: number;\n}\n\nexport interface RepairResult {\n /** True iff a child beat the baseline and was promoted. */\n improved: boolean;\n /** Winner variant id (lineage tail), or null when nothing beat the baseline. */\n winnerId: string | null;\n /** baseline → … → winner ids. */\n winnerLineage: string[];\n /** Baseline finalScore (0 if unevaluated). */\n baselineScore: number;\n /** Winner finalScore, or null. */\n winnerScore: number | null;\n /** winnerScore − baselineScore. */\n deltaOverBaseline: number;\n generations: number;\n /** Total variants in the archive (baseline + descendants). */\n variantsEvaluated: number;\n /** Full Darwin result for callers that need the archive/traces. */\n raw: EvolutionResult;\n}\n\n/** Default scoring tasks for a repair run. */\nexport const REPAIR_TASKS: readonly string[] = [\n 'run repository test suite',\n 'verify generated harness safety',\n 'check trace quality',\n];\n\nfunction finalScoreOf(record: ArchiveRecord | null): number | null {\n return record?.score?.finalScore ?? null;\n}\n\n/**\n * Run an autonomous repair/evolution pass over a repo and return a friendly\n * summary. Defaults to Test-Driven Repair ('real' sandbox): the repo's own\n * tests gate every promotion.\n */\nexport async function repair(opts: RepairOptions): Promise<RepairResult> {\n const repoRoot = resolve(opts.repoRoot);\n const config: EvolutionConfig = {\n repoRoot,\n workRoot: opts.workRoot ? resolve(opts.workRoot) : resolve(repoRoot, '.metaharness'),\n generations: opts.generations ?? 3,\n childrenPerGeneration: opts.childrenPerGeneration ?? 4,\n tasks: opts.tasks ? [...opts.tasks] : [...REPAIR_TASKS],\n promotionDelta: opts.promotionDelta ?? 0.05,\n concurrency: opts.concurrency ?? 4,\n seed: opts.seed ?? 0,\n sandboxMode: opts.sandboxMode ?? 'real',\n ...(opts.taskTimeoutMs ? { taskTimeoutMs: opts.taskTimeoutMs } : {}),\n };\n\n const result = await evolve(config);\n\n const baselineScore = finalScoreOf(result.baseline) ?? 0;\n const winnerScore = finalScoreOf(result.winner);\n const delta = (winnerScore ?? baselineScore) - baselineScore;\n // winnerLineage is baseline → … → winner, so the tail is the winner id.\n const winnerId = result.winner ? result.winnerLineage[result.winnerLineage.length - 1] ?? null : null;\n\n return {\n improved: result.winner != null && delta > 0,\n winnerId,\n winnerLineage: result.winnerLineage,\n baselineScore,\n winnerScore,\n deltaOverBaseline: delta,\n generations: result.generations,\n variantsEvaluated: result.records.length,\n raw: result,\n };\n}\n\n/** Reusable repair runner with bound defaults (e.g. a fixed sandbox mode). */\nexport class DarwinRepair {\n constructor(private readonly defaults: Partial<RepairOptions> = {}) {}\n\n repair(opts: RepairOptions): Promise<RepairResult> {\n return repair({ ...this.defaults, ...opts });\n }\n}\n"]}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Cost-Optimal Model Router (ADR-073)
3
+ *
4
+ * Routes each query to the *cheapest model predicted to clear a quality bar*,
5
+ * learned from eval logs — the productized DRACO Phase-2 finding. Wraps
6
+ * `@metaharness/router` (dependency-free k-NN / kernel-ridge, optional native
7
+ * FastGRNN via the already-present `@ruvector/tiny-dancer`).
8
+ *
9
+ * This is additive to `ModelRouter`'s existing config-rule routing: it selects
10
+ * a *model* by predicted cost-quality rather than a provider by static rule.
11
+ * It degrades gracefully — with no labelled examples it falls back to the
12
+ * best-predicted candidate (effectively the caller's prior behavior), so a
13
+ * cold start never breaks routing.
14
+ *
15
+ * @see docs/adr/ADR-073-metaharness-router-cost-optimal-model-routing.md
16
+ */
17
+ import { type RouteResult, type RouterCandidate } from '@metaharness/router';
18
+ import type { ProviderType } from './types.js';
19
+ /** One labelled observation: a query embedding and the quality a model achieved on it. */
20
+ export interface RoutingExample {
21
+ embedding: number[];
22
+ quality: number;
23
+ }
24
+ /** A flat training row: query embedding → quality each model achieved on that query. */
25
+ export interface RoutingDatasetRow {
26
+ embedding: number[];
27
+ scores: Record<string, number>;
28
+ }
29
+ /** Maps a router model id (e.g. "anthropic/claude-haiku-4.5") to a concrete provider + model. */
30
+ export interface ModelBinding {
31
+ provider: ProviderType;
32
+ model: string;
33
+ }
34
+ export interface CostOptimalRouterConfig {
35
+ /**
36
+ * Quality bar (0..1). When set, route() returns the CHEAPEST candidate
37
+ * predicted to clear it; when none clear it, the best-predicted; when unset,
38
+ * always the best-predicted.
39
+ */
40
+ qualityBar?: number;
41
+ /** k-NN neighbours used to predict quality on a new query (default 5). */
42
+ k?: number;
43
+ /**
44
+ * Optional explicit model id → provider/model bindings. When a routed id is
45
+ * absent here, it is parsed as `"<provider>/<model>"` (the OpenRouter-style
46
+ * convention), falling back to treating the whole id as the model name on the
47
+ * default provider.
48
+ */
49
+ modelMap?: Record<string, ModelBinding>;
50
+ /** Provider to assume when a model id has no `"<provider>/"` prefix. */
51
+ defaultProvider?: ProviderType;
52
+ }
53
+ /** A routing decision: the underlying RouteResult plus the resolved provider/model. */
54
+ export interface CostOptimalDecision extends RouteResult {
55
+ provider: ProviderType;
56
+ model: string;
57
+ }
58
+ /**
59
+ * Parse a router model id into a provider/model binding. `"anthropic/claude-..."`
60
+ * → `{ provider: 'anthropic', model: 'claude-...' }`. Ids without a known
61
+ * provider prefix bind to `defaultProvider` with the id as the model name.
62
+ */
63
+ export declare function parseModelId(id: string, modelMap: Record<string, ModelBinding> | undefined, defaultProvider: ProviderType): ModelBinding;
64
+ export declare class CostOptimalRouter {
65
+ private readonly router;
66
+ private readonly modelMap?;
67
+ private readonly defaultProvider;
68
+ private constructor();
69
+ /**
70
+ * Build from explicit candidates (each with its own labelled examples + price).
71
+ * Use when you already have per-model example sets.
72
+ */
73
+ static fromCandidates(candidates: RouterCandidate[], config?: CostOptimalRouterConfig): CostOptimalRouter;
74
+ /**
75
+ * Build from a flat routing dataset — rows of (query embedding → quality each
76
+ * model achieved) plus a per-model price table. This is the shape eval logs
77
+ * and the DRACO benchmark emit, and the same shape that seeds a native
78
+ * tiny-dancer trainer.
79
+ */
80
+ static fromDataset(rows: RoutingDatasetRow[], prices: Record<string, number>, config?: CostOptimalRouterConfig): CostOptimalRouter;
81
+ /** Route a pre-embedded query to the cost-optimal model + provider. */
82
+ route(queryEmbedding: number[]): CostOptimalDecision;
83
+ /**
84
+ * Route using an injected embedder (e.g. the ONNX/ruvector embedding service).
85
+ * Keeps this module decoupled from any specific embedding backend.
86
+ */
87
+ routeText(text: string, embed: (text: string) => Promise<number[]> | number[]): Promise<CostOptimalDecision>;
88
+ }
89
+ //# sourceMappingURL=cost-optimal-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-optimal-router.d.ts","sourceRoot":"","sources":["../../src/router/cost-optimal-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,0FAA0F;AAC1F,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,iGAAiG;AACjG,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,CAAC,CAAC,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACxC,wEAAwE;IACxE,eAAe,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,uFAAuF;AACvF,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,SAAS,EAClD,eAAe,EAAE,YAAY,GAC5B,YAAY,CAoBd;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAA+B;IACzD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAE/C,OAAO;IAMP;;;OAGG;IACH,MAAM,CAAC,cAAc,CACnB,UAAU,EAAE,eAAe,EAAE,EAC7B,MAAM,GAAE,uBAA4B,GACnC,iBAAiB;IASpB;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAChB,IAAI,EAAE,iBAAiB,EAAE,EACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,GAAE,uBAA4B,GACnC,iBAAiB;IAQpB,uEAAuE;IACvE,KAAK,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,mBAAmB;IAMpD;;;OAGG;IACG,SAAS,CACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,GACpD,OAAO,CAAC,mBAAmB,CAAC;CAIhC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Cost-Optimal Model Router (ADR-073)
3
+ *
4
+ * Routes each query to the *cheapest model predicted to clear a quality bar*,
5
+ * learned from eval logs — the productized DRACO Phase-2 finding. Wraps
6
+ * `@metaharness/router` (dependency-free k-NN / kernel-ridge, optional native
7
+ * FastGRNN via the already-present `@ruvector/tiny-dancer`).
8
+ *
9
+ * This is additive to `ModelRouter`'s existing config-rule routing: it selects
10
+ * a *model* by predicted cost-quality rather than a provider by static rule.
11
+ * It degrades gracefully — with no labelled examples it falls back to the
12
+ * best-predicted candidate (effectively the caller's prior behavior), so a
13
+ * cold start never breaks routing.
14
+ *
15
+ * @see docs/adr/ADR-073-metaharness-router-cost-optimal-model-routing.md
16
+ */
17
+ import { Router } from '@metaharness/router';
18
+ /**
19
+ * Parse a router model id into a provider/model binding. `"anthropic/claude-..."`
20
+ * → `{ provider: 'anthropic', model: 'claude-...' }`. Ids without a known
21
+ * provider prefix bind to `defaultProvider` with the id as the model name.
22
+ */
23
+ export function parseModelId(id, modelMap, defaultProvider) {
24
+ if (modelMap && modelMap[id])
25
+ return modelMap[id];
26
+ const slash = id.indexOf('/');
27
+ if (slash > 0) {
28
+ const maybeProvider = id.slice(0, slash);
29
+ const known = [
30
+ 'anthropic',
31
+ 'openai',
32
+ 'openrouter',
33
+ 'ollama',
34
+ 'litellm',
35
+ 'onnx',
36
+ 'gemini',
37
+ 'custom',
38
+ ];
39
+ if (known.includes(maybeProvider)) {
40
+ return { provider: maybeProvider, model: id.slice(slash + 1) };
41
+ }
42
+ }
43
+ return { provider: defaultProvider, model: id };
44
+ }
45
+ export class CostOptimalRouter {
46
+ router;
47
+ modelMap;
48
+ defaultProvider;
49
+ constructor(router, config) {
50
+ this.router = router;
51
+ this.modelMap = config.modelMap;
52
+ this.defaultProvider = config.defaultProvider ?? 'anthropic';
53
+ }
54
+ /**
55
+ * Build from explicit candidates (each with its own labelled examples + price).
56
+ * Use when you already have per-model example sets.
57
+ */
58
+ static fromCandidates(candidates, config = {}) {
59
+ const router = new Router({
60
+ candidates,
61
+ k: config.k,
62
+ qualityBar: config.qualityBar,
63
+ });
64
+ return new CostOptimalRouter(router, config);
65
+ }
66
+ /**
67
+ * Build from a flat routing dataset — rows of (query embedding → quality each
68
+ * model achieved) plus a per-model price table. This is the shape eval logs
69
+ * and the DRACO benchmark emit, and the same shape that seeds a native
70
+ * tiny-dancer trainer.
71
+ */
72
+ static fromDataset(rows, prices, config = {}) {
73
+ const router = Router.fromExamples(rows, prices, {
74
+ k: config.k,
75
+ qualityBar: config.qualityBar,
76
+ });
77
+ return new CostOptimalRouter(router, config);
78
+ }
79
+ /** Route a pre-embedded query to the cost-optimal model + provider. */
80
+ route(queryEmbedding) {
81
+ const result = this.router.route(queryEmbedding);
82
+ const { provider, model } = parseModelId(result.id, this.modelMap, this.defaultProvider);
83
+ return { ...result, provider, model };
84
+ }
85
+ /**
86
+ * Route using an injected embedder (e.g. the ONNX/ruvector embedding service).
87
+ * Keeps this module decoupled from any specific embedding backend.
88
+ */
89
+ async routeText(text, embed) {
90
+ const embedding = await embed(text);
91
+ return this.route(embedding);
92
+ }
93
+ }
94
+ //# sourceMappingURL=cost-optimal-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-optimal-router.js","sourceRoot":"","sources":["../../src/router/cost-optimal-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,MAAM,EAA0C,MAAM,qBAAqB,CAAC;AA+CrF;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,EAAU,EACV,QAAkD,EAClD,eAA6B;IAE7B,IAAI,QAAQ,IAAI,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,aAAa,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAiB,CAAC;QACzD,MAAM,KAAK,GAAmB;YAC5B,WAAW;YACX,QAAQ;YACR,YAAY;YACZ,QAAQ;YACR,SAAS;YACT,MAAM;YACN,QAAQ;YACR,QAAQ;SACT,CAAC;QACF,IAAI,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QACjE,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,OAAO,iBAAiB;IACX,MAAM,CAAS;IACf,QAAQ,CAAgC;IACxC,eAAe,CAAe;IAE/C,YAAoB,MAAc,EAAE,MAA+B;QACjE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,WAAW,CAAC;IAC/D,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,cAAc,CACnB,UAA6B,EAC7B,SAAkC,EAAE;QAEpC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;YACxB,UAAU;YACV,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC,CAAC;QACH,OAAO,IAAI,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAChB,IAAyB,EACzB,MAA8B,EAC9B,SAAkC,EAAE;QAEpC,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE;YAC/C,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC,CAAC;QACH,OAAO,IAAI,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,cAAwB;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACjD,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACzF,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACxC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS,CACb,IAAY,EACZ,KAAqD;QAErD,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;CACF","sourcesContent":["/**\n * Cost-Optimal Model Router (ADR-073)\n *\n * Routes each query to the *cheapest model predicted to clear a quality bar*,\n * learned from eval logs — the productized DRACO Phase-2 finding. Wraps\n * `@metaharness/router` (dependency-free k-NN / kernel-ridge, optional native\n * FastGRNN via the already-present `@ruvector/tiny-dancer`).\n *\n * This is additive to `ModelRouter`'s existing config-rule routing: it selects\n * a *model* by predicted cost-quality rather than a provider by static rule.\n * It degrades gracefully — with no labelled examples it falls back to the\n * best-predicted candidate (effectively the caller's prior behavior), so a\n * cold start never breaks routing.\n *\n * @see docs/adr/ADR-073-metaharness-router-cost-optimal-model-routing.md\n */\n\nimport { Router, type RouteResult, type RouterCandidate } from '@metaharness/router';\nimport type { ProviderType } from './types.js';\n\n/** One labelled observation: a query embedding and the quality a model achieved on it. */\nexport interface RoutingExample {\n embedding: number[];\n quality: number; // 0..1\n}\n\n/** A flat training row: query embedding → quality each model achieved on that query. */\nexport interface RoutingDatasetRow {\n embedding: number[];\n scores: Record<string, number>; // modelId → quality (0..1)\n}\n\n/** Maps a router model id (e.g. \"anthropic/claude-haiku-4.5\") to a concrete provider + model. */\nexport interface ModelBinding {\n provider: ProviderType;\n model: string;\n}\n\nexport interface CostOptimalRouterConfig {\n /**\n * Quality bar (0..1). When set, route() returns the CHEAPEST candidate\n * predicted to clear it; when none clear it, the best-predicted; when unset,\n * always the best-predicted.\n */\n qualityBar?: number;\n /** k-NN neighbours used to predict quality on a new query (default 5). */\n k?: number;\n /**\n * Optional explicit model id → provider/model bindings. When a routed id is\n * absent here, it is parsed as `\"<provider>/<model>\"` (the OpenRouter-style\n * convention), falling back to treating the whole id as the model name on the\n * default provider.\n */\n modelMap?: Record<string, ModelBinding>;\n /** Provider to assume when a model id has no `\"<provider>/\"` prefix. */\n defaultProvider?: ProviderType;\n}\n\n/** A routing decision: the underlying RouteResult plus the resolved provider/model. */\nexport interface CostOptimalDecision extends RouteResult {\n provider: ProviderType;\n model: string;\n}\n\n/**\n * Parse a router model id into a provider/model binding. `\"anthropic/claude-...\"`\n * → `{ provider: 'anthropic', model: 'claude-...' }`. Ids without a known\n * provider prefix bind to `defaultProvider` with the id as the model name.\n */\nexport function parseModelId(\n id: string,\n modelMap: Record<string, ModelBinding> | undefined,\n defaultProvider: ProviderType,\n): ModelBinding {\n if (modelMap && modelMap[id]) return modelMap[id];\n const slash = id.indexOf('/');\n if (slash > 0) {\n const maybeProvider = id.slice(0, slash) as ProviderType;\n const known: ProviderType[] = [\n 'anthropic',\n 'openai',\n 'openrouter',\n 'ollama',\n 'litellm',\n 'onnx',\n 'gemini',\n 'custom',\n ];\n if (known.includes(maybeProvider)) {\n return { provider: maybeProvider, model: id.slice(slash + 1) };\n }\n }\n return { provider: defaultProvider, model: id };\n}\n\nexport class CostOptimalRouter {\n private readonly router: Router;\n private readonly modelMap?: Record<string, ModelBinding>;\n private readonly defaultProvider: ProviderType;\n\n private constructor(router: Router, config: CostOptimalRouterConfig) {\n this.router = router;\n this.modelMap = config.modelMap;\n this.defaultProvider = config.defaultProvider ?? 'anthropic';\n }\n\n /**\n * Build from explicit candidates (each with its own labelled examples + price).\n * Use when you already have per-model example sets.\n */\n static fromCandidates(\n candidates: RouterCandidate[],\n config: CostOptimalRouterConfig = {},\n ): CostOptimalRouter {\n const router = new Router({\n candidates,\n k: config.k,\n qualityBar: config.qualityBar,\n });\n return new CostOptimalRouter(router, config);\n }\n\n /**\n * Build from a flat routing dataset — rows of (query embedding → quality each\n * model achieved) plus a per-model price table. This is the shape eval logs\n * and the DRACO benchmark emit, and the same shape that seeds a native\n * tiny-dancer trainer.\n */\n static fromDataset(\n rows: RoutingDatasetRow[],\n prices: Record<string, number>,\n config: CostOptimalRouterConfig = {},\n ): CostOptimalRouter {\n const router = Router.fromExamples(rows, prices, {\n k: config.k,\n qualityBar: config.qualityBar,\n });\n return new CostOptimalRouter(router, config);\n }\n\n /** Route a pre-embedded query to the cost-optimal model + provider. */\n route(queryEmbedding: number[]): CostOptimalDecision {\n const result = this.router.route(queryEmbedding);\n const { provider, model } = parseModelId(result.id, this.modelMap, this.defaultProvider);\n return { ...result, provider, model };\n }\n\n /**\n * Route using an injected embedder (e.g. the ONNX/ruvector embedding service).\n * Keeps this module decoupled from any specific embedding backend.\n */\n async routeText(\n text: string,\n embed: (text: string) => Promise<number[]> | number[],\n ): Promise<CostOptimalDecision> {\n const embedding = await embed(text);\n return this.route(embedding);\n }\n}\n"]}