claude-flow 3.20.0 → 3.21.1

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 (36) hide show
  1. package/package.json +1 -1
  2. package/v3/@claude-flow/cli/dist/src/agenticow/speculative-exploration.d.ts +148 -0
  3. package/v3/@claude-flow/cli/dist/src/agenticow/speculative-exploration.js +218 -0
  4. package/v3/@claude-flow/cli/dist/src/commands/autopilot.js +45 -0
  5. package/v3/@claude-flow/cli/dist/src/commands/doctor.js +56 -0
  6. package/v3/@claude-flow/cli/dist/src/commands/neural.js +309 -1
  7. package/v3/@claude-flow/cli/dist/src/init/executor.js +17 -0
  8. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +26 -2
  9. package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.d.ts +53 -0
  10. package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.js +118 -0
  11. package/v3/@claude-flow/cli/dist/src/mcp-client.js +6 -1
  12. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-execute-core.js +33 -0
  13. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +47 -1
  14. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-loader.d.ts +59 -0
  15. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-loader.js +105 -0
  16. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.d.ts +24 -0
  17. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.js +202 -0
  18. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-tools.js +9 -82
  19. package/v3/@claude-flow/cli/dist/src/mcp-tools/index.d.ts +1 -0
  20. package/v3/@claude-flow/cli/dist/src/mcp-tools/index.js +2 -0
  21. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +49 -8
  22. package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.d.ts +33 -0
  23. package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.js +31 -0
  24. package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.d.ts +154 -0
  25. package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.js +209 -0
  26. package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.d.ts +140 -0
  27. package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.js +223 -0
  28. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.d.ts +190 -0
  29. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.js +349 -0
  30. package/v3/@claude-flow/cli/dist/src/services/fable-harness.d.ts +168 -0
  31. package/v3/@claude-flow/cli/dist/src/services/fable-harness.js +347 -0
  32. package/v3/@claude-flow/cli/dist/src/services/swarm-memory-branches.d.ts +135 -0
  33. package/v3/@claude-flow/cli/dist/src/services/swarm-memory-branches.js +213 -0
  34. package/v3/@claude-flow/cli/dist/src/services/weight-eft.d.ts +305 -0
  35. package/v3/@claude-flow/cli/dist/src/services/weight-eft.js +296 -0
  36. package/v3/@claude-flow/cli/package.json +5 -4
