claude-flow 3.19.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.
- package/package.json +2 -2
- package/v3/@claude-flow/cli/dist/src/agenticow/speculative-exploration.d.ts +148 -0
- package/v3/@claude-flow/cli/dist/src/agenticow/speculative-exploration.js +218 -0
- package/v3/@claude-flow/cli/dist/src/commands/autopilot.js +45 -0
- package/v3/@claude-flow/cli/dist/src/commands/neural.js +366 -12
- package/v3/@claude-flow/cli/dist/src/mcp-client.js +6 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-execute-core.js +33 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +47 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-loader.d.ts +59 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-loader.js +105 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.d.ts +24 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.js +202 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-tools.js +186 -79
- package/v3/@claude-flow/cli/dist/src/mcp-tools/index.d.ts +1 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/index.js +2 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/index.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/ruvector/index.js +1 -1
- package/v3/@claude-flow/cli/dist/src/ruvector/lora-adapter.d.ts +35 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/lora-adapter.js +108 -1
- package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.d.ts +33 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.js +31 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.d.ts +154 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.js +209 -0
- package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.d.ts +140 -0
- package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.js +223 -0
- package/v3/@claude-flow/cli/dist/src/services/distill-oracle.d.ts +190 -0
- package/v3/@claude-flow/cli/dist/src/services/distill-oracle.js +349 -0
- package/v3/@claude-flow/cli/dist/src/services/fable-harness.d.ts +168 -0
- package/v3/@claude-flow/cli/dist/src/services/fable-harness.js +347 -0
- package/v3/@claude-flow/cli/dist/src/services/native-training.d.ts +28 -0
- package/v3/@claude-flow/cli/dist/src/services/native-training.js +62 -5
- package/v3/@claude-flow/cli/dist/src/services/swarm-memory-branches.d.ts +135 -0
- package/v3/@claude-flow/cli/dist/src/services/swarm-memory-branches.js +213 -0
- package/v3/@claude-flow/cli/dist/src/services/weight-eft.d.ts +305 -0
- package/v3/@claude-flow/cli/dist/src/services/weight-eft.js +296 -0
- package/v3/@claude-flow/cli/package.json +5 -4
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckpointGate — agenticow-backed checkpoint/rollback gate for autopilot
|
|
3
|
+
* loops and long-horizon workflows (agenticow integration, step 3).
|
|
4
|
+
*
|
|
5
|
+
* ── The pattern ───────────────────────────────────────────────────────────
|
|
6
|
+
* Before a risky loop tick that mutates `.rvf` memory, take an O(1) agenticow
|
|
7
|
+
* checkpoint (162 bytes, fixed cost — see mcp-tools/agenticow-tools.ts). Run
|
|
8
|
+
* the tick. If it regresses — throws, or the caller's verdict says the outcome
|
|
9
|
+
* got worse (verifier fail / error / worse metric) — roll the memory back to
|
|
10
|
+
* the checkpoint. Rollback is O(edits-since-checkpoint), NOT an O(N) rebuild,
|
|
11
|
+
* and the earlier history stays intact via the `.agenticow.json` lineage
|
|
12
|
+
* manifest. On success the checkpoint is simply kept as the new good baseline.
|
|
13
|
+
*
|
|
14
|
+
* The three lifecycle verbs (checkpoint / rollback / promote) already exist in
|
|
15
|
+
* the `agenticow` package and are surfaced as MCP tools in
|
|
16
|
+
* `mcp-tools/agenticow-tools.ts`. This module is the *orchestration hook* that
|
|
17
|
+
* calls them from inside a loop — the MCP surface is for agents, this is for
|
|
18
|
+
* the in-process loop.
|
|
19
|
+
*
|
|
20
|
+
* ── Architectural constraint (ADR-150) ────────────────────────────────────
|
|
21
|
+
* `agenticow` lives in `optionalDependencies` and must NEVER be a hard runtime
|
|
22
|
+
* dependency. This gate degrades gracefully in three ways, each of which runs
|
|
23
|
+
* the tick UNGUARDED rather than failing:
|
|
24
|
+
* 1. package missing → { degraded: true, reason: 'agenticow-not-found' }
|
|
25
|
+
* 2. kill-switch env set → { degraded: true, reason: 'kill-switch' }
|
|
26
|
+
* 3. no memory path configured → { degraded: true, reason: 'no-memory-path' }
|
|
27
|
+
* The package is lazy-loaded on the first guard() call, so importing this
|
|
28
|
+
* module has zero startup cost.
|
|
29
|
+
*
|
|
30
|
+
* The optional-dep loader + path/label/lineage helpers are the canonical ones
|
|
31
|
+
* from `mcp-tools/agenticow-loader.ts` — one implementation shared across every
|
|
32
|
+
* agenticow consumer (verbs, swarm branches, speculative, oracle, this gate).
|
|
33
|
+
*
|
|
34
|
+
* @module @claude-flow/cli/services/checkpoint-gate
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Env var pointing at the `.rvf` memory file that a long-horizon loop mutates.
|
|
38
|
+
* When set, the autopilot loop opts in to checkpoint/rollback around each tick.
|
|
39
|
+
* When unset, the gate is a transparent pass-through.
|
|
40
|
+
*/
|
|
41
|
+
export declare const CHECKPOINT_MEM_ENV = "CLAUDE_FLOW_AUTOPILOT_CHECKPOINT_MEM";
|
|
42
|
+
/**
|
|
43
|
+
* Kill switch. When truthy (`1`/`true`/`yes`), the gate never touches agenticow
|
|
44
|
+
* and every guard() runs the tick unguarded. Lets an operator disable the
|
|
45
|
+
* feature without changing config or code.
|
|
46
|
+
*/
|
|
47
|
+
export declare const KILL_SWITCH_ENV = "CLAUDE_FLOW_AGENTICOW_DISABLE";
|
|
48
|
+
/** Outcome of a guarded tick. `result` is undefined only when the tick threw
|
|
49
|
+
* AND `rethrow` was disabled. */
|
|
50
|
+
export interface CheckpointGuardResult<T> {
|
|
51
|
+
/** The value fn() returned (undefined if it threw with rethrow:false). */
|
|
52
|
+
result: T | undefined;
|
|
53
|
+
/** True when a checkpoint was actually taken before running fn. */
|
|
54
|
+
checkpointed: boolean;
|
|
55
|
+
/** True when memory was rolled back to the checkpoint (throw or regression). */
|
|
56
|
+
rolledBack: boolean;
|
|
57
|
+
/** True when the gate could not engage agenticow and ran fn unguarded. */
|
|
58
|
+
degraded: boolean;
|
|
59
|
+
/** Machine-readable reason for degraded / rollback. */
|
|
60
|
+
reason?: string;
|
|
61
|
+
/** The checkpoint label, when one was taken. */
|
|
62
|
+
checkpointLabel?: string;
|
|
63
|
+
/** The error fn() threw, when rethrow was disabled. */
|
|
64
|
+
error?: unknown;
|
|
65
|
+
}
|
|
66
|
+
export interface GuardOptions<T> {
|
|
67
|
+
/**
|
|
68
|
+
* Verdict function: return true when `result` represents a regression that
|
|
69
|
+
* should trigger a rollback. Defaults to treating `{success:false}` /
|
|
70
|
+
* `{ok:false}` / `{regressed:true}` as regressions.
|
|
71
|
+
*/
|
|
72
|
+
isRegression?: (result: T) => boolean;
|
|
73
|
+
/**
|
|
74
|
+
* When the tick throws: roll back, then re-throw the original error
|
|
75
|
+
* (default true). Set false to swallow the error and return it on the
|
|
76
|
+
* result object instead — useful when the loop must never crash.
|
|
77
|
+
*/
|
|
78
|
+
rethrow?: boolean;
|
|
79
|
+
/** Optional checkpoint id to roll back to (defaults to most recent). */
|
|
80
|
+
checkpointId?: string;
|
|
81
|
+
}
|
|
82
|
+
export declare class CheckpointGate {
|
|
83
|
+
/** True when the kill-switch env is set to a truthy value. */
|
|
84
|
+
static isKillSwitchSet(): boolean;
|
|
85
|
+
/** The configured loop memory path, or undefined when the feature is off. */
|
|
86
|
+
static configuredMemPath(): string | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Lazy-load agenticow. Returns null when the package is absent (optional dep)
|
|
89
|
+
* or the kill switch is set. Any *other* import error is re-thrown — a broken
|
|
90
|
+
* install should be loud, a missing optional dep should be silent.
|
|
91
|
+
*/
|
|
92
|
+
private load;
|
|
93
|
+
/** True when agenticow can be engaged (present + not killed). */
|
|
94
|
+
available(): Promise<boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* Take a checkpoint on the given `.rvf` memory file. Non-throwing except for
|
|
97
|
+
* validation errors (path traversal, bad label) — those are surfaced so a
|
|
98
|
+
* misconfiguration is caught early.
|
|
99
|
+
*/
|
|
100
|
+
checkpoint(memPath: string, label: string): Promise<{
|
|
101
|
+
ok: boolean;
|
|
102
|
+
degraded: boolean;
|
|
103
|
+
reason?: string;
|
|
104
|
+
checkpoint?: unknown;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Roll the given `.rvf` memory file back to a checkpoint. Discards edits made
|
|
108
|
+
* since (O(edits-since-checkpoint)). Omit `checkpointId` to target the most
|
|
109
|
+
* recent checkpoint.
|
|
110
|
+
*/
|
|
111
|
+
rollback(memPath: string, checkpointId?: string): Promise<{
|
|
112
|
+
ok: boolean;
|
|
113
|
+
degraded: boolean;
|
|
114
|
+
reason?: string;
|
|
115
|
+
result?: unknown;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Guard a risky loop tick with a checkpoint/rollback bracket.
|
|
119
|
+
*
|
|
120
|
+
* const outcome = await gate.guard(memPath, 'iter-7', async () => runTick());
|
|
121
|
+
*
|
|
122
|
+
* Behaviour:
|
|
123
|
+
* - No memPath / kill-switch / agenticow-absent → run fn unguarded, degraded:true.
|
|
124
|
+
* - Checkpoint fails to engage (degraded) → run fn unguarded, degraded:true.
|
|
125
|
+
* - fn throws → roll back to checkpoint, then re-throw (unless rethrow:false).
|
|
126
|
+
* - fn returns a regression verdict → roll back, return result with rolledBack:true.
|
|
127
|
+
* - fn succeeds → keep the checkpoint, return result with rolledBack:false.
|
|
128
|
+
*
|
|
129
|
+
* The gate is non-fatal: an internal agenticow failure never masks fn()'s own
|
|
130
|
+
* result — if checkpointing throws, fn still runs unguarded.
|
|
131
|
+
*/
|
|
132
|
+
guard<T>(memPath: string | undefined, label: string, fn: () => Promise<T>, opts?: GuardOptions<T>): Promise<CheckpointGuardResult<T>>;
|
|
133
|
+
/** Roll back without throwing — used on the failure path so a rollback error
|
|
134
|
+
* never masks the original tick failure. */
|
|
135
|
+
private safeRollback;
|
|
136
|
+
}
|
|
137
|
+
export declare function getCheckpointGate(): CheckpointGate;
|
|
138
|
+
/** Reset the lazy-load cache — test-only. */
|
|
139
|
+
export declare function __resetCheckpointGateForTests(): void;
|
|
140
|
+
//# sourceMappingURL=checkpoint-gate.d.ts.map
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckpointGate — agenticow-backed checkpoint/rollback gate for autopilot
|
|
3
|
+
* loops and long-horizon workflows (agenticow integration, step 3).
|
|
4
|
+
*
|
|
5
|
+
* ── The pattern ───────────────────────────────────────────────────────────
|
|
6
|
+
* Before a risky loop tick that mutates `.rvf` memory, take an O(1) agenticow
|
|
7
|
+
* checkpoint (162 bytes, fixed cost — see mcp-tools/agenticow-tools.ts). Run
|
|
8
|
+
* the tick. If it regresses — throws, or the caller's verdict says the outcome
|
|
9
|
+
* got worse (verifier fail / error / worse metric) — roll the memory back to
|
|
10
|
+
* the checkpoint. Rollback is O(edits-since-checkpoint), NOT an O(N) rebuild,
|
|
11
|
+
* and the earlier history stays intact via the `.agenticow.json` lineage
|
|
12
|
+
* manifest. On success the checkpoint is simply kept as the new good baseline.
|
|
13
|
+
*
|
|
14
|
+
* The three lifecycle verbs (checkpoint / rollback / promote) already exist in
|
|
15
|
+
* the `agenticow` package and are surfaced as MCP tools in
|
|
16
|
+
* `mcp-tools/agenticow-tools.ts`. This module is the *orchestration hook* that
|
|
17
|
+
* calls them from inside a loop — the MCP surface is for agents, this is for
|
|
18
|
+
* the in-process loop.
|
|
19
|
+
*
|
|
20
|
+
* ── Architectural constraint (ADR-150) ────────────────────────────────────
|
|
21
|
+
* `agenticow` lives in `optionalDependencies` and must NEVER be a hard runtime
|
|
22
|
+
* dependency. This gate degrades gracefully in three ways, each of which runs
|
|
23
|
+
* the tick UNGUARDED rather than failing:
|
|
24
|
+
* 1. package missing → { degraded: true, reason: 'agenticow-not-found' }
|
|
25
|
+
* 2. kill-switch env set → { degraded: true, reason: 'kill-switch' }
|
|
26
|
+
* 3. no memory path configured → { degraded: true, reason: 'no-memory-path' }
|
|
27
|
+
* The package is lazy-loaded on the first guard() call, so importing this
|
|
28
|
+
* module has zero startup cost.
|
|
29
|
+
*
|
|
30
|
+
* The optional-dep loader + path/label/lineage helpers are the canonical ones
|
|
31
|
+
* from `mcp-tools/agenticow-loader.ts` — one implementation shared across every
|
|
32
|
+
* agenticow consumer (verbs, swarm branches, speculative, oracle, this gate).
|
|
33
|
+
*
|
|
34
|
+
* @module @claude-flow/cli/services/checkpoint-gate
|
|
35
|
+
*/
|
|
36
|
+
import { loadAgenticow, __resetAgenticowCache, resolveMemoryPath, manifestFor, validateLabel, openWithLineage, } from '../mcp-tools/agenticow-loader.js';
|
|
37
|
+
/**
|
|
38
|
+
* Env var pointing at the `.rvf` memory file that a long-horizon loop mutates.
|
|
39
|
+
* When set, the autopilot loop opts in to checkpoint/rollback around each tick.
|
|
40
|
+
* When unset, the gate is a transparent pass-through.
|
|
41
|
+
*/
|
|
42
|
+
export const CHECKPOINT_MEM_ENV = 'CLAUDE_FLOW_AUTOPILOT_CHECKPOINT_MEM';
|
|
43
|
+
/**
|
|
44
|
+
* Kill switch. When truthy (`1`/`true`/`yes`), the gate never touches agenticow
|
|
45
|
+
* and every guard() runs the tick unguarded. Lets an operator disable the
|
|
46
|
+
* feature without changing config or code.
|
|
47
|
+
*/
|
|
48
|
+
export const KILL_SWITCH_ENV = 'CLAUDE_FLOW_AGENTICOW_DISABLE';
|
|
49
|
+
function defaultIsRegression(result) {
|
|
50
|
+
if (!result || typeof result !== 'object')
|
|
51
|
+
return false;
|
|
52
|
+
const r = result;
|
|
53
|
+
return r.success === false || r.ok === false || r.regressed === true;
|
|
54
|
+
}
|
|
55
|
+
// ── The gate ────────────────────────────────────────────────────────────────
|
|
56
|
+
export class CheckpointGate {
|
|
57
|
+
/** True when the kill-switch env is set to a truthy value. */
|
|
58
|
+
static isKillSwitchSet() {
|
|
59
|
+
const v = (process.env[KILL_SWITCH_ENV] || '').trim().toLowerCase();
|
|
60
|
+
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
61
|
+
}
|
|
62
|
+
/** The configured loop memory path, or undefined when the feature is off. */
|
|
63
|
+
static configuredMemPath() {
|
|
64
|
+
const p = (process.env[CHECKPOINT_MEM_ENV] || '').trim();
|
|
65
|
+
return p || undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Lazy-load agenticow. Returns null when the package is absent (optional dep)
|
|
69
|
+
* or the kill switch is set. Any *other* import error is re-thrown — a broken
|
|
70
|
+
* install should be loud, a missing optional dep should be silent.
|
|
71
|
+
*/
|
|
72
|
+
async load() {
|
|
73
|
+
if (CheckpointGate.isKillSwitchSet())
|
|
74
|
+
return null;
|
|
75
|
+
// Delegates to the canonical loader (#22) — one cache, one degradation
|
|
76
|
+
// policy shared across every agenticow consumer.
|
|
77
|
+
return loadAgenticow();
|
|
78
|
+
}
|
|
79
|
+
/** True when agenticow can be engaged (present + not killed). */
|
|
80
|
+
async available() {
|
|
81
|
+
return (await this.load()) !== null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Take a checkpoint on the given `.rvf` memory file. Non-throwing except for
|
|
85
|
+
* validation errors (path traversal, bad label) — those are surfaced so a
|
|
86
|
+
* misconfiguration is caught early.
|
|
87
|
+
*/
|
|
88
|
+
async checkpoint(memPath, label) {
|
|
89
|
+
const api = await this.load();
|
|
90
|
+
if (!api)
|
|
91
|
+
return { ok: false, degraded: true, reason: CheckpointGate.isKillSwitchSet() ? 'kill-switch' : 'agenticow-not-found' };
|
|
92
|
+
const path = resolveMemoryPath(String(memPath));
|
|
93
|
+
const lbl = validateLabel(String(label));
|
|
94
|
+
const mem = await openWithLineage(api, path);
|
|
95
|
+
try {
|
|
96
|
+
const cp = await mem.checkpoint(lbl);
|
|
97
|
+
await mem.save?.(manifestFor(path));
|
|
98
|
+
return { ok: true, degraded: false, checkpoint: cp };
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
await mem.close?.();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Roll the given `.rvf` memory file back to a checkpoint. Discards edits made
|
|
106
|
+
* since (O(edits-since-checkpoint)). Omit `checkpointId` to target the most
|
|
107
|
+
* recent checkpoint.
|
|
108
|
+
*/
|
|
109
|
+
async rollback(memPath, checkpointId) {
|
|
110
|
+
const api = await this.load();
|
|
111
|
+
if (!api)
|
|
112
|
+
return { ok: false, degraded: true, reason: CheckpointGate.isKillSwitchSet() ? 'kill-switch' : 'agenticow-not-found' };
|
|
113
|
+
const path = resolveMemoryPath(String(memPath));
|
|
114
|
+
const mem = await openWithLineage(api, path);
|
|
115
|
+
try {
|
|
116
|
+
const r = checkpointId ? await mem.rollback(checkpointId) : await mem.rollback();
|
|
117
|
+
await mem.save?.(manifestFor(path));
|
|
118
|
+
return { ok: true, degraded: false, result: r };
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
await mem.close?.();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Guard a risky loop tick with a checkpoint/rollback bracket.
|
|
126
|
+
*
|
|
127
|
+
* const outcome = await gate.guard(memPath, 'iter-7', async () => runTick());
|
|
128
|
+
*
|
|
129
|
+
* Behaviour:
|
|
130
|
+
* - No memPath / kill-switch / agenticow-absent → run fn unguarded, degraded:true.
|
|
131
|
+
* - Checkpoint fails to engage (degraded) → run fn unguarded, degraded:true.
|
|
132
|
+
* - fn throws → roll back to checkpoint, then re-throw (unless rethrow:false).
|
|
133
|
+
* - fn returns a regression verdict → roll back, return result with rolledBack:true.
|
|
134
|
+
* - fn succeeds → keep the checkpoint, return result with rolledBack:false.
|
|
135
|
+
*
|
|
136
|
+
* The gate is non-fatal: an internal agenticow failure never masks fn()'s own
|
|
137
|
+
* result — if checkpointing throws, fn still runs unguarded.
|
|
138
|
+
*/
|
|
139
|
+
async guard(memPath, label, fn, opts = {}) {
|
|
140
|
+
const rethrow = opts.rethrow !== false;
|
|
141
|
+
const isRegression = opts.isRegression ?? defaultIsRegression;
|
|
142
|
+
// Fast paths: feature off → transparent pass-through.
|
|
143
|
+
if (!memPath) {
|
|
144
|
+
const result = await fn();
|
|
145
|
+
return { result, checkpointed: false, rolledBack: false, degraded: true, reason: 'no-memory-path' };
|
|
146
|
+
}
|
|
147
|
+
if (CheckpointGate.isKillSwitchSet()) {
|
|
148
|
+
const result = await fn();
|
|
149
|
+
return { result, checkpointed: false, rolledBack: false, degraded: true, reason: 'kill-switch' };
|
|
150
|
+
}
|
|
151
|
+
// Try to checkpoint. Any failure here degrades to unguarded — the loop must
|
|
152
|
+
// never break because memory branching is unavailable.
|
|
153
|
+
let checkpointed = false;
|
|
154
|
+
try {
|
|
155
|
+
const cp = await this.checkpoint(memPath, label);
|
|
156
|
+
if (!cp.ok) {
|
|
157
|
+
const result = await fn();
|
|
158
|
+
return { result, checkpointed: false, rolledBack: false, degraded: true, reason: cp.reason };
|
|
159
|
+
}
|
|
160
|
+
checkpointed = true;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
const result = await fn();
|
|
164
|
+
return {
|
|
165
|
+
result,
|
|
166
|
+
checkpointed: false,
|
|
167
|
+
rolledBack: false,
|
|
168
|
+
degraded: true,
|
|
169
|
+
reason: `checkpoint-error: ${err instanceof Error ? err.message : String(err)}`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// Run the tick under the checkpoint.
|
|
173
|
+
let result;
|
|
174
|
+
try {
|
|
175
|
+
result = await fn();
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
const rb = await this.safeRollback(memPath, opts.checkpointId);
|
|
179
|
+
if (rethrow)
|
|
180
|
+
throw err;
|
|
181
|
+
return {
|
|
182
|
+
result: undefined,
|
|
183
|
+
checkpointed,
|
|
184
|
+
rolledBack: rb,
|
|
185
|
+
degraded: false,
|
|
186
|
+
reason: 'tick-threw',
|
|
187
|
+
checkpointLabel: label,
|
|
188
|
+
error: err,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Verdict: regression → roll back, keep the returned result for the caller.
|
|
192
|
+
if (isRegression(result)) {
|
|
193
|
+
const rb = await this.safeRollback(memPath, opts.checkpointId);
|
|
194
|
+
return { result, checkpointed, rolledBack: rb, degraded: false, reason: 'regression-verdict', checkpointLabel: label };
|
|
195
|
+
}
|
|
196
|
+
// Success → keep the checkpoint as the new baseline.
|
|
197
|
+
return { result, checkpointed, rolledBack: false, degraded: false, checkpointLabel: label };
|
|
198
|
+
}
|
|
199
|
+
/** Roll back without throwing — used on the failure path so a rollback error
|
|
200
|
+
* never masks the original tick failure. */
|
|
201
|
+
async safeRollback(memPath, checkpointId) {
|
|
202
|
+
try {
|
|
203
|
+
const r = await this.rollback(memPath, checkpointId);
|
|
204
|
+
return r.ok;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** Shared process-wide gate instance. */
|
|
212
|
+
let _shared = null;
|
|
213
|
+
export function getCheckpointGate() {
|
|
214
|
+
if (!_shared)
|
|
215
|
+
_shared = new CheckpointGate();
|
|
216
|
+
return _shared;
|
|
217
|
+
}
|
|
218
|
+
/** Reset the lazy-load cache — test-only. */
|
|
219
|
+
export function __resetCheckpointGateForTests() {
|
|
220
|
+
__resetAgenticowCache();
|
|
221
|
+
_shared = null;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=checkpoint-gate.js.map
|
|
@@ -0,0 +1,190 @@
|
|
|
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 { type VerifyTaskKind } from '../ruvector/output-verifier.js';
|
|
38
|
+
import { FableHarness, type ReflectItem, type ReflectResult } from './fable-harness.js';
|
|
39
|
+
export type ResolvedProvenance = 'oracle:test-exec' | 'judge:fable' | 'proxy:structural';
|
|
40
|
+
/** SWE-bench-shaped / bench-suite-mapped test spec that Tier 1 can execute. */
|
|
41
|
+
export interface TestSpec {
|
|
42
|
+
/** SWE-bench FAIL_TO_PASS tests — must go red→green for `resolved`. */
|
|
43
|
+
failToPass?: string[];
|
|
44
|
+
/** SWE-bench PASS_TO_PASS tests — must stay green (no regressions). */
|
|
45
|
+
passToPass?: string[];
|
|
46
|
+
/** Repo identifier (for the remote checkout). */
|
|
47
|
+
repo?: string;
|
|
48
|
+
/** Base commit the patch applies onto. */
|
|
49
|
+
baseCommit?: string;
|
|
50
|
+
/** Candidate patch/diff to apply before evaluating. */
|
|
51
|
+
patch?: string;
|
|
52
|
+
/** An explicit eval command that returns 0 iff the task is resolved. */
|
|
53
|
+
evalCommand?: string;
|
|
54
|
+
/** metaharness/darwin bench-suite this case belongs to. */
|
|
55
|
+
benchSuite?: string;
|
|
56
|
+
/** Case id within the bench suite. */
|
|
57
|
+
benchCase?: string;
|
|
58
|
+
/** Working directory on the remote host (default derived). */
|
|
59
|
+
workdir?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Minimal trajectory contract. Extra fields are preserved verbatim on output
|
|
63
|
+
* (the labeler spreads `...trajectory`). `task`/`output` feed the fable judge
|
|
64
|
+
* and structural proxy; `testSpec` (when present) unlocks Tier 1.
|
|
65
|
+
*/
|
|
66
|
+
export interface Trajectory {
|
|
67
|
+
id: string;
|
|
68
|
+
task?: string;
|
|
69
|
+
output?: string;
|
|
70
|
+
testSpec?: TestSpec;
|
|
71
|
+
/** Hint for the structural verifier's task-kind detection. */
|
|
72
|
+
taskKind?: VerifyTaskKind;
|
|
73
|
+
[key: string]: unknown;
|
|
74
|
+
}
|
|
75
|
+
export interface LabelOptions {
|
|
76
|
+
/** SSH host for Tier-1 execution. Falls back to env RUFLO_DISTILL_REMOTE. Never hard-coded. */
|
|
77
|
+
remote?: string;
|
|
78
|
+
/** Run the REAL Tier-1 eval over SSH. Default false → dry-run preflight only. */
|
|
79
|
+
execute?: boolean;
|
|
80
|
+
/** Enable the Tier-2 Fable judge. Default false. Requires maxBudgetUsd to spend. */
|
|
81
|
+
fableJudge?: boolean;
|
|
82
|
+
/** Hard budget cap (USD) for the Fable tier. No cap ⇒ no Fable spend. */
|
|
83
|
+
maxBudgetUsd?: number;
|
|
84
|
+
/** Items per Fable call (default 20 — see FABLE_COST_MODEL). */
|
|
85
|
+
fableBatchSize?: number;
|
|
86
|
+
/** Structural verifier: min score to call a trajectory resolved (default: strict — confident only). */
|
|
87
|
+
minStructuralConfidence?: number;
|
|
88
|
+
/** Injected Tier-1 runner (tests). Defaults to a real SSH command runner. */
|
|
89
|
+
runner?: OracleRunner;
|
|
90
|
+
/** Injected Tier-2 harness (tests). Defaults to a real FableHarness. */
|
|
91
|
+
harness?: FableHarness;
|
|
92
|
+
}
|
|
93
|
+
/** A trajectory with its resolved label + honest provenance. */
|
|
94
|
+
export type LabeledTrajectory<T extends Trajectory = Trajectory> = T & {
|
|
95
|
+
resolved: boolean;
|
|
96
|
+
resolvedBy: ResolvedProvenance;
|
|
97
|
+
resolvedConfidence?: number;
|
|
98
|
+
resolvedReason?: string;
|
|
99
|
+
/** Present for trajectories that had a mechanical spec — the Tier-1 plan/outcome. */
|
|
100
|
+
oraclePreflight?: OraclePreflight;
|
|
101
|
+
};
|
|
102
|
+
export type OracleKind = 'ssh-darwin-bench' | 'ssh-swebench-eval' | 'ssh-eval' | 'local-eval';
|
|
103
|
+
/** The concrete command plan for executing a trajectory's eval on the remote. */
|
|
104
|
+
export interface OraclePlan {
|
|
105
|
+
kind: OracleKind;
|
|
106
|
+
/** Resolved remote host, or null when none is configured. */
|
|
107
|
+
remote: string | null;
|
|
108
|
+
/** Preflight probe commands (reachability, docker/darwin presence). */
|
|
109
|
+
probeCommands: string[];
|
|
110
|
+
/** The eval commands whose success ⇒ resolved. */
|
|
111
|
+
evalCommands: string[];
|
|
112
|
+
/** Human note describing how the resolved boolean is derived. */
|
|
113
|
+
parseHint: string;
|
|
114
|
+
}
|
|
115
|
+
/** Preflight/dry-run record attached to labeled trajectories that had a spec. */
|
|
116
|
+
export interface OraclePreflight {
|
|
117
|
+
/** True when nothing was executed (default path). */
|
|
118
|
+
dryRun: boolean;
|
|
119
|
+
plan: OraclePlan;
|
|
120
|
+
/** Probe outcome when executed (execute:true); absent in dry-run. */
|
|
121
|
+
probe?: {
|
|
122
|
+
ok: boolean;
|
|
123
|
+
reason: string;
|
|
124
|
+
};
|
|
125
|
+
/** Why Tier 1 did not produce the final label (e.g. dry-run, no remote, probe failed). */
|
|
126
|
+
fellThroughBecause?: string;
|
|
127
|
+
}
|
|
128
|
+
/** Outcome of a real Tier-1 eval. */
|
|
129
|
+
export interface OracleOutcome {
|
|
130
|
+
resolved: boolean;
|
|
131
|
+
reason: string;
|
|
132
|
+
}
|
|
133
|
+
/** Result of a shelled command. */
|
|
134
|
+
export interface CommandResult {
|
|
135
|
+
stdout: string;
|
|
136
|
+
stderr: string;
|
|
137
|
+
code: number | null;
|
|
138
|
+
}
|
|
139
|
+
/** Injectable command executor (tests mock this instead of touching SSH). */
|
|
140
|
+
export type CommandExec = (cmd: string, args: string[], opts: {
|
|
141
|
+
timeoutMs: number;
|
|
142
|
+
}) => Promise<CommandResult>;
|
|
143
|
+
/** Tier-1 runner contract: probe the remote, then run the eval. */
|
|
144
|
+
export interface OracleRunner {
|
|
145
|
+
preflight(plan: OraclePlan): Promise<{
|
|
146
|
+
ok: boolean;
|
|
147
|
+
reason: string;
|
|
148
|
+
}>;
|
|
149
|
+
runEval(plan: OraclePlan, spec: TestSpec): Promise<OracleOutcome>;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Label each trajectory with `resolved` + honest provenance, trying the tiers
|
|
153
|
+
* in order per trajectory. See the module header for tier semantics and the
|
|
154
|
+
* zero-spend default guarantee.
|
|
155
|
+
*/
|
|
156
|
+
export declare function labelResolved<T extends Trajectory>(trajectories: T[], opts?: LabelOptions): Promise<Array<LabeledTrajectory<T>>>;
|
|
157
|
+
/**
|
|
158
|
+
* Reflective failure analysis (GEPA/evolve mutation input). Thin, cost-
|
|
159
|
+
* disciplined pass-through to the Fable harness. Opt-in: returns [] unless a
|
|
160
|
+
* harness with a budget cap is provided/configured.
|
|
161
|
+
*/
|
|
162
|
+
export declare function reflectFailures(items: ReflectItem[], opts?: {
|
|
163
|
+
maxBudgetUsd?: number;
|
|
164
|
+
fableBatchSize?: number;
|
|
165
|
+
harness?: FableHarness;
|
|
166
|
+
}): Promise<ReflectResult[]>;
|
|
167
|
+
/** A trajectory can be mechanically evaluated iff it carries an actionable spec. */
|
|
168
|
+
export declare function hasMechanicalSpec(t: Trajectory): boolean;
|
|
169
|
+
/** Resolve the remote host from opts → env, never a hard-coded host. */
|
|
170
|
+
export declare function resolveRemote(remote?: string): string | null;
|
|
171
|
+
/**
|
|
172
|
+
* Build the concrete command plan for a spec. Pure — constructs command strings
|
|
173
|
+
* only; executes nothing. `remote` is always substituted from the parameter,
|
|
174
|
+
* never hard-coded.
|
|
175
|
+
*/
|
|
176
|
+
export declare function buildOraclePlan(spec: TestSpec, remote: string | null): OraclePlan;
|
|
177
|
+
/**
|
|
178
|
+
* Default command executor: shells out, piping any heredoc/stdin via argv only
|
|
179
|
+
* (commands are self-contained strings run through `sh -c`). Never invoked on
|
|
180
|
+
* the default (dry-run) path.
|
|
181
|
+
*/
|
|
182
|
+
export declare const defaultCommandExec: CommandExec;
|
|
183
|
+
/**
|
|
184
|
+
* Build a real SSH-backed oracle runner. `exec` is injectable so tests assert
|
|
185
|
+
* command construction without ever touching a network. Each plan command is
|
|
186
|
+
* run via `sh -c <command-string>` so the fully-rendered ssh line executes as-is.
|
|
187
|
+
*/
|
|
188
|
+
export declare function createSshOracleRunner(exec?: CommandExec, timeoutMs?: number): OracleRunner;
|
|
189
|
+
export default labelResolved;
|
|
190
|
+
//# sourceMappingURL=distill-oracle.d.ts.map
|