claude-flow 3.20.0 → 3.21.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 (30) 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/neural.js +309 -1
  6. package/v3/@claude-flow/cli/dist/src/mcp-client.js +6 -1
  7. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-execute-core.js +33 -0
  8. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +47 -1
  9. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-loader.d.ts +59 -0
  10. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-loader.js +105 -0
  11. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.d.ts +24 -0
  12. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.js +202 -0
  13. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-tools.js +9 -82
  14. package/v3/@claude-flow/cli/dist/src/mcp-tools/index.d.ts +1 -0
  15. package/v3/@claude-flow/cli/dist/src/mcp-tools/index.js +2 -0
  16. package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.d.ts +33 -0
  17. package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.js +31 -0
  18. package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.d.ts +154 -0
  19. package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.js +209 -0
  20. package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.d.ts +140 -0
  21. package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.js +223 -0
  22. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.d.ts +190 -0
  23. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.js +349 -0
  24. package/v3/@claude-flow/cli/dist/src/services/fable-harness.d.ts +168 -0
  25. package/v3/@claude-flow/cli/dist/src/services/fable-harness.js +347 -0
  26. package/v3/@claude-flow/cli/dist/src/services/swarm-memory-branches.d.ts +135 -0
  27. package/v3/@claude-flow/cli/dist/src/services/swarm-memory-branches.js +213 -0
  28. package/v3/@claude-flow/cli/dist/src/services/weight-eft.d.ts +305 -0
  29. package/v3/@claude-flow/cli/dist/src/services/weight-eft.js +296 -0
  30. package/v3/@claude-flow/cli/package.json +5 -4
