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.
- package/package.json +1 -1
- 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 +309 -1
- 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 +9 -82
- 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/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/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,213 @@
|
|
|
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
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
|
|
44
|
+
import { dirname, join, basename } from 'node:path';
|
|
45
|
+
import { getProjectCwd } from '../mcp-tools/types.js';
|
|
46
|
+
import { loadAgenticow, resolveMemoryPath, manifestFor, validateLabel, openWithLineage, } from '../mcp-tools/agenticow-loader.js';
|
|
47
|
+
/** Env var that hard-disables per-agent COW branching (operator kill switch). */
|
|
48
|
+
export const COW_KILL_SWITCH_ENV = 'CLAUDE_FLOW_NO_COW_MEMORY';
|
|
49
|
+
/** True unless the operator set the kill switch. */
|
|
50
|
+
export function cowMemoryEnabled() {
|
|
51
|
+
const v = process.env[COW_KILL_SWITCH_ENV];
|
|
52
|
+
return !(v === '1' || v === 'true' || v === 'yes');
|
|
53
|
+
}
|
|
54
|
+
const REGISTRY_REL = join('.claude-flow', 'swarm', 'cow-branches.json');
|
|
55
|
+
/**
|
|
56
|
+
* Manages the branch→base COW lifecycle for a swarm's agents. One instance per
|
|
57
|
+
* project cwd; state is persisted to `.claude-flow/swarm/cow-branches.json` so
|
|
58
|
+
* a branch forked in `branchForAgent` survives to a later `promoteAgent` /
|
|
59
|
+
* `discardAgent` call in a different process.
|
|
60
|
+
*/
|
|
61
|
+
export class SwarmMemoryBranches {
|
|
62
|
+
registryPath;
|
|
63
|
+
/**
|
|
64
|
+
* @param registryPath Override the registry location (tests point this at a
|
|
65
|
+
* temp dir). Defaults to `<cwd>/.claude-flow/swarm/cow-branches.json`.
|
|
66
|
+
*/
|
|
67
|
+
constructor(registryPath) {
|
|
68
|
+
this.registryPath = registryPath ?? join(getProjectCwd(), REGISTRY_REL);
|
|
69
|
+
}
|
|
70
|
+
// ---- registry persistence -------------------------------------------------
|
|
71
|
+
loadRegistry() {
|
|
72
|
+
try {
|
|
73
|
+
if (existsSync(this.registryPath)) {
|
|
74
|
+
const parsed = JSON.parse(readFileSync(this.registryPath, 'utf-8'));
|
|
75
|
+
if (parsed && typeof parsed === 'object' && parsed.branches)
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch { /* fall through to empty */ }
|
|
80
|
+
return { version: '1.0.0', branches: {} };
|
|
81
|
+
}
|
|
82
|
+
saveRegistry(reg) {
|
|
83
|
+
mkdirSync(dirname(this.registryPath), { recursive: true });
|
|
84
|
+
writeFileSync(this.registryPath, JSON.stringify(reg, null, 2), 'utf-8');
|
|
85
|
+
}
|
|
86
|
+
/** Look up the branch record for an agent, if one was forked. */
|
|
87
|
+
getBranch(agentId) {
|
|
88
|
+
return this.loadRegistry().branches[agentId];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Deterministic per-agent branch path: `<baseDir>/.swarm-cow/<base>.<agentId>.rvf`.
|
|
92
|
+
* Keeps branch files namespaced next to their base so cleanup is obvious.
|
|
93
|
+
*/
|
|
94
|
+
branchPathFor(basePath, agentId) {
|
|
95
|
+
const safeAgent = agentId.replace(/[^A-Za-z0-9_.\-]/g, '_');
|
|
96
|
+
const baseName = basename(basePath).replace(/\.rvf$/i, '');
|
|
97
|
+
return join(dirname(basePath), '.swarm-cow', `${baseName}.${safeAgent}.rvf`);
|
|
98
|
+
}
|
|
99
|
+
// ---- lifecycle ------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Fork a 162-byte COW branch off `base` for `agentId`. The agent then owns
|
|
102
|
+
* an isolated read/write view (parent ∪ its own edits). Idempotent: a second
|
|
103
|
+
* call for the same agent returns the existing branch record.
|
|
104
|
+
*
|
|
105
|
+
* @param base Path to the shared base `.rvf` (absolute or cwd-relative).
|
|
106
|
+
* @param agentId Agent that owns the branch (used as the COW label).
|
|
107
|
+
* @param opts.dimension Required only when `base` does not yet exist.
|
|
108
|
+
* @param opts.nativeAnn Fork with the native Rust ANN path (default true —
|
|
109
|
+
* agent branches are meant to be queried).
|
|
110
|
+
*/
|
|
111
|
+
async branchForAgent(base, agentId, opts = {}) {
|
|
112
|
+
if (!cowMemoryEnabled()) {
|
|
113
|
+
return { success: true, agentId, degraded: true, reason: 'cow-memory-disabled' };
|
|
114
|
+
}
|
|
115
|
+
const api = await loadAgenticow();
|
|
116
|
+
if (!api)
|
|
117
|
+
return { success: true, agentId, degraded: true, reason: 'agenticow-not-found' };
|
|
118
|
+
const label = validateLabel(agentId);
|
|
119
|
+
const basePath = resolveMemoryPath(base);
|
|
120
|
+
// Idempotent — a retried spawn must not double-fork.
|
|
121
|
+
const existing = this.getBranch(agentId);
|
|
122
|
+
if (existing && existsSync(existing.branchPath)) {
|
|
123
|
+
return {
|
|
124
|
+
success: true, agentId,
|
|
125
|
+
basePath: existing.basePath, branchPath: existing.branchPath, label: existing.label,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const branchPath = this.branchPathFor(basePath, agentId);
|
|
129
|
+
mkdirSync(dirname(branchPath), { recursive: true });
|
|
130
|
+
const nativeAnn = opts.nativeAnn !== false;
|
|
131
|
+
const baseMem = await openWithLineage(api, basePath, opts.dimension);
|
|
132
|
+
try {
|
|
133
|
+
const branch = await baseMem.fork(label, branchPath, { nativeAnn });
|
|
134
|
+
// Persist lineage manifests so the branch + base reopen with the COW
|
|
135
|
+
// chain intact — without this, the fork is in-memory only.
|
|
136
|
+
await branch.save?.(manifestFor(branchPath));
|
|
137
|
+
await baseMem.save?.(manifestFor(basePath));
|
|
138
|
+
await branch.close?.();
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
await baseMem.close?.();
|
|
142
|
+
}
|
|
143
|
+
const reg = this.loadRegistry();
|
|
144
|
+
reg.branches[agentId] = {
|
|
145
|
+
agentId, basePath, branchPath, label, createdAt: new Date().toISOString(),
|
|
146
|
+
};
|
|
147
|
+
this.saveRegistry(reg);
|
|
148
|
+
return { success: true, agentId, basePath, branchPath, label };
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Promote an agent's branch back into its base (merge its edits + tombstones
|
|
152
|
+
* atomically), then delete the branch file and drop it from the registry.
|
|
153
|
+
* Call on agent success. No-op (`promoted:false`) when the agent has no
|
|
154
|
+
* branch.
|
|
155
|
+
*/
|
|
156
|
+
async promoteAgent(agentId) {
|
|
157
|
+
const rec = this.getBranch(agentId);
|
|
158
|
+
if (!rec)
|
|
159
|
+
return { success: true, agentId, promoted: false, reason: 'no-branch' };
|
|
160
|
+
const api = await loadAgenticow();
|
|
161
|
+
if (!api)
|
|
162
|
+
return { success: true, agentId, promoted: false, degraded: true, reason: 'agenticow-not-found' };
|
|
163
|
+
if (existsSync(manifestFor(rec.branchPath)) || existsSync(rec.branchPath)) {
|
|
164
|
+
const branch = await openWithLineage(api, rec.branchPath);
|
|
165
|
+
const baseMem = await openWithLineage(api, rec.basePath);
|
|
166
|
+
try {
|
|
167
|
+
await branch.promote(baseMem);
|
|
168
|
+
await branch.save?.(manifestFor(rec.branchPath));
|
|
169
|
+
await baseMem.save?.(manifestFor(rec.basePath));
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
await branch.close?.();
|
|
173
|
+
await baseMem.close?.();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
this.removeBranchFiles(rec.branchPath);
|
|
177
|
+
this.forgetBranch(agentId);
|
|
178
|
+
return { success: true, agentId, promoted: true, branchPath: rec.branchPath, basePath: rec.basePath };
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Discard an agent's branch — delete the branch file + lineage manifest and
|
|
182
|
+
* drop the registry entry, WITHOUT merging anything into base. Call on agent
|
|
183
|
+
* failure. No-op (`discarded:false`) when the agent has no branch. Never
|
|
184
|
+
* touches agenticow (pure filesystem), so it works even in the degraded path.
|
|
185
|
+
*/
|
|
186
|
+
async discardAgent(agentId) {
|
|
187
|
+
const rec = this.getBranch(agentId);
|
|
188
|
+
if (!rec)
|
|
189
|
+
return { success: true, agentId, discarded: false, reason: 'no-branch' };
|
|
190
|
+
this.removeBranchFiles(rec.branchPath);
|
|
191
|
+
this.forgetBranch(agentId);
|
|
192
|
+
return { success: true, agentId, discarded: true, branchPath: rec.branchPath };
|
|
193
|
+
}
|
|
194
|
+
// ---- helpers --------------------------------------------------------------
|
|
195
|
+
removeBranchFiles(branchPath) {
|
|
196
|
+
try {
|
|
197
|
+
rmSync(branchPath, { recursive: true, force: true });
|
|
198
|
+
}
|
|
199
|
+
catch { /* best-effort */ }
|
|
200
|
+
try {
|
|
201
|
+
rmSync(manifestFor(branchPath), { force: true });
|
|
202
|
+
}
|
|
203
|
+
catch { /* best-effort */ }
|
|
204
|
+
}
|
|
205
|
+
forgetBranch(agentId) {
|
|
206
|
+
const reg = this.loadRegistry();
|
|
207
|
+
if (reg.branches[agentId]) {
|
|
208
|
+
delete reg.branches[agentId];
|
|
209
|
+
this.saveRegistry(reg);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=swarm-memory-branches.js.map
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weight-eft.ts — Optional-dependency service wrapper around
|
|
3
|
+
* `@metaharness/weight-eft` (agenticow / ADR-150 weight-eft slice).
|
|
4
|
+
*
|
|
5
|
+
* WHAT THIS SHIPS (and what it deliberately does NOT)
|
|
6
|
+
* ---------------------------------------------------
|
|
7
|
+
* This service turns ruflo's captured run transcripts into AUDITED TRAINING
|
|
8
|
+
* DATA + a COST-PARETO measurement + a GPU TRAINING PLAN. It does NOT train a
|
|
9
|
+
* model and it does NOT "reduce escalation":
|
|
10
|
+
* - `runExport` → SFT (OpenAI chat) + DPO (TRL preference) JSONL + a guard
|
|
11
|
+
* report (contamination / reward-hack / long-context). $0.
|
|
12
|
+
* - `runPlan` → the GPU training plan + the exact `ruvllm microlora`
|
|
13
|
+
* command a GPU host would run. $0 dry-run — never spawns.
|
|
14
|
+
* - `runEval` → the cost-Pareto delta folded from two CascadeOutcome[]. $0.
|
|
15
|
+
* - `runRemoteTrain` → an SSH-based remote-GPU invocation. DRY-RUN by default
|
|
16
|
+
* (prints commands + read-only preflight); real compute only
|
|
17
|
+
* behind explicit `execute && yes`. This is the ONLY path
|
|
18
|
+
* that can spend, and it spends on the USER's host, not $0.
|
|
19
|
+
*
|
|
20
|
+
* HARD HONESTY RULE (do not overclaim): weight-eft's own `train` never spawns
|
|
21
|
+
* (its README §Status), and no GPU tune has run here. `resolved` in the
|
|
22
|
+
* captured archive is a PROXY (ruflo has no SWE-bench gold oracle) — the SFT
|
|
23
|
+
* data-quality caveat stands. Nothing here claims ruflo "trains a model" as a
|
|
24
|
+
* $0/local capability.
|
|
25
|
+
*
|
|
26
|
+
* ADR-150 GRACEFUL DEGRADATION
|
|
27
|
+
* ----------------------------
|
|
28
|
+
* `@metaharness/weight-eft` is an OPTIONAL dependency. Every entry point loads
|
|
29
|
+
* it via a dynamic import through `loadWeightEft()` and returns a structured
|
|
30
|
+
* `{ degraded: true }` result when it is absent — never a throw. There is NO
|
|
31
|
+
* static `import … from '@metaharness/weight-eft'` anywhere in this file; the
|
|
32
|
+
* types below are LOCAL mirrors so tsc stays green with the package removed.
|
|
33
|
+
*
|
|
34
|
+
* @module services/weight-eft
|
|
35
|
+
*/
|
|
36
|
+
import type { RunTranscriptRecord } from '../ruvector/run-transcript-recorder.js';
|
|
37
|
+
export interface WeftToolCall {
|
|
38
|
+
id: string;
|
|
39
|
+
type: 'function';
|
|
40
|
+
function: {
|
|
41
|
+
name: string;
|
|
42
|
+
arguments: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface WeftChatMessage {
|
|
46
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
47
|
+
content: string | null;
|
|
48
|
+
tool_calls?: WeftToolCall[];
|
|
49
|
+
tool_call_id?: string;
|
|
50
|
+
name?: string;
|
|
51
|
+
}
|
|
52
|
+
export type WeftPolicyTier = 'cheap' | 'frontier';
|
|
53
|
+
/** Input contract for the exporter. Mirrors weight-eft's DarwinTrajectory. */
|
|
54
|
+
export interface DarwinTrajectory {
|
|
55
|
+
instance_id: string;
|
|
56
|
+
model: string;
|
|
57
|
+
tier: WeftPolicyTier;
|
|
58
|
+
resolved: boolean;
|
|
59
|
+
messages: WeftChatMessage[];
|
|
60
|
+
model_patch: string;
|
|
61
|
+
sample?: number;
|
|
62
|
+
source?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface WeftExportReport {
|
|
65
|
+
totalTrajectories: number;
|
|
66
|
+
excludedByHoldout: number;
|
|
67
|
+
droppedOverLength: number;
|
|
68
|
+
truncatedOverLength: number;
|
|
69
|
+
droppedRewardHacked: number;
|
|
70
|
+
sftRows: number;
|
|
71
|
+
dpoRows: number;
|
|
72
|
+
sftInstanceIds: string[];
|
|
73
|
+
dpoInstanceIds: string[];
|
|
74
|
+
notes: string[];
|
|
75
|
+
}
|
|
76
|
+
export interface WeftCascadeOutcome {
|
|
77
|
+
instance_id: string;
|
|
78
|
+
cheapResolved: boolean;
|
|
79
|
+
escalated: boolean;
|
|
80
|
+
resolved: boolean;
|
|
81
|
+
costUsd: number;
|
|
82
|
+
}
|
|
83
|
+
export interface WeftCostParetoDelta {
|
|
84
|
+
base: unknown;
|
|
85
|
+
adapter: unknown;
|
|
86
|
+
cheapResolveLift: number;
|
|
87
|
+
escalationRateReduction: number;
|
|
88
|
+
costPerResolvedReduction: number;
|
|
89
|
+
resolveRateDelta: number;
|
|
90
|
+
verdict: string;
|
|
91
|
+
}
|
|
92
|
+
export interface WeftBaseModelSpec {
|
|
93
|
+
id: string;
|
|
94
|
+
paramsB: number;
|
|
95
|
+
}
|
|
96
|
+
export interface WeftTrainingPlan {
|
|
97
|
+
config: unknown;
|
|
98
|
+
command: string;
|
|
99
|
+
summary: string;
|
|
100
|
+
}
|
|
101
|
+
/** The subset of the weight-eft module surface this service calls. */
|
|
102
|
+
interface WeightEftApi {
|
|
103
|
+
exportTrainingData: (t: DarwinTrajectory[], o: {
|
|
104
|
+
evalHoldout: string[];
|
|
105
|
+
maxTokens?: number;
|
|
106
|
+
truncateOverLength?: boolean;
|
|
107
|
+
dropRewardHacked?: boolean;
|
|
108
|
+
}) => {
|
|
109
|
+
sft: {
|
|
110
|
+
messages: WeftChatMessage[];
|
|
111
|
+
}[];
|
|
112
|
+
dpo: unknown[];
|
|
113
|
+
report: WeftExportReport;
|
|
114
|
+
};
|
|
115
|
+
sftToJsonl: (rows: {
|
|
116
|
+
messages: WeftChatMessage[];
|
|
117
|
+
}[]) => string;
|
|
118
|
+
dpoToJsonl: (rows: unknown[]) => string;
|
|
119
|
+
costParetoDelta: (base: WeftCascadeOutcome[], adapter: WeftCascadeOutcome[]) => WeftCostParetoDelta;
|
|
120
|
+
twoStagePlan: (base: WeftBaseModelSpec, sftPath: string, dpoPath: string, adapterPrefix: string) => {
|
|
121
|
+
sft: WeftTrainingPlan;
|
|
122
|
+
dpo: WeftTrainingPlan;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Injectable importer so tests can force the degraded path deterministically. */
|
|
126
|
+
export type WeightEftImporter = () => Promise<unknown>;
|
|
127
|
+
/**
|
|
128
|
+
* Load `@metaharness/weight-eft` (optional dep). Returns the module or `null`
|
|
129
|
+
* when it is absent/broken. Never throws. The specifier is held in a variable
|
|
130
|
+
* so tsc does not statically resolve the package (keeps the build green when
|
|
131
|
+
* the optional dep is removed — ADR-150).
|
|
132
|
+
*/
|
|
133
|
+
export declare function loadWeightEft(importer?: WeightEftImporter): Promise<WeightEftApi | null>;
|
|
134
|
+
export interface ArchiveBuildStats {
|
|
135
|
+
total: number;
|
|
136
|
+
resolved: number;
|
|
137
|
+
byTier: Record<WeftPolicyTier, number>;
|
|
138
|
+
/** Honest breakdown: how many `resolved` booleans came from which proxy. */
|
|
139
|
+
byResolvedSource: Record<string, number>;
|
|
140
|
+
skipped: number;
|
|
141
|
+
}
|
|
142
|
+
export interface ArchiveBuildResult {
|
|
143
|
+
trajectories: DarwinTrajectory[];
|
|
144
|
+
stats: ArchiveBuildStats;
|
|
145
|
+
/** Load-bearing honesty banner surfaced to CLI/report output. */
|
|
146
|
+
proxyNote: string;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Map captured ruflo run transcripts to the DarwinTrajectory[] contract the
|
|
150
|
+
* weight-eft exporter codes against. PURE + synchronous — this is the seam the
|
|
151
|
+
* archive-builder unit test exercises.
|
|
152
|
+
*
|
|
153
|
+
* A record is skipped (not thrown) when it lacks the minimum an exporter needs:
|
|
154
|
+
* an instance_id and a non-empty messages array. `resolved` is copied verbatim
|
|
155
|
+
* and its proxy provenance is tallied so the caller can print an honest note.
|
|
156
|
+
*/
|
|
157
|
+
export declare function buildArchiveFromRecords(records: RunTranscriptRecord[]): ArchiveBuildResult;
|
|
158
|
+
export type ExportOutcome = {
|
|
159
|
+
degraded: false;
|
|
160
|
+
report: WeftExportReport;
|
|
161
|
+
sftJsonl: string;
|
|
162
|
+
dpoJsonl: string;
|
|
163
|
+
sftRows: number;
|
|
164
|
+
dpoRows: number;
|
|
165
|
+
} | {
|
|
166
|
+
degraded: true;
|
|
167
|
+
reason: string;
|
|
168
|
+
};
|
|
169
|
+
/** Run the weight-eft exporter over a DarwinTrajectory[] archive. $0. */
|
|
170
|
+
export declare function runExport(opts: {
|
|
171
|
+
archive: DarwinTrajectory[];
|
|
172
|
+
evalHoldout?: string[];
|
|
173
|
+
maxTokens?: number;
|
|
174
|
+
truncateOverLength?: boolean;
|
|
175
|
+
dropRewardHacked?: boolean;
|
|
176
|
+
importer?: WeightEftImporter;
|
|
177
|
+
}): Promise<ExportOutcome>;
|
|
178
|
+
export type PlanOutcome = {
|
|
179
|
+
degraded: false;
|
|
180
|
+
base: WeftBaseModelSpec;
|
|
181
|
+
sft: WeftTrainingPlan;
|
|
182
|
+
dpo: WeftTrainingPlan;
|
|
183
|
+
} | {
|
|
184
|
+
degraded: true;
|
|
185
|
+
reason: string;
|
|
186
|
+
};
|
|
187
|
+
/** Emit the two-stage (SFT → on-policy DPO) GPU training plan. $0 dry-run. */
|
|
188
|
+
export declare function runPlan(opts: {
|
|
189
|
+
base?: WeftBaseModelSpec;
|
|
190
|
+
sftPath: string;
|
|
191
|
+
dpoPath: string;
|
|
192
|
+
adapterPrefix?: string;
|
|
193
|
+
importer?: WeightEftImporter;
|
|
194
|
+
}): Promise<PlanOutcome>;
|
|
195
|
+
export type EvalOutcome = {
|
|
196
|
+
degraded: false;
|
|
197
|
+
delta: WeftCostParetoDelta;
|
|
198
|
+
} | {
|
|
199
|
+
degraded: true;
|
|
200
|
+
reason: string;
|
|
201
|
+
};
|
|
202
|
+
/** Fold base + adapter CascadeOutcome[] into the cost-Pareto delta. $0. */
|
|
203
|
+
export declare function runEval(opts: {
|
|
204
|
+
baseOutcomes: WeftCascadeOutcome[];
|
|
205
|
+
adapterOutcomes: WeftCascadeOutcome[];
|
|
206
|
+
importer?: WeightEftImporter;
|
|
207
|
+
}): Promise<EvalOutcome>;
|
|
208
|
+
/** Default cheap-tier tune target — a 7B coder in the tunable [1,14]B band. */
|
|
209
|
+
export declare const DEFAULT_BASE_MODEL: WeftBaseModelSpec;
|
|
210
|
+
export interface RemoteTrainArgs {
|
|
211
|
+
/** SSH host or tailscale name (parameterized; never hard-coded). */
|
|
212
|
+
host: string;
|
|
213
|
+
/** Base model id to tune. Default DEFAULT_BASE_MODEL.id. */
|
|
214
|
+
base?: string;
|
|
215
|
+
/** Local path to the exported SFT jsonl. */
|
|
216
|
+
sftPath: string;
|
|
217
|
+
/** Local path to the exported DPO jsonl. */
|
|
218
|
+
dpoPath: string;
|
|
219
|
+
/** Local dir to fetch the trained LoRA adapter back into. Default .claude-flow/neural. */
|
|
220
|
+
adapterDir?: string;
|
|
221
|
+
/** Remote working dir. Default ~/.ruflo-weft/<runId>. */
|
|
222
|
+
remoteWorkdir?: string;
|
|
223
|
+
/** SSH user (default: current, i.e. no user@ prefix). */
|
|
224
|
+
sshUser?: string;
|
|
225
|
+
/** SSH port. Default 22. */
|
|
226
|
+
sshPort?: number;
|
|
227
|
+
/** Stable run id used in remote workdir + adapter names. Default derived. */
|
|
228
|
+
runId?: string;
|
|
229
|
+
/** Adapter name prefix. Default 'ruflo-weft'. */
|
|
230
|
+
adapterPrefix?: string;
|
|
231
|
+
}
|
|
232
|
+
export interface RemoteStep {
|
|
233
|
+
label: string;
|
|
234
|
+
argv: string[];
|
|
235
|
+
}
|
|
236
|
+
export interface RemoteTrainPlan {
|
|
237
|
+
host: string;
|
|
238
|
+
base: string;
|
|
239
|
+
runId: string;
|
|
240
|
+
remoteWorkdir: string;
|
|
241
|
+
adapterDir: string;
|
|
242
|
+
sftAdapter: string;
|
|
243
|
+
dpoAdapter: string;
|
|
244
|
+
/** Read-only reachability/capability probes (safe to run in dry-run). */
|
|
245
|
+
preflight: RemoteStep[];
|
|
246
|
+
/** The mutating steps: rsync data up, train, fetch adapter back. */
|
|
247
|
+
steps: RemoteStep[];
|
|
248
|
+
/** Rendered one-line commands for human display. */
|
|
249
|
+
humanCommands: string[];
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Construct the exact ssh/rsync/ruvllm invocations a remote GPU tune would run.
|
|
253
|
+
* PURE — no spawning, no filesystem, deterministic. This is the seam the
|
|
254
|
+
* command-construction unit test exercises. The ruvllm commands mirror the
|
|
255
|
+
* canonical `ruvllm microlora sft … && ruvllm microlora dpo --init-from …`
|
|
256
|
+
* plan weight-eft emits, wrapped for SSH execution on the remote host.
|
|
257
|
+
*/
|
|
258
|
+
export declare function buildRemoteTrainInvocation(args: RemoteTrainArgs): RemoteTrainPlan;
|
|
259
|
+
/** Minimal spawn result shape (subset of child_process.SpawnSyncReturns). */
|
|
260
|
+
export interface SpawnLike {
|
|
261
|
+
status: number | null;
|
|
262
|
+
stdout?: string;
|
|
263
|
+
stderr?: string;
|
|
264
|
+
error?: Error;
|
|
265
|
+
}
|
|
266
|
+
export type SpawnFn = (cmd: string, argv: string[]) => SpawnLike;
|
|
267
|
+
export type RemoteTrainOutcome = {
|
|
268
|
+
degraded: true;
|
|
269
|
+
reason: string;
|
|
270
|
+
} | {
|
|
271
|
+
degraded: false;
|
|
272
|
+
mode: 'dry-run' | 'refused' | 'preflight-failed' | 'executed';
|
|
273
|
+
plan: RemoteTrainPlan;
|
|
274
|
+
preflight?: {
|
|
275
|
+
label: string;
|
|
276
|
+
ok: boolean;
|
|
277
|
+
detail: string;
|
|
278
|
+
}[];
|
|
279
|
+
steps?: {
|
|
280
|
+
label: string;
|
|
281
|
+
ok: boolean;
|
|
282
|
+
detail: string;
|
|
283
|
+
}[];
|
|
284
|
+
reason?: string;
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* Run (or, by default, DRY-RUN) a remote-GPU tune. SAFETY MODEL:
|
|
288
|
+
* - DEFAULT (no execute): builds the plan, runs read-only preflight probes,
|
|
289
|
+
* and returns the commands that WOULD run. NO rsync of data, NO training.
|
|
290
|
+
* - execute && !yes → 'refused' (real GPU spend needs an explicit second gate).
|
|
291
|
+
* - execute && yes → runs preflight; if unreachable → 'preflight-failed'
|
|
292
|
+
* WITHOUT training; else runs rsync + ssh ruvllm sft/dpo + fetch-back.
|
|
293
|
+
* - No `ssh` binary / host unreachable / any spawn error → structured
|
|
294
|
+
* degraded/preflight-failed. NEVER throws.
|
|
295
|
+
*
|
|
296
|
+
* `spawn` is injected for testing so CI never touches a live host.
|
|
297
|
+
*/
|
|
298
|
+
export declare function runRemoteTrain(args: RemoteTrainArgs & {
|
|
299
|
+
execute?: boolean;
|
|
300
|
+
yes?: boolean;
|
|
301
|
+
preflight?: boolean;
|
|
302
|
+
spawn?: SpawnFn;
|
|
303
|
+
}): Promise<RemoteTrainOutcome>;
|
|
304
|
+
export {};
|
|
305
|
+
//# sourceMappingURL=weight-eft.d.ts.map
|