@@ -0,0 +1,349 @@
1
+ /**
2
+ * distill-oracle.ts — Tiered `resolved` oracle for distill / weight-EFT SFT data.
3
+ *
4
+ * THE PROBLEM: weight-EFT's SFT data wants a gold `resolved: boolean` per
5
+ * trajectory. Ruflo has no SWE-bench oracle, so today `resolved` is
6
+ * structural-confidence — a proxy that risks distilling plausible-but-wrong
7
+ * completions. This module replaces that single proxy with a TIERED labeler,
8
+ * where every label carries HONEST PROVENANCE (ADR-169 reporting integrity — a
9
+ * proxy is never presented as ground truth).
10
+ *
11
+ * ── TIERS (tried in order, per trajectory) ──────────────────────────────────
12
+ * TIER 1 — MECHANICAL ORACLE (provenance `oracle:test-exec`, real ground truth)
13
+ * When a trajectory carries a test spec (SWE-bench FAIL_TO_PASS shape) or
14
+ * maps to a metaharness/darwin bench-suite case, EXECUTE the real eval and
15
+ * set resolved = tests-pass. This needs Docker/compute, so it runs on a
16
+ * REMOTE host over SSH (host parameterized via `remote` / env
17
+ * RUFLO_DISTILL_REMOTE — never hard-coded). DRY-RUN by default: it prints
18
+ * the ssh/darwin-bench/eval commands + a preflight plan and does NOT touch
19
+ * the network; only `execute: true` runs the real eval (with a wrapped,
20
+ * non-fatal preflight probe first).
21
+ *
22
+ * TIER 2 — FABLE JUDGE (provenance `judge:fable`, smarter proxy)
23
+ * For trajectories with no mechanical spec, judge via the cost-disciplined
24
+ * headless Fable harness (fable-harness.ts). Opt-in and OFF by default;
25
+ * spends nothing unless `fableJudge: true` AND a `maxBudgetUsd` cap is set.
26
+ *
27
+ * TIER 3 — STRUCTURAL PROXY (provenance `proxy:structural`, weakest)
28
+ * The existing output-verifier structural confidence. Always available,
29
+ * $0, no external calls. Explicitly the weakest signal, clearly labeled.
30
+ *
31
+ * DEFAULT (no opts): dry-run oracle preflight + structural-proxy fallback.
32
+ * ZERO spend, no SSH exec, no Fable call. Everything degrades gracefully
33
+ * (ADR-150) — a missing remote or a failed probe is reported, never fatal.
34
+ *
35
+ * @module services/distill-oracle
36
+ */
37
+ import { spawn } from 'child_process';
38
+ import { verifyAndEscalate } from '../ruvector/output-verifier.js';
39
+ import { FableHarness, } from './fable-harness.js';
40
+ // ── Public API ───────────────────────────────────────────────────────────
41
+ /**
42
+ * Label each trajectory with `resolved` + honest provenance, trying the tiers
43
+ * in order per trajectory. See the module header for tier semantics and the
44
+ * zero-spend default guarantee.
45
+ */
46
+ export async function labelResolved(trajectories, opts = {}) {
47
+ const results = new Array(trajectories.length);
48
+ const preflights = new Map();
49
+ const remote = resolveRemote(opts.remote);
50
+ const fableActive = Boolean(opts.fableJudge) && Number(opts.maxBudgetUsd) > 0;
51
+ // ── Tier 1: mechanical oracle (real exec only under execute:true) ──────────
52
+ const fableQueue = [];
53
+ for (let i = 0; i < trajectories.length; i++) {
54
+ const t = trajectories[i];
55
+ if (hasMechanicalSpec(t)) {
56
+ const plan = buildOraclePlan(t.testSpec, remote);
57
+ if (opts.execute) {
58
+ const runner = opts.runner ?? createSshOracleRunner();
59
+ // Wrapped probe: a failure is REPORTED, not thrown.
60
+ const probe = await safeProbe(runner, plan);
61
+ if (probe.ok) {
62
+ const outcome = await safeRunEval(runner, plan, t.testSpec);
63
+ results[i] = {
64
+ ...t,
65
+ resolved: outcome.resolved,
66
+ resolvedBy: 'oracle:test-exec',
67
+ resolvedConfidence: 1,
68
+ resolvedReason: outcome.reason,
69
+ oraclePreflight: { dryRun: false, plan, probe },
70
+ };
71
+ continue;
72
+ }
73
+ // Probe failed → report and fall through to next tier.
74
+ preflights.set(i, {
75
+ dryRun: false,
76
+ plan,
77
+ probe,
78
+ fellThroughBecause: `preflight failed: ${probe.reason}`,
79
+ });
80
+ }
81
+ else {
82
+ // DRY-RUN: emit the plan/commands, touch nothing, fall through.
83
+ preflights.set(i, {
84
+ dryRun: true,
85
+ plan,
86
+ fellThroughBecause: remote
87
+ ? 'dry-run (pass execute:true to run the real eval over SSH)'
88
+ : 'dry-run and no remote configured (set remote / RUFLO_DISTILL_REMOTE)',
89
+ });
90
+ }
91
+ }
92
+ // ── Route to Tier 2 (batched later) or Tier 3 now ──
93
+ if (fableActive) {
94
+ fableQueue.push({ index: i, item: toJudgeItem(t) });
95
+ }
96
+ else {
97
+ results[i] = await proxyLabel(t, opts, preflights.get(i));
98
+ }
99
+ }
100
+ // ── Tier 2: batched Fable judge for queued items ──────────────────────────
101
+ if (fableActive && fableQueue.length > 0) {
102
+ const harness = opts.harness ??
103
+ new FableHarness({ maxBudgetUsd: opts.maxBudgetUsd, batchSize: opts.fableBatchSize });
104
+ let verdicts = [];
105
+ try {
106
+ verdicts = await harness.judgeBatch(fableQueue.map((q) => q.item));
107
+ }
108
+ catch {
109
+ verdicts = []; // graceful: any harness failure ⇒ proxy fallback for all
110
+ }
111
+ const byId = new Map(verdicts.map((v) => [v.id, v]));
112
+ for (const { index, item } of fableQueue) {
113
+ const v = byId.get(item.id);
114
+ const t = trajectories[index];
115
+ if (v) {
116
+ results[index] = {
117
+ ...t,
118
+ resolved: v.resolved,
119
+ resolvedBy: 'judge:fable',
120
+ resolvedConfidence: v.confidence,
121
+ resolvedReason: v.reason,
122
+ ...(preflights.has(index) ? { oraclePreflight: preflights.get(index) } : {}),
123
+ };
124
+ }
125
+ else {
126
+ // Model omitted this id / budget exhausted → Tier-3 proxy fallback.
127
+ results[index] = await proxyLabel(t, opts, preflights.get(index));
128
+ }
129
+ }
130
+ }
131
+ // ── Safety net: nothing should be undefined, but fall back to proxy if so ──
132
+ for (let i = 0; i < results.length; i++) {
133
+ if (results[i] === undefined) {
134
+ results[i] = await proxyLabel(trajectories[i], opts, preflights.get(i));
135
+ }
136
+ }
137
+ return results;
138
+ }
139
+ /**
140
+ * Reflective failure analysis (GEPA/evolve mutation input). Thin, cost-
141
+ * disciplined pass-through to the Fable harness. Opt-in: returns [] unless a
142
+ * harness with a budget cap is provided/configured.
143
+ */
144
+ export async function reflectFailures(items, opts = {}) {
145
+ const harness = opts.harness ??
146
+ new FableHarness({ maxBudgetUsd: opts.maxBudgetUsd, batchSize: opts.fableBatchSize });
147
+ if (!harness.isEnabled())
148
+ return [];
149
+ return harness.reflectFailures(items);
150
+ }
151
+ // ── Tier-3 structural proxy ──────────────────────────────────────────────
152
+ async function proxyLabel(t, opts, preflight) {
153
+ const verdict = await verifyAndEscalate({
154
+ task: t.task ?? '',
155
+ output: t.output ?? '',
156
+ taskKind: t.taskKind,
157
+ });
158
+ const threshold = opts.minStructuralConfidence;
159
+ const resolved = typeof threshold === 'number' ? verdict.score >= threshold : verdict.confident;
160
+ return {
161
+ ...t,
162
+ resolved,
163
+ resolvedBy: 'proxy:structural',
164
+ resolvedConfidence: verdict.score,
165
+ resolvedReason: verdict.confident
166
+ ? 'structural signals confident'
167
+ : `structural signals weak: ${verdict.reasons.slice(0, 3).join('; ')}`,
168
+ ...(preflight ? { oraclePreflight: preflight } : {}),
169
+ };
170
+ }
171
+ // ── Tier-1 planning ──────────────────────────────────────────────────────
172
+ /** A trajectory can be mechanically evaluated iff it carries an actionable spec. */
173
+ export function hasMechanicalSpec(t) {
174
+ const s = t.testSpec;
175
+ if (!s)
176
+ return false;
177
+ return Boolean((s.failToPass && s.failToPass.length > 0) ||
178
+ s.evalCommand ||
179
+ s.benchSuite ||
180
+ s.benchCase);
181
+ }
182
+ /** Resolve the remote host from opts → env, never a hard-coded host. */
183
+ export function resolveRemote(remote) {
184
+ const r = (remote ?? process.env.RUFLO_DISTILL_REMOTE ?? '').trim();
185
+ return r.length > 0 ? r : null;
186
+ }
187
+ /**
188
+ * Build the concrete command plan for a spec. Pure — constructs command strings
189
+ * only; executes nothing. `remote` is always substituted from the parameter,
190
+ * never hard-coded.
191
+ */
192
+ export function buildOraclePlan(spec, remote) {
193
+ const host = remote; // may be null; commands render `<remote>` placeholder then
194
+ const r = host ?? '<remote>';
195
+ const sshLocal = host ? `ssh ${host} ` : `ssh <remote> `;
196
+ const workdir = spec.workdir ?? (spec.repo ? `~/ruflo-distill/${sanitize(spec.repo)}` : '~/ruflo-distill/work');
197
+ // Preflight probes — reachability + the tools Tier-1 needs on the remote.
198
+ const probeCommands = [
199
+ `ssh -o BatchMode=yes -o ConnectTimeout=8 ${r} 'echo ok'`,
200
+ `${sshLocal}'command -v docker >/dev/null 2>&1 && echo docker-present || echo docker-missing'`,
201
+ `${sshLocal}'command -v darwin >/dev/null 2>&1 || npx --yes @metaharness/darwin --version'`,
202
+ ];
203
+ let kind;
204
+ const evalCommands = [];
205
+ let parseHint;
206
+ if (spec.benchSuite || spec.benchCase) {
207
+ kind = 'ssh-darwin-bench';
208
+ const suite = spec.benchSuite ? `--suite ${shq(spec.benchSuite)}` : '';
209
+ const kase = spec.benchCase ? `--case ${shq(spec.benchCase)}` : '';
210
+ evalCommands.push(`${sshLocal}'cd ${workdir} && npx --yes @metaharness/darwin bench run ${suite} ${kase} --json'`.replace(/\s+/g, ' ').trim());
211
+ parseHint = 'resolved = darwin bench run reports the case scored PASS (exit 0, json.passed=true)';
212
+ }
213
+ else if (spec.evalCommand) {
214
+ kind = 'ssh-eval';
215
+ evalCommands.push(`${sshLocal}'cd ${workdir} && ${spec.evalCommand}'`);
216
+ parseHint = 'resolved = evalCommand exits 0';
217
+ }
218
+ else {
219
+ kind = 'ssh-swebench-eval';
220
+ if (spec.patch) {
221
+ evalCommands.push(`${sshLocal}'cd ${workdir} && git apply - <<"PATCH"\n${spec.patch}\nPATCH'`);
222
+ }
223
+ const f2p = (spec.failToPass ?? []).map(shq).join(' ');
224
+ const p2p = (spec.passToPass ?? []).map(shq).join(' ');
225
+ evalCommands.push(`${sshLocal}'cd ${workdir} && npx --yes @metaharness/darwin swebench-eval` +
226
+ `${spec.repo ? ` --repo ${shq(spec.repo)}` : ''}` +
227
+ `${spec.baseCommit ? ` --base ${shq(spec.baseCommit)}` : ''}` +
228
+ `${f2p ? ` --fail-to-pass ${f2p}` : ''}` +
229
+ `${p2p ? ` --pass-to-pass ${p2p}` : ''} --json'`);
230
+ parseHint = 'resolved = all FAIL_TO_PASS now pass AND all PASS_TO_PASS still pass';
231
+ }
232
+ return { kind, remote: host, probeCommands, evalCommands, parseHint };
233
+ }
234
+ // ── Default SSH runner (real exec; injectable for tests) ─────────────────────
235
+ /**
236
+ * Default command executor: shells out, piping any heredoc/stdin via argv only
237
+ * (commands are self-contained strings run through `sh -c`). Never invoked on
238
+ * the default (dry-run) path.
239
+ */
240
+ export const defaultCommandExec = (cmd, args, opts) => new Promise((resolve) => {
241
+ let child;
242
+ try {
243
+ child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
244
+ }
245
+ catch (err) {
246
+ resolve({ stdout: '', stderr: err instanceof Error ? err.message : String(err), code: null });
247
+ return;
248
+ }
249
+ let stdout = '';
250
+ let stderr = '';
251
+ let done = false;
252
+ const finish = (r) => { if (!done) {
253
+ done = true;
254
+ clearTimeout(timer);
255
+ resolve(r);
256
+ } };
257
+ const timer = setTimeout(() => {
258
+ try {
259
+ child.kill('SIGTERM');
260
+ }
261
+ catch { /* dead */ }
262
+ finish({ stdout, stderr: stderr || `timed out after ${opts.timeoutMs}ms`, code: null });
263
+ }, opts.timeoutMs);
264
+ timer.unref?.();
265
+ child.stdout?.on('data', (d) => { stdout += d.toString(); });
266
+ child.stderr?.on('data', (d) => { stderr += d.toString(); });
267
+ child.on('error', (e) => finish({ stdout, stderr: e.message, code: null }));
268
+ child.on('close', (code) => finish({ stdout, stderr, code }));
269
+ });
270
+ /**
271
+ * Build a real SSH-backed oracle runner. `exec` is injectable so tests assert
272
+ * command construction without ever touching a network. Each plan command is
273
+ * run via `sh -c <command-string>` so the fully-rendered ssh line executes as-is.
274
+ */
275
+ export function createSshOracleRunner(exec = defaultCommandExec, timeoutMs = 15 * 60 * 1000) {
276
+ const run = (command) => exec('sh', ['-c', command], { timeoutMs });
277
+ return {
278
+ async preflight(plan) {
279
+ if (!plan.remote)
280
+ return { ok: false, reason: 'no remote configured' };
281
+ for (const c of plan.probeCommands) {
282
+ const res = await run(c);
283
+ if (res.code !== 0) {
284
+ return { ok: false, reason: `probe failed (${c.slice(0, 60)}…): ${(res.stderr || '').slice(0, 120)}` };
285
+ }
286
+ }
287
+ return { ok: true, reason: 'remote reachable; docker/darwin present' };
288
+ },
289
+ async runEval(plan) {
290
+ let lastReason = 'no eval commands';
291
+ for (const c of plan.evalCommands) {
292
+ const res = await run(c);
293
+ if (res.code !== 0) {
294
+ return { resolved: false, reason: `eval failed (exit ${res.code}): ${(res.stderr || res.stdout || '').slice(0, 160)}` };
295
+ }
296
+ lastReason = interpretEvalStdout(res.stdout);
297
+ if (lastReason.startsWith('NOT_RESOLVED')) {
298
+ return { resolved: false, reason: lastReason };
299
+ }
300
+ }
301
+ return { resolved: true, reason: `${plan.parseHint} — ${lastReason}` };
302
+ },
303
+ };
304
+ }
305
+ /** Interpret an eval command's JSON stdout for an explicit pass/fail signal. */
306
+ function interpretEvalStdout(stdout) {
307
+ const trimmed = (stdout ?? '').trim();
308
+ const m = trimmed.match(/\{[\s\S]*\}/);
309
+ if (m) {
310
+ try {
311
+ const o = JSON.parse(m[0]);
312
+ if (o.passed === false || o.resolved === false)
313
+ return 'NOT_RESOLVED (json.passed=false)';
314
+ if (o.passed === true || o.resolved === true)
315
+ return 'json.passed=true';
316
+ }
317
+ catch { /* fall through to exit-code semantics */ }
318
+ }
319
+ return 'exit 0';
320
+ }
321
+ // ── small helpers ────────────────────────────────────────────────────────
322
+ async function safeProbe(runner, plan) {
323
+ try {
324
+ return await runner.preflight(plan);
325
+ }
326
+ catch (err) {
327
+ return { ok: false, reason: `probe threw: ${err instanceof Error ? err.message : String(err)}` };
328
+ }
329
+ }
330
+ async function safeRunEval(runner, plan, spec) {
331
+ try {
332
+ return await runner.runEval(plan, spec);
333
+ }
334
+ catch (err) {
335
+ return { resolved: false, reason: `eval threw: ${err instanceof Error ? err.message : String(err)}` };
336
+ }
337
+ }
338
+ function toJudgeItem(t) {
339
+ return { id: t.id, task: t.task ?? '', output: t.output ?? '' };
340
+ }
341
+ function sanitize(s) {
342
+ return s.replace(/[^A-Za-z0-9._-]/g, '_');
343
+ }
344
+ /** Single-quote a token for safe interpolation into a shell command string. */
345
+ function shq(s) {
346
+ return `'${String(s).replace(/'/g, `'\\''`)}'`;
347
+ }
348
+ export default labelResolved;
349
+ //# sourceMappingURL=distill-oracle.js.map
@@ -0,0 +1,168 @@
1
+ /**
2
+ * fable-harness.ts — Cost-disciplined headless Fable LLM-as-judge harness.
3
+ *
4
+ * This is TIER 2 of the tiered `resolved` oracle (see distill-oracle.ts). When
5
+ * a trajectory has NO mechanical test spec to execute, we judge whether its
6
+ * completion actually resolved the task with a headless Fable model — a smarter
7
+ * proxy than the structural verifier, but still a proxy (provenance tag
8
+ * `judge:fable`, never presented as ground truth per ADR-169).
9
+ *
10
+ * ── MEASURED COST DATA (load-bearing — this file is built around it) ────────
11
+ * `claude -p --model claude-fable-5 --output-format json` costs, per call:
12
+ * • ~$1.56 when launched FROM THE PROJECT DIR — it auto-loads CLAUDE.md and
13
+ * ~56k cache tokens of project context we do NOT want for judging.
14
+ * • ~$0.34 from a CLEAN empty cwd with `--append-system-prompt` for the role.
15
+ * • ~$0.02/item when we BATCH ~20 items into a single call (context amortizes
16
+ * ~free across the batch).
17
+ *
18
+ * Therefore this harness MUST, and does:
19
+ * 1. run `claude -p` from a FRESH EMPTY TEMP cwd — never the project dir, so
20
+ * no CLAUDE.md / project context is loaded (the 5x cost driver);
21
+ * 2. carry the judge/reflect role via `--append-system-prompt`, not a project
22
+ * system prompt;
23
+ * 3. BATCH N items per call (default 20) so per-item cost collapses to ~$0.02;
24
+ * 4. pass `--max-budget-usd` as a hard per-call cap and stop launching batches
25
+ * once the cumulative measured spend reaches the caller's budget;
26
+ * 5. be OPT-IN and OFF BY DEFAULT — nothing here runs unless a caller
27
+ * explicitly constructs the harness AND provides a budget cap.
28
+ *
29
+ * SAFETY: constructing this class spends nothing. Only `judgeBatch` /
30
+ * `reflectFailures` spawn `claude`, and only when `maxBudgetUsd > 0`. The
31
+ * `spawnClaude` implementation is injectable so tests never touch the real CLI.
32
+ *
33
+ * @module services/fable-harness
34
+ */
35
+ export declare const FABLE_COST_MODEL: {
36
+ /** Measured $/call when launched from the project dir (loads CLAUDE.md). Anti-pattern. */
37
+ readonly perCallProjectCwdUsd: 1.56;
38
+ /** Measured $/call from a clean cwd with --append-system-prompt. */
39
+ readonly perCallCleanCwdUsd: 0.34;
40
+ /** Measured amortized $/item when batching ~20 items per call. */
41
+ readonly perItemBatchedUsd: 0.02;
42
+ /** Default items per `claude -p` call. */
43
+ readonly defaultBatchSize: 20;
44
+ /** The judge model. */
45
+ readonly model: "claude-fable-5";
46
+ };
47
+ /**
48
+ * Estimate the USD cost of judging `itemCount` items in batches of `batchSize`.
49
+ * Uses the measured amortized per-item figure; callers use this to size a
50
+ * budget cap before opting in.
51
+ */
52
+ export declare function estimateFableCostUsd(itemCount: number, batchSize?: number): number;
53
+ /** One item to judge: did `output` actually resolve `task`? */
54
+ export interface JudgeItem {
55
+ id: string;
56
+ task: string;
57
+ output: string;
58
+ }
59
+ /** Verdict for a single judged item. */
60
+ export interface JudgeResult {
61
+ id: string;
62
+ resolved: boolean;
63
+ /** 0..1 self-reported judge confidence. */
64
+ confidence: number;
65
+ reason: string;
66
+ }
67
+ /** One item for reflective failure analysis (GEPA/evolve mutation input). */
68
+ export interface ReflectItem {
69
+ id: string;
70
+ task: string;
71
+ output: string;
72
+ /** Optional signal that this trajectory is believed to have failed. */
73
+ failureHint?: string;
74
+ }
75
+ /** Reflective diagnosis for a single item (the reflective-mutation SOTA trick). */
76
+ export interface ReflectResult {
77
+ id: string;
78
+ failureClass: string;
79
+ diagnosis: string;
80
+ mutationHint: string;
81
+ }
82
+ /** Result of a raw claude spawn. */
83
+ export interface ClaudeSpawnResult {
84
+ stdout: string;
85
+ stderr: string;
86
+ code: number | null;
87
+ /** Measured spend for this call, parsed from the JSON envelope when present. */
88
+ costUsd?: number;
89
+ }
90
+ /**
91
+ * Injectable spawner. Receives the argv (after `claude`), the prompt to pipe to
92
+ * stdin, and the cwd (a fresh empty temp dir). Default implementation shells out
93
+ * to the real `claude` CLI; tests inject a fake.
94
+ */
95
+ export type ClaudeSpawnFn = (argv: string[], stdinPrompt: string, cwd: string, opts: {
96
+ timeoutMs: number;
97
+ }) => Promise<ClaudeSpawnResult>;
98
+ export interface FableHarnessOptions {
99
+ /** Model id (default claude-fable-5). */
100
+ model?: string;
101
+ /** Items per `claude -p` call (default 20). */
102
+ batchSize?: number;
103
+ /**
104
+ * Hard budget cap in USD across all calls this harness makes. REQUIRED to be
105
+ * > 0 for any spend to happen — 0/undefined means the harness refuses to
106
+ * spawn (safe default).
107
+ */
108
+ maxBudgetUsd?: number;
109
+ /** Per-call timeout (default 5 min). */
110
+ timeoutMs?: number;
111
+ /** Injected spawner for tests; defaults to the real `claude` CLI. */
112
+ spawnClaude?: ClaudeSpawnFn;
113
+ }
114
+ /**
115
+ * Default `claude -p` spawner. Pipes the prompt via stdin (never as an argv
116
+ * positional — mirrors the #1852 fix so shell metachars in prompts are never
117
+ * re-tokenized), runs in the provided (temp) cwd, and returns stdout/stderr.
118
+ * Parses `total_cost_usd` from the `--output-format json` envelope when present.
119
+ */
120
+ export declare const defaultSpawnClaude: ClaudeSpawnFn;
121
+ /** Pull `total_cost_usd`/`cost_usd` out of a claude `--output-format json` envelope. */
122
+ export declare function parseCostFromEnvelope(stdout: string): number | undefined;
123
+ export declare class FableHarness {
124
+ private readonly model;
125
+ private readonly batchSize;
126
+ private readonly maxBudgetUsd;
127
+ private readonly timeoutMs;
128
+ private readonly spawnClaude;
129
+ private spentUsd;
130
+ constructor(opts?: FableHarnessOptions);
131
+ /** Cumulative measured spend across all calls this harness has made. */
132
+ getSpentUsd(): number;
133
+ /** True when a positive budget cap is configured (a precondition for any spend). */
134
+ isEnabled(): boolean;
135
+ /**
136
+ * Judge a set of items in batches. Returns one JudgeResult per input id that
137
+ * the model returned. Items that fall outside the budget, or that the model
138
+ * omits, are simply absent from the result — the caller (distill-oracle) is
139
+ * responsible for falling back to the structural proxy for those.
140
+ *
141
+ * Spends $0 and returns [] when no budget cap is configured.
142
+ */
143
+ judgeBatch(items: JudgeItem[]): Promise<JudgeResult[]>;
144
+ /**
145
+ * Reflective failure analysis over items — the second cost-disciplined entry
146
+ * point, used by GEPA/evolve for mutation hints. Same batching + budget
147
+ * discipline as judgeBatch. Returns [] when no budget cap is configured.
148
+ */
149
+ reflectFailures(items: ReflectItem[]): Promise<ReflectResult[]>;
150
+ /** Build the argv for a `claude -p` call. Exposed shape for testability. */
151
+ buildArgv(systemPrompt: string): string[];
152
+ /**
153
+ * Run one batch: create a FRESH EMPTY temp cwd (critical — no project
154
+ * context), spawn `claude -p` there with the role via --append-system-prompt,
155
+ * pipe the batch JSON to stdin, parse the verdict array out of the envelope,
156
+ * and account the measured spend.
157
+ */
158
+ private runBatch;
159
+ }
160
+ /**
161
+ * Extract the model's verdict array. `claude -p --output-format json` wraps the
162
+ * assistant text in an envelope `{ result: "<text>", ... }`; the text is itself
163
+ * the JSON array we asked for. Handle both the enveloped and bare forms, and
164
+ * arrays fenced in ```json blocks.
165
+ */
166
+ export declare function extractVerdictArray(stdout: string): unknown[];
167
+ export default FableHarness;
168
+ //# sourceMappingURL=fable-harness.d.ts.map