@@ -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
@@ -0,0 +1,347 @@
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
+ import { spawn } from 'child_process';
36
+ import { mkdtemp, rm } from 'fs/promises';
37
+ import { tmpdir } from 'os';
38
+ import { join } from 'path';
39
+ // ── Cost model (measured — exported so callers/docs share one source) ────────
40
+ export const FABLE_COST_MODEL = {
41
+ /** Measured $/call when launched from the project dir (loads CLAUDE.md). Anti-pattern. */
42
+ perCallProjectCwdUsd: 1.56,
43
+ /** Measured $/call from a clean cwd with --append-system-prompt. */
44
+ perCallCleanCwdUsd: 0.34,
45
+ /** Measured amortized $/item when batching ~20 items per call. */
46
+ perItemBatchedUsd: 0.02,
47
+ /** Default items per `claude -p` call. */
48
+ defaultBatchSize: 20,
49
+ /** The judge model. */
50
+ model: 'claude-fable-5',
51
+ };
52
+ /**
53
+ * Estimate the USD cost of judging `itemCount` items in batches of `batchSize`.
54
+ * Uses the measured amortized per-item figure; callers use this to size a
55
+ * budget cap before opting in.
56
+ */
57
+ export function estimateFableCostUsd(itemCount, batchSize = FABLE_COST_MODEL.defaultBatchSize) {
58
+ if (itemCount <= 0)
59
+ return 0;
60
+ const batches = Math.ceil(itemCount / Math.max(1, batchSize));
61
+ // Amortized per-item dominates; add a tiny floor per batch for the base call.
62
+ const perItem = itemCount * FABLE_COST_MODEL.perItemBatchedUsd;
63
+ const perBatchFloor = batches * 0.0; // per-item figure already includes base amortization
64
+ return Number((perItem + perBatchFloor).toFixed(4));
65
+ }
66
+ // ── Prompt templates (carried via --append-system-prompt) ────────────────────
67
+ const JUDGE_SYSTEM_PROMPT = [
68
+ 'You are a strict evaluator judging whether an agent trajectory RESOLVED its task.',
69
+ 'You receive a JSON array of items: [{id, task, output}].',
70
+ 'For each item decide if `output` genuinely and correctly resolves `task`.',
71
+ 'Be conservative: plausible-but-unverified or partial completions are NOT resolved.',
72
+ 'Reply with ONLY a JSON array, one object per input id, no prose:',
73
+ '[{"id": "...", "resolved": true|false, "confidence": 0.0-1.0, "reason": "<one sentence>"}]',
74
+ ].join('\n');
75
+ const REFLECT_SYSTEM_PROMPT = [
76
+ 'You are a failure-analysis expert supporting reflective mutation (GEPA/evolve).',
77
+ 'You receive a JSON array of items: [{id, task, output, failureHint?}].',
78
+ 'For each item, classify the failure and propose a concrete mutation hint.',
79
+ 'Reply with ONLY a JSON array, one object per input id, no prose:',
80
+ '[{"id":"...","failureClass":"<short label>","diagnosis":"<one sentence>","mutationHint":"<actionable>"}]',
81
+ ].join('\n');
82
+ // ── Default spawner (real CLI) ───────────────────────────────────────────────
83
+ /**
84
+ * Default `claude -p` spawner. Pipes the prompt via stdin (never as an argv
85
+ * positional — mirrors the #1852 fix so shell metachars in prompts are never
86
+ * re-tokenized), runs in the provided (temp) cwd, and returns stdout/stderr.
87
+ * Parses `total_cost_usd` from the `--output-format json` envelope when present.
88
+ */
89
+ export const defaultSpawnClaude = (argv, stdinPrompt, cwd, opts) => new Promise((resolve) => {
90
+ let child;
91
+ try {
92
+ child = spawn('claude', argv, {
93
+ cwd,
94
+ env: { ...process.env, CLAUDE_ENTRYPOINT: 'fable-judge' },
95
+ stdio: ['pipe', 'pipe', 'pipe'],
96
+ windowsHide: true,
97
+ });
98
+ }
99
+ catch (err) {
100
+ resolve({ stdout: '', stderr: err instanceof Error ? err.message : String(err), code: null });
101
+ return;
102
+ }
103
+ let stdout = '';
104
+ let stderr = '';
105
+ let done = false;
106
+ const finish = (r) => {
107
+ if (done)
108
+ return;
109
+ done = true;
110
+ clearTimeout(timer);
111
+ resolve(r);
112
+ };
113
+ const timer = setTimeout(() => {
114
+ try {
115
+ child.kill('SIGTERM');
116
+ }
117
+ catch { /* already dead */ }
118
+ finish({ stdout, stderr: stderr || `timed out after ${opts.timeoutMs}ms`, code: null });
119
+ }, opts.timeoutMs);
120
+ timer.unref?.();
121
+ try {
122
+ child.stdin?.end(stdinPrompt);
123
+ }
124
+ catch { /* surfaced via error */ }
125
+ child.stdout?.on('data', (d) => { stdout += d.toString(); });
126
+ child.stderr?.on('data', (d) => { stderr += d.toString(); });
127
+ child.on('error', (e) => finish({ stdout, stderr: e.message, code: null }));
128
+ child.on('close', (code) => {
129
+ finish({ stdout, stderr, code, costUsd: parseCostFromEnvelope(stdout) });
130
+ });
131
+ });
132
+ /** Pull `total_cost_usd`/`cost_usd` out of a claude `--output-format json` envelope. */
133
+ export function parseCostFromEnvelope(stdout) {
134
+ const env = tryParseJson(stdout);
135
+ if (env && typeof env === 'object') {
136
+ const o = env;
137
+ for (const k of ['total_cost_usd', 'cost_usd', 'costUsd']) {
138
+ if (typeof o[k] === 'number')
139
+ return o[k];
140
+ }
141
+ }
142
+ return undefined;
143
+ }
144
+ // ── Harness ──────────────────────────────────────────────────────────────
145
+ export class FableHarness {
146
+ model;
147
+ batchSize;
148
+ maxBudgetUsd;
149
+ timeoutMs;
150
+ spawnClaude;
151
+ spentUsd = 0;
152
+ constructor(opts = {}) {
153
+ this.model = opts.model ?? FABLE_COST_MODEL.model;
154
+ this.batchSize = Math.max(1, opts.batchSize ?? FABLE_COST_MODEL.defaultBatchSize);
155
+ this.maxBudgetUsd = opts.maxBudgetUsd ?? 0;
156
+ this.timeoutMs = opts.timeoutMs ?? 5 * 60 * 1000;
157
+ this.spawnClaude = opts.spawnClaude ?? defaultSpawnClaude;
158
+ }
159
+ /** Cumulative measured spend across all calls this harness has made. */
160
+ getSpentUsd() {
161
+ return Number(this.spentUsd.toFixed(4));
162
+ }
163
+ /** True when a positive budget cap is configured (a precondition for any spend). */
164
+ isEnabled() {
165
+ return this.maxBudgetUsd > 0;
166
+ }
167
+ /**
168
+ * Judge a set of items in batches. Returns one JudgeResult per input id that
169
+ * the model returned. Items that fall outside the budget, or that the model
170
+ * omits, are simply absent from the result — the caller (distill-oracle) is
171
+ * responsible for falling back to the structural proxy for those.
172
+ *
173
+ * Spends $0 and returns [] when no budget cap is configured.
174
+ */
175
+ async judgeBatch(items) {
176
+ if (items.length === 0)
177
+ return [];
178
+ if (!this.isEnabled())
179
+ return [];
180
+ const out = [];
181
+ for (const batch of chunk(items, this.batchSize)) {
182
+ if (this.spentUsd >= this.maxBudgetUsd)
183
+ break;
184
+ const parsed = await this.runBatch(JUDGE_SYSTEM_PROMPT, batch);
185
+ for (const raw of parsed) {
186
+ const r = normalizeJudge(raw);
187
+ if (r)
188
+ out.push(r);
189
+ }
190
+ }
191
+ return out;
192
+ }
193
+ /**
194
+ * Reflective failure analysis over items — the second cost-disciplined entry
195
+ * point, used by GEPA/evolve for mutation hints. Same batching + budget
196
+ * discipline as judgeBatch. Returns [] when no budget cap is configured.
197
+ */
198
+ async reflectFailures(items) {
199
+ if (items.length === 0)
200
+ return [];
201
+ if (!this.isEnabled())
202
+ return [];
203
+ const out = [];
204
+ for (const batch of chunk(items, this.batchSize)) {
205
+ if (this.spentUsd >= this.maxBudgetUsd)
206
+ break;
207
+ const parsed = await this.runBatch(REFLECT_SYSTEM_PROMPT, batch);
208
+ for (const raw of parsed) {
209
+ const r = normalizeReflect(raw);
210
+ if (r)
211
+ out.push(r);
212
+ }
213
+ }
214
+ return out;
215
+ }
216
+ /** Build the argv for a `claude -p` call. Exposed shape for testability. */
217
+ buildArgv(systemPrompt) {
218
+ const perCallCap = Math.max(0, this.maxBudgetUsd - this.spentUsd);
219
+ return [
220
+ '-p',
221
+ '--model', this.model,
222
+ '--output-format', 'json',
223
+ '--append-system-prompt', systemPrompt,
224
+ '--max-budget-usd', String(round(perCallCap)),
225
+ ];
226
+ }
227
+ /**
228
+ * Run one batch: create a FRESH EMPTY temp cwd (critical — no project
229
+ * context), spawn `claude -p` there with the role via --append-system-prompt,
230
+ * pipe the batch JSON to stdin, parse the verdict array out of the envelope,
231
+ * and account the measured spend.
232
+ */
233
+ async runBatch(systemPrompt, batch) {
234
+ const argv = this.buildArgv(systemPrompt);
235
+ const stdinPrompt = JSON.stringify(batch);
236
+ const cwd = await mkdtemp(join(tmpdir(), 'ruflo-fable-'));
237
+ try {
238
+ const res = await this.spawnClaude(argv, stdinPrompt, cwd, { timeoutMs: this.timeoutMs });
239
+ // Account spend: prefer measured envelope cost, else the amortized estimate.
240
+ this.spentUsd += typeof res.costUsd === 'number'
241
+ ? res.costUsd
242
+ : estimateFableCostUsd(batch.length, this.batchSize);
243
+ if (res.code !== 0 && !res.stdout)
244
+ return [];
245
+ return extractVerdictArray(res.stdout);
246
+ }
247
+ finally {
248
+ await rm(cwd, { recursive: true, force: true }).catch(() => { });
249
+ }
250
+ }
251
+ }
252
+ // ── Parsing helpers ──────────────────────────────────────────────────────
253
+ /**
254
+ * Extract the model's verdict array. `claude -p --output-format json` wraps the
255
+ * assistant text in an envelope `{ result: "<text>", ... }`; the text is itself
256
+ * the JSON array we asked for. Handle both the enveloped and bare forms, and
257
+ * arrays fenced in ```json blocks.
258
+ */
259
+ export function extractVerdictArray(stdout) {
260
+ const trimmed = (stdout ?? '').trim();
261
+ if (!trimmed)
262
+ return [];
263
+ // 1. Enveloped: parse envelope, then its `result` field.
264
+ const env = tryParseJson(trimmed);
265
+ if (env && typeof env === 'object' && !Array.isArray(env)) {
266
+ const result = env.result;
267
+ if (typeof result === 'string') {
268
+ const inner = findJsonArray(result);
269
+ if (inner)
270
+ return inner;
271
+ }
272
+ // Some envelopes embed the array directly.
273
+ const direct = env.results;
274
+ if (Array.isArray(direct))
275
+ return direct;
276
+ }
277
+ if (Array.isArray(env))
278
+ return env;
279
+ // 2. Bare text: find a JSON array anywhere in stdout.
280
+ const arr = findJsonArray(trimmed);
281
+ return arr ?? [];
282
+ }
283
+ function findJsonArray(text) {
284
+ const fenced = text.match(/```(?:json)?\s*(\[[\s\S]*?\])\s*```/);
285
+ if (fenced) {
286
+ const v = tryParseJson(fenced[1]);
287
+ if (Array.isArray(v))
288
+ return v;
289
+ }
290
+ const bare = text.match(/\[[\s\S]*\]/);
291
+ if (bare) {
292
+ const v = tryParseJson(bare[0]);
293
+ if (Array.isArray(v))
294
+ return v;
295
+ }
296
+ return null;
297
+ }
298
+ function normalizeJudge(raw) {
299
+ if (!raw || typeof raw !== 'object')
300
+ return null;
301
+ const o = raw;
302
+ if (typeof o.id !== 'string')
303
+ return null;
304
+ return {
305
+ id: o.id,
306
+ resolved: o.resolved === true || o.resolved === 'true',
307
+ confidence: clamp01(typeof o.confidence === 'number' ? o.confidence : Number(o.confidence)),
308
+ reason: typeof o.reason === 'string' ? o.reason : '',
309
+ };
310
+ }
311
+ function normalizeReflect(raw) {
312
+ if (!raw || typeof raw !== 'object')
313
+ return null;
314
+ const o = raw;
315
+ if (typeof o.id !== 'string')
316
+ return null;
317
+ return {
318
+ id: o.id,
319
+ failureClass: typeof o.failureClass === 'string' ? o.failureClass : 'unknown',
320
+ diagnosis: typeof o.diagnosis === 'string' ? o.diagnosis : '',
321
+ mutationHint: typeof o.mutationHint === 'string' ? o.mutationHint : '',
322
+ };
323
+ }
324
+ function tryParseJson(s) {
325
+ try {
326
+ return JSON.parse(s);
327
+ }
328
+ catch {
329
+ return undefined;
330
+ }
331
+ }
332
+ function chunk(arr, size) {
333
+ const out = [];
334
+ for (let i = 0; i < arr.length; i += size)
335
+ out.push(arr.slice(i, i + size));
336
+ return out;
337
+ }
338
+ function clamp01(n) {
339
+ if (!Number.isFinite(n))
340
+ return 0;
341
+ return Math.max(0, Math.min(1, n));
342
+ }
343
+ function round(n) {
344
+ return Math.round(n * 100) / 100;
345
+ }
346
+ export default FableHarness;
347
+ //# sourceMappingURL=fable-harness.js.map
@@ -0,0 +1,135 @@
1
+ /**
2
+ * SwarmMemoryBranches — per-agent Copy-On-Write memory branching for swarms.
3
+ *
4
+ * ## Why this exists
5
+ *
6
+ * The v3.14.4 release uncovered a tarball-bloat regression: the Darwin
7
+ * git-worktree-per-agent pattern accumulated 3.3 GB of disk because each
8
+ * agent got a *full copy* of shared state. `agenticow` (a sibling RVF-based
9
+ * COW vector store by the same author) forks a branch in a measured **162
10
+ * bytes** regardless of base size — read-through semantics (parent ∪ edits,
11
+ * child wins) instead of a linear-growth snapshot.
12
+ *
13
+ * This service is the swarm-facing consumer of that primitive. The pattern
14
+ * mirrors `agenticow`'s own `examples/parallel-agents`:
15
+ *
16
+ * shared base .rvf
17
+ * → each agent forks a 162-byte COW branch (nativeAnn:true) at spawn
18
+ * → the agent reads/writes its branch in isolation
19
+ * → on success: promote branch → base (merge its edits back)
20
+ * → on failure: discard the branch file (throw the edits away)
21
+ *
22
+ * ## Honest scope (the seam)
23
+ *
24
+ * The current `agent_spawn` MCP path (src/mcp-tools/agent-tools.ts) stores an
25
+ * agent as pure JSON metadata — it does **not** create, copy, or hold any
26
+ * per-agent `.rvf` workspace today. So there is no full-copy for a COW branch
27
+ * to "replace" inline. This service therefore wires in as an **opt-in**: a
28
+ * swarm/agent that actually wants an isolated memory workspace passes a base
29
+ * memory path, and only then does a branch get forked. Default behavior is
30
+ * unchanged.
31
+ *
32
+ * ## Non-fatal + kill-switch (ADR-150)
33
+ *
34
+ * - `agenticow` is an optional dependency — every method degrades to
35
+ * `{ degraded: true }` when it is absent (never throws MODULE_NOT_FOUND).
36
+ * - The `CLAUDE_FLOW_NO_COW_MEMORY=1` env var hard-disables branching so an
37
+ * operator can kill the feature without a redeploy.
38
+ * - agenticow is loaded lazily (dynamic import inside `loadAgenticow`), so
39
+ * importing this module does NOT pull agenticow onto the CLI startup path.
40
+ *
41
+ * @module @claude-flow/cli/services/swarm-memory-branches
42
+ */
43
+ /** Env var that hard-disables per-agent COW branching (operator kill switch). */
44
+ export declare const COW_KILL_SWITCH_ENV = "CLAUDE_FLOW_NO_COW_MEMORY";
45
+ /** True unless the operator set the kill switch. */
46
+ export declare function cowMemoryEnabled(): boolean;
47
+ /** Persisted mapping so promote/discard (a later, separate call) can find the branch. */
48
+ export interface BranchRecord {
49
+ agentId: string;
50
+ basePath: string;
51
+ branchPath: string;
52
+ label: string;
53
+ createdAt: string;
54
+ }
55
+ export interface BranchResult {
56
+ success: boolean;
57
+ agentId: string;
58
+ basePath?: string;
59
+ branchPath?: string;
60
+ label?: string;
61
+ /** Set when the operation was a no-op because COW is unavailable/disabled. */
62
+ degraded?: true;
63
+ reason?: string;
64
+ }
65
+ export interface PromoteResult {
66
+ success: boolean;
67
+ agentId: string;
68
+ promoted: boolean;
69
+ branchPath?: string;
70
+ basePath?: string;
71
+ degraded?: true;
72
+ reason?: string;
73
+ }
74
+ export interface DiscardResult {
75
+ success: boolean;
76
+ agentId: string;
77
+ discarded: boolean;
78
+ branchPath?: string;
79
+ reason?: string;
80
+ }
81
+ /**
82
+ * Manages the branch→base COW lifecycle for a swarm's agents. One instance per
83
+ * project cwd; state is persisted to `.claude-flow/swarm/cow-branches.json` so
84
+ * a branch forked in `branchForAgent` survives to a later `promoteAgent` /
85
+ * `discardAgent` call in a different process.
86
+ */
87
+ export declare class SwarmMemoryBranches {
88
+ private readonly registryPath;
89
+ /**
90
+ * @param registryPath Override the registry location (tests point this at a
91
+ * temp dir). Defaults to `<cwd>/.claude-flow/swarm/cow-branches.json`.
92
+ */
93
+ constructor(registryPath?: string);
94
+ private loadRegistry;
95
+ private saveRegistry;
96
+ /** Look up the branch record for an agent, if one was forked. */
97
+ getBranch(agentId: string): BranchRecord | undefined;
98
+ /**
99
+ * Deterministic per-agent branch path: `<baseDir>/.swarm-cow/<base>.<agentId>.rvf`.
100
+ * Keeps branch files namespaced next to their base so cleanup is obvious.
101
+ */
102
+ private branchPathFor;
103
+ /**
104
+ * Fork a 162-byte COW branch off `base` for `agentId`. The agent then owns
105
+ * an isolated read/write view (parent ∪ its own edits). Idempotent: a second
106
+ * call for the same agent returns the existing branch record.
107
+ *
108
+ * @param base Path to the shared base `.rvf` (absolute or cwd-relative).
109
+ * @param agentId Agent that owns the branch (used as the COW label).
110
+ * @param opts.dimension Required only when `base` does not yet exist.
111
+ * @param opts.nativeAnn Fork with the native Rust ANN path (default true —
112
+ * agent branches are meant to be queried).
113
+ */
114
+ branchForAgent(base: string, agentId: string, opts?: {
115
+ dimension?: number;
116
+ nativeAnn?: boolean;
117
+ }): Promise<BranchResult>;
118
+ /**
119
+ * Promote an agent's branch back into its base (merge its edits + tombstones
120
+ * atomically), then delete the branch file and drop it from the registry.
121
+ * Call on agent success. No-op (`promoted:false`) when the agent has no
122
+ * branch.
123
+ */
124
+ promoteAgent(agentId: string): Promise<PromoteResult>;
125
+ /**
126
+ * Discard an agent's branch — delete the branch file + lineage manifest and
127
+ * drop the registry entry, WITHOUT merging anything into base. Call on agent
128
+ * failure. No-op (`discarded:false`) when the agent has no branch. Never
129
+ * touches agenticow (pure filesystem), so it works even in the degraded path.
130
+ */
131
+ discardAgent(agentId: string): Promise<DiscardResult>;
132
+ private removeBranchFiles;
133
+ private forgetBranch;
134
+ }
135
+ //# sourceMappingURL=swarm-memory-branches.d.ts.map