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.
Files changed (36) hide show
  1. package/package.json +2 -2
  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 +366 -12
  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 +186 -79
  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/index.d.ts +1 -1
  17. package/v3/@claude-flow/cli/dist/src/ruvector/index.js +1 -1
  18. package/v3/@claude-flow/cli/dist/src/ruvector/lora-adapter.d.ts +35 -0
  19. package/v3/@claude-flow/cli/dist/src/ruvector/lora-adapter.js +108 -1
  20. package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.d.ts +33 -0
  21. package/v3/@claude-flow/cli/dist/src/ruvector/router-trajectory.js +31 -0
  22. package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.d.ts +154 -0
  23. package/v3/@claude-flow/cli/dist/src/ruvector/run-transcript-recorder.js +209 -0
  24. package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.d.ts +140 -0
  25. package/v3/@claude-flow/cli/dist/src/services/checkpoint-gate.js +223 -0
  26. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.d.ts +190 -0
  27. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.js +349 -0
  28. package/v3/@claude-flow/cli/dist/src/services/fable-harness.d.ts +168 -0
  29. package/v3/@claude-flow/cli/dist/src/services/fable-harness.js +347 -0
  30. package/v3/@claude-flow/cli/dist/src/services/native-training.d.ts +28 -0
  31. package/v3/@claude-flow/cli/dist/src/services/native-training.js +62 -5
  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,105 @@
1
+ /**
2
+ * Agenticow shared loader + helpers.
3
+ *
4
+ * Extracted from `agenticow-tools.ts` so both the MCP tool surface AND the
5
+ * `SwarmMemoryBranches` service (src/services/swarm-memory-branches.ts) share
6
+ * one loader, one degradation contract, and one set of path/lineage helpers —
7
+ * rather than duplicating the optional-dep dance in two places.
8
+ *
9
+ * Architectural constraint (ADR-150, mirrors metaharness-tools.ts):
10
+ * - `agenticow` lives in `optionalDependencies` — never a hard runtime dep.
11
+ * - When the package is missing, callers get `null` from `loadAgenticow()`
12
+ * (or a `{degraded:true}` result via `degradedResult`) and MUST fall back
13
+ * gracefully — never throw a MODULE_NOT_FOUND.
14
+ *
15
+ * @module @claude-flow/cli/mcp-tools/agenticow-loader
16
+ */
17
+ import { existsSync } from 'node:fs';
18
+ import { resolve, isAbsolute } from 'node:path';
19
+ import { getProjectCwd } from './types.js';
20
+ export const PACKAGE_NAME = 'agenticow';
21
+ // Cache: module load is expensive enough to amortize across handler calls.
22
+ // null = not yet attempted; false = attempted and unavailable; module = loaded.
23
+ let _agenticowMod = null;
24
+ let _loadAttempted = false;
25
+ /**
26
+ * Lazily import `agenticow`. Returns the module on success, or `null` when the
27
+ * package is not installed (the optional-dep degraded path). Any OTHER load
28
+ * error (e.g. a corrupt install) is re-thrown so it is not silently masked.
29
+ */
30
+ export async function loadAgenticow() {
31
+ if (_loadAttempted)
32
+ return _agenticowMod || null;
33
+ _loadAttempted = true;
34
+ try {
35
+ _agenticowMod = await import(PACKAGE_NAME);
36
+ return _agenticowMod;
37
+ }
38
+ catch (err) {
39
+ if (err && (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND' ||
40
+ /Cannot find (module|package)/i.test(String(err.message)))) {
41
+ _agenticowMod = false;
42
+ return null;
43
+ }
44
+ throw err;
45
+ }
46
+ }
47
+ /** Reset the module-level load cache. Test-only seam. */
48
+ export function __resetAgenticowCache() {
49
+ _agenticowMod = null;
50
+ _loadAttempted = false;
51
+ }
52
+ export function degradedResult(reason) {
53
+ return { success: true, degraded: true, reason };
54
+ }
55
+ /**
56
+ * Resolve a user-supplied memory path against the project cwd, rejecting path
57
+ * traversal and NUL bytes (D-2 style hardening — same rule the MCP verbs use).
58
+ */
59
+ export function resolveMemoryPath(path) {
60
+ if (!path || typeof path !== 'string')
61
+ throw new Error('memory path is required');
62
+ if (/\.\.[\\/]|\0/.test(path))
63
+ throw new Error('memory path contains disallowed characters');
64
+ return isAbsolute(path) ? path : resolve(getProjectCwd(), path);
65
+ }
66
+ /**
67
+ * Lineage manifest companion path. agenticow persists the COW chain
68
+ * (working → checkpoints → base) into `<file>.agenticow.json` next to the
69
+ * `.rvf` data file. Without it, forks/checkpoints are in-memory only and
70
+ * disappear when the AgenticMemory handle closes.
71
+ */
72
+ export function manifestFor(file) {
73
+ return `${file}.agenticow.json`;
74
+ }
75
+ /** Validate a COW branch/checkpoint label (alnum + a small safe symbol set). */
76
+ export function validateLabel(label) {
77
+ if (!label || typeof label !== 'string')
78
+ throw new Error('label is required');
79
+ if (label.length > 256)
80
+ throw new Error('label exceeds 256 chars');
81
+ if (!/^[A-Za-z0-9_.\-:/@]+$/.test(label)) {
82
+ throw new Error('label may only contain [A-Za-z0-9_.\\-:/@]');
83
+ }
84
+ return label;
85
+ }
86
+ /**
87
+ * Open (or create) a memory file, restoring its COW chain from the lineage
88
+ * manifest when one exists. When neither the `.rvf` nor the manifest exists,
89
+ * `dimension` is required to create a fresh base.
90
+ */
91
+ export async function openWithLineage(api, file, dimension) {
92
+ const manifest = manifestFor(file);
93
+ if (existsSync(manifest)) {
94
+ return api.AgenticMemory.load(manifest);
95
+ }
96
+ const opts = {};
97
+ if (typeof dimension === 'number' && Number.isInteger(dimension) && dimension > 0) {
98
+ opts.dimension = dimension;
99
+ }
100
+ else if (!existsSync(file)) {
101
+ throw new Error('dimension is required when creating a new memory file');
102
+ }
103
+ return api.open(file, opts);
104
+ }
105
+ //# sourceMappingURL=agenticow-loader.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * agenticow speculate — MCP surface for speculative branch-and-promote
3
+ * (agenticow step 4).
4
+ *
5
+ * Exposes the `SpeculativeExploration` module as a single MCP tool
6
+ * `agenticow_speculate` so agents can drive parallel A/B memory exploration:
7
+ * fan out N candidate ingest-sets, each on its own 162-byte COW branch of a
8
+ * shared base `.rvf`, score them by a probe query, PROMOTE the winner into
9
+ * base, and DISCARD the losers (delete their branch files).
10
+ *
11
+ * Because MCP inputs are JSON (not JS functions), candidates are DECLARATIVE:
12
+ * each candidate is a label + a set of vectors to ingest, and scoring is a
13
+ * probe query (`nearest`) or an ingest count (`count`). The generic
14
+ * `explore(base, {label, fn}[], score)` core does the real work.
15
+ *
16
+ * Optional-dep contract (mirrors agenticow-tools.ts): when `agenticow` is not
17
+ * installed the tool returns `{success:true, degraded:true,
18
+ * reason:'agenticow-not-found'}` and never throws for that reason.
19
+ *
20
+ * @module @claude-flow/cli/mcp-tools/agenticow-speculate
21
+ */
22
+ import type { MCPTool } from './types.js';
23
+ export declare const agenticowSpeculateTools: MCPTool[];
24
+ //# sourceMappingURL=agenticow-speculate-tools.d.ts.map
@@ -0,0 +1,202 @@
1
+ /**
2
+ * agenticow speculate — MCP surface for speculative branch-and-promote
3
+ * (agenticow step 4).
4
+ *
5
+ * Exposes the `SpeculativeExploration` module as a single MCP tool
6
+ * `agenticow_speculate` so agents can drive parallel A/B memory exploration:
7
+ * fan out N candidate ingest-sets, each on its own 162-byte COW branch of a
8
+ * shared base `.rvf`, score them by a probe query, PROMOTE the winner into
9
+ * base, and DISCARD the losers (delete their branch files).
10
+ *
11
+ * Because MCP inputs are JSON (not JS functions), candidates are DECLARATIVE:
12
+ * each candidate is a label + a set of vectors to ingest, and scoring is a
13
+ * probe query (`nearest`) or an ingest count (`count`). The generic
14
+ * `explore(base, {label, fn}[], score)` core does the real work.
15
+ *
16
+ * Optional-dep contract (mirrors agenticow-tools.ts): when `agenticow` is not
17
+ * installed the tool returns `{success:true, degraded:true,
18
+ * reason:'agenticow-not-found'}` and never throws for that reason.
19
+ *
20
+ * @module @claude-flow/cli/mcp-tools/agenticow-speculate
21
+ */
22
+ import { loadAgenticow, degradedResult, resolveMemoryPath, openWithLineage, manifestFor, validateLabel, } from './agenticow-loader.js';
23
+ import { explore, } from '../agenticow/speculative-exploration.js';
24
+ /** Turn a validated label into a filesystem-safe branch filename segment. */
25
+ function safeSegment(label) {
26
+ return label.replace(/[^A-Za-z0-9_-]+/g, '_').slice(0, 64) || 'branch';
27
+ }
28
+ export const agenticowSpeculateTools = [
29
+ {
30
+ name: 'agenticow_speculate',
31
+ description: "agenticow — speculative branch-and-promote for parallel A/B memory exploration. " +
32
+ "Forks a 162-byte COW branch per candidate off a shared base .rvf, ingests each candidate's " +
33
+ "vectors into its OWN isolated branch, scores every branch (probe-query 'nearest' distance or " +
34
+ "ingest 'count'), PROMOTES the winning branch's edits into base, and DISCARDS the losers by " +
35
+ "deleting their branch files. Use when you want to try N competing memory-write strategies and " +
36
+ "keep only the best — the memory-state analogue of worktree-per-agent code exploration. " +
37
+ "Copying the base per candidate is wrong (full-copy snapshots grow linearly, the 3.3 GB Darwin " +
38
+ "bloat); COW forks are constant-size and losers cost ~162 B to throw away. Optional dep — " +
39
+ "degrades to {degraded:true} when agenticow is missing.",
40
+ category: 'memory',
41
+ tags: ['agenticow', 'memory', 'cow', 'speculative', 'branch', 'promote', 'ab'],
42
+ inputSchema: {
43
+ type: 'object',
44
+ properties: {
45
+ basePath: {
46
+ type: 'string',
47
+ description: 'Path to base .rvf memory file (absolute or relative to cwd)',
48
+ },
49
+ dimension: {
50
+ type: 'integer',
51
+ description: 'Vector dimension (required only when basePath does not exist yet)',
52
+ },
53
+ candidates: {
54
+ type: 'array',
55
+ description: 'The A/B candidates. Each explores its own COW branch.',
56
+ items: {
57
+ type: 'object',
58
+ properties: {
59
+ label: { type: 'string', description: 'Branch label (alnum + _.-:/@ only)' },
60
+ ingest: {
61
+ type: 'array',
62
+ description: 'Vectors to ingest into this candidate branch',
63
+ items: {
64
+ type: 'object',
65
+ properties: {
66
+ id: { type: 'integer', description: 'Explicit vector id (auto when omitted)' },
67
+ vector: { type: 'array', items: { type: 'number' }, description: 'Dense vector' },
68
+ text: { type: 'string', description: 'Optional payload text' },
69
+ },
70
+ required: ['vector'],
71
+ },
72
+ },
73
+ branchPath: {
74
+ type: 'string',
75
+ description: 'Optional explicit path for this branch file (default: alongside base)',
76
+ },
77
+ },
78
+ required: ['label', 'ingest'],
79
+ },
80
+ },
81
+ probe: {
82
+ type: 'array',
83
+ items: { type: 'number' },
84
+ description: "Probe vector for scoreBy='nearest' (scores each branch by best-hit similarity)",
85
+ },
86
+ k: { type: 'integer', description: 'Nearest-neighbours to fetch per probe (default 1)' },
87
+ scoreBy: {
88
+ type: 'string',
89
+ enum: ['nearest', 'count'],
90
+ description: "'nearest' = closest probe distance wins; 'count' = most accepted ingests wins. Default 'nearest'.",
91
+ },
92
+ requireClearance: {
93
+ type: 'boolean',
94
+ description: 'ADR-171 fail-closed gate. When true, the top-scored winner is NOT promoted (base stays unchanged) and a provenance-tagged receipt is emitted — score alone cannot graduate work. Use when speculating over TASK outcomes rather than pure memory A/B. Default false (score-only promotion, tagged `unverified`).',
95
+ default: false,
96
+ },
97
+ },
98
+ required: ['basePath', 'candidates'],
99
+ },
100
+ handler: async (input) => {
101
+ const api = await loadAgenticow();
102
+ if (!api)
103
+ return degradedResult('agenticow-not-found');
104
+ const basePath = resolveMemoryPath(String(input.basePath));
105
+ const dimension = input.dimension;
106
+ const scoreBy = input.scoreBy === 'count' ? 'count' : 'nearest';
107
+ const k = Number.isInteger(input.k) && input.k > 0 ? input.k : 1;
108
+ const probe = Array.isArray(input.probe) ? input.probe : null;
109
+ const rawCandidates = input.candidates;
110
+ if (!Array.isArray(rawCandidates) || rawCandidates.length === 0) {
111
+ throw new Error('at least one candidate is required');
112
+ }
113
+ if (scoreBy === 'nearest' && !probe) {
114
+ throw new Error("scoreBy='nearest' requires a probe vector");
115
+ }
116
+ // Build the generic {label, fn} candidates. Each fn ingests into its own
117
+ // branch handle, then (for 'nearest') probes it so we can score.
118
+ const candidates = rawCandidates.map((c) => {
119
+ const label = validateLabel(String(c.label));
120
+ if (!Array.isArray(c.ingest) || c.ingest.length === 0) {
121
+ throw new Error(`candidate ${label} must ingest at least one vector`);
122
+ }
123
+ const records = c.ingest.map((r) => ({
124
+ ...(Number.isInteger(r.id) ? { id: r.id } : {}),
125
+ vector: r.vector,
126
+ ...(typeof r.text === 'string' ? { text: r.text } : {}),
127
+ }));
128
+ return {
129
+ label,
130
+ fn: (branch) => {
131
+ const res = branch.ingest(records);
132
+ const accepted = Number(res?.accepted ?? records.length);
133
+ let hits = [];
134
+ if (probe) {
135
+ hits = (branch.query(probe, k) || []).map((h) => ({
136
+ id: h.id,
137
+ distance: h.distance,
138
+ }));
139
+ }
140
+ const bestDistance = hits.length ? hits[0].distance : null;
141
+ return { accepted, bestDistance, hits };
142
+ },
143
+ };
144
+ });
145
+ const score = (r) => {
146
+ if (scoreBy === 'count')
147
+ return r.accepted;
148
+ // nearest: smaller distance = better. Map to a higher-is-better score.
149
+ if (r.bestDistance === null)
150
+ return -Infinity;
151
+ return 1 / (1 + Math.max(0, r.bestDistance));
152
+ };
153
+ const branchPath = (label) => {
154
+ const explicit = rawCandidates.find((c) => validateLabel(String(c.label)) === label)?.branchPath;
155
+ if (explicit)
156
+ return resolveMemoryPath(String(explicit));
157
+ return `${basePath}.spec-${safeSegment(label)}.rvf`;
158
+ };
159
+ // ADR-171 promotion gate. For pure memory A/B the `score` IS the chosen
160
+ // metric (not a proxy for task correctness), so score-only promotion is
161
+ // legitimate and stays the default (tagged `unverified`, never
162
+ // masquerading as ground truth). Callers grafting this onto TASK work
163
+ // pass requireClearance:true to fail-closed — the winner is then
164
+ // ineligible unless a real clearance mechanism graduates it.
165
+ const requireClearance = input.requireClearance === true;
166
+ const exploreOpts = { branchPath, persist: true };
167
+ if (requireClearance)
168
+ exploreOpts.requireClearance = true;
169
+ // Open base, run the speculative exploration, persist base with the winner promoted.
170
+ const base = await openWithLineage(api, basePath, dimension);
171
+ try {
172
+ const result = await explore(base, candidates, score, exploreOpts);
173
+ base.save?.(manifestFor(basePath));
174
+ return {
175
+ success: true,
176
+ basePath,
177
+ scoreBy,
178
+ winner: result.winner,
179
+ scores: result.scores,
180
+ promoted: result.promoted,
181
+ promotedBy: result.promotedBy,
182
+ promotionDecision: result.promotionDecision,
183
+ promoteStats: result.promoteStats,
184
+ discarded: result.discarded,
185
+ receipts: result.receipts,
186
+ branches: result.branches.map((b) => ({
187
+ label: b.label,
188
+ path: b.path,
189
+ score: b.score,
190
+ kept: b.kept,
191
+ accepted: b.result.accepted,
192
+ bestDistance: b.result.bestDistance,
193
+ })),
194
+ };
195
+ }
196
+ finally {
197
+ await base.close?.();
198
+ }
199
+ },
200
+ },
201
+ ];
202
+ //# sourceMappingURL=agenticow-speculate-tools.js.map
@@ -31,83 +31,10 @@
31
31
  *
32
32
  * @module @claude-flow/cli/mcp-tools/agenticow
33
33
  */
34
- import { existsSync } from 'node:fs';
35
- import { getProjectCwd } from './types.js';
36
- import { resolve, isAbsolute } from 'node:path';
37
- const PACKAGE_NAME = 'agenticow';
38
- // Cache: module load is expensive enough to amortize across handler calls.
39
- // null = not yet attempted; false = attempted and unavailable; module = loaded.
40
- let _agenticowMod = null;
41
- let _loadAttempted = false;
42
- async function loadAgenticow() {
43
- if (_loadAttempted)
44
- return _agenticowMod;
45
- _loadAttempted = true;
46
- try {
47
- _agenticowMod = await import(PACKAGE_NAME);
48
- return _agenticowMod;
49
- }
50
- catch (err) {
51
- if (err && (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND' ||
52
- /Cannot find (module|package)/i.test(String(err.message)))) {
53
- _agenticowMod = false;
54
- return null;
55
- }
56
- throw err;
57
- }
58
- }
59
- function degradedResult(reason) {
60
- return { success: true, degraded: true, reason };
61
- }
62
- function resolveMemoryPath(path) {
63
- if (!path || typeof path !== 'string')
64
- throw new Error('memory path is required');
65
- // D-2 style: reject path traversal in user-supplied paths
66
- if (/\.\.[\\/]|\0/.test(path))
67
- throw new Error('memory path contains disallowed characters');
68
- return isAbsolute(path) ? path : resolve(getProjectCwd(), path);
69
- }
70
- /**
71
- * Lineage manifest companion path. agenticow persists the COW chain
72
- * (working → checkpoints → base) into `<file>.agenticow.json` next to the
73
- * `.rvf` data file. Without this, checkpoints and forks are in-memory only
74
- * and disappear when the AgenticMemory handle closes. Mirrors the bin
75
- * CLI's `manifestFor(file)` helper.
76
- */
77
- function manifestFor(file) {
78
- return `${file}.agenticow.json`;
79
- }
80
- function validateLabel(label) {
81
- if (!label || typeof label !== 'string')
82
- throw new Error('label is required');
83
- if (label.length > 256)
84
- throw new Error('label exceeds 256 chars');
85
- if (!/^[A-Za-z0-9_.\-:/@]+$/.test(label)) {
86
- throw new Error('label may only contain [A-Za-z0-9_.\\-:/@]');
87
- }
88
- return label;
89
- }
90
- /**
91
- * Open (or create) a base memory file. When a lineage manifest exists at
92
- * `<file>.agenticow.json`, we load that to restore the COW chain (checkpoints,
93
- * ancestors). When only the `.rvf` exists, fresh-open it. When neither exists,
94
- * dimension is required to create. Mirrors the bin CLI's `loadMem()` helper.
95
- */
96
- async function openWithLineage(api, file, dimension) {
97
- const manifest = manifestFor(file);
98
- if (existsSync(manifest)) {
99
- // The class-level static method `load` reconstructs the full chain.
100
- return api.AgenticMemory.load(manifest);
101
- }
102
- const opts = {};
103
- if (typeof dimension === 'number' && Number.isInteger(dimension) && dimension > 0) {
104
- opts.dimension = dimension;
105
- }
106
- else if (!existsSync(file)) {
107
- throw new Error('dimension is required when creating a new memory file');
108
- }
109
- return api.open(file, opts);
110
- }
34
+ // Loader + path/lineage helpers are shared with the SwarmMemoryBranches
35
+ // service (src/services/swarm-memory-branches.ts) via one module so the
36
+ // optional-dep dance and the COW open/fork semantics live in exactly one place.
37
+ import { loadAgenticow, degradedResult, resolveMemoryPath, manifestFor, validateLabel, openWithLineage, } from './agenticow-loader.js';
111
38
  export const agenticowTools = [
112
39
  {
113
40
  name: 'agenticow_branch',
@@ -121,6 +48,7 @@ export const agenticowTools = [
121
48
  branchPath: { type: 'string', description: 'Path to write the branch file' },
122
49
  label: { type: 'string', description: 'Human-readable label for the branch (alnum + _.-:/@ only)' },
123
50
  dimension: { type: 'integer', description: 'Vector dimension (required only when basePath does not exist yet)' },
51
+ nativeAnn: { type: 'boolean', description: 'Use the native Rust COW dual-graph ANN path (recall@10=1.0, query spans the COW boundary in one Rust call). Default false (exact JS chain-walk). Set true when the branch will be queried.', default: false },
124
52
  },
125
53
  required: ['basePath', 'branchPath', 'label'],
126
54
  },
@@ -132,9 +60,10 @@ export const agenticowTools = [
132
60
  const basePath = resolveMemoryPath(String(input.basePath));
133
61
  const branchPath = resolveMemoryPath(String(input.branchPath));
134
62
  const dim = input.dimension;
63
+ const nativeAnn = input.nativeAnn === true;
135
64
  const base = await openWithLineage(api, basePath, dim);
136
65
  try {
137
- const branch = await base.fork(label, branchPath);
66
+ const branch = await base.fork(label, branchPath, { nativeAnn });
138
67
  // Persist lineage manifests so the branch (and base) reopen with
139
68
  // their COW chain intact. Without this, fork is in-memory only.
140
69
  await branch.save?.(manifestFor(branchPath));
@@ -145,6 +74,7 @@ export const agenticowTools = [
145
74
  basePath,
146
75
  branchPath,
147
76
  label,
77
+ nativeAnn,
148
78
  };
149
79
  }
150
80
  finally {
@@ -152,6 +82,181 @@ export const agenticowTools = [
152
82
  }
153
83
  },
154
84
  },
85
+ {
86
+ name: 'agenticow_ingest',
87
+ description: 'agenticow — write vectors (with optional text payloads) into an .rvf memory branch or base. Records: [{id?, vector, text?}] — id auto-assigns when omitted. This is the write half that makes a branch usable: agenticow_branch creates an empty COW child, but without ingest it has nothing to read back. Use when you have branched and must populate the branch (agenticow_branch alone leaves it empty). Editing the base directly is wrong when the writes are speculative — ingest into a branch, then promote only if validated. Persists via .agenticow.json lineage manifest.',
88
+ category: 'memory',
89
+ tags: ['agenticow', 'memory', 'cow', 'ingest', 'write'],
90
+ inputSchema: {
91
+ type: 'object',
92
+ properties: {
93
+ path: { type: 'string', description: 'Path to .rvf memory file (branch or base)' },
94
+ records: {
95
+ type: 'array',
96
+ description: 'Vectors to ingest: [{id?: number, vector: number[], text?: string}]',
97
+ items: {
98
+ type: 'object',
99
+ properties: {
100
+ id: { type: 'integer', description: 'Explicit id (auto-assigned when omitted)' },
101
+ vector: { type: 'array', items: { type: 'number' }, description: 'Embedding vector (length must equal the memory dimension)' },
102
+ text: { type: 'string', description: 'Optional payload surfaced on query hits' },
103
+ },
104
+ required: ['vector'],
105
+ },
106
+ },
107
+ dimension: { type: 'integer', description: 'Vector dimension (required only when path does not exist yet)' },
108
+ },
109
+ required: ['path', 'records'],
110
+ },
111
+ handler: async (input) => {
112
+ const api = await loadAgenticow();
113
+ if (!api)
114
+ return degradedResult('agenticow-not-found');
115
+ const path = resolveMemoryPath(String(input.path));
116
+ const records = input.records;
117
+ if (!Array.isArray(records) || records.length === 0) {
118
+ throw new Error('records must be a non-empty array of {id?, vector, text?}');
119
+ }
120
+ for (const r of records) {
121
+ if (!Array.isArray(r.vector) || r.vector.length === 0) {
122
+ throw new Error('each record requires a non-empty numeric vector');
123
+ }
124
+ }
125
+ const dim = input.dimension ?? records[0].vector.length;
126
+ const mem = await openWithLineage(api, path, dim);
127
+ try {
128
+ const result = await mem.ingest(records.map((r) => ({
129
+ ...(typeof r.id === 'number' ? { id: r.id } : {}),
130
+ vector: r.vector,
131
+ ...(r.text !== undefined ? { text: r.text } : {}),
132
+ })));
133
+ await mem.save?.(manifestFor(path));
134
+ return { success: true, path, ingested: result };
135
+ }
136
+ finally {
137
+ await mem.close?.();
138
+ }
139
+ },
140
+ },
141
+ {
142
+ name: 'agenticow_query',
143
+ description: 'agenticow — k-NN read across an .rvf memory branch\'s full COW lineage (parent ∪ edits, child wins), returning {id, distance, branch, text}. Read-only (no manifest write). The `branch` field on each hit tells you which lineage node the result came from — the read-through semantics that make branching useful. Use when you need to read from an agent/session branch without materializing a full copy. Re-opening the base and manually merging edits is wrong: query already spans the chain (and uses the single-call Rust path when the branch was forked with nativeAnn).',
144
+ category: 'memory',
145
+ tags: ['agenticow', 'memory', 'cow', 'query', 'read', 'search'],
146
+ inputSchema: {
147
+ type: 'object',
148
+ properties: {
149
+ path: { type: 'string', description: 'Path to .rvf memory file' },
150
+ vector: { type: 'array', items: { type: 'number' }, description: 'Query embedding vector' },
151
+ k: { type: 'integer', description: 'Number of nearest neighbors to return', default: 10 },
152
+ efSearch: { type: 'integer', description: 'HNSW efSearch per lineage store (higher = better recall, slower)' },
153
+ },
154
+ required: ['path', 'vector'],
155
+ },
156
+ handler: async (input) => {
157
+ const api = await loadAgenticow();
158
+ if (!api)
159
+ return degradedResult('agenticow-not-found');
160
+ const path = resolveMemoryPath(String(input.path));
161
+ const vector = input.vector;
162
+ if (!Array.isArray(vector) || vector.length === 0) {
163
+ throw new Error('vector must be a non-empty numeric array');
164
+ }
165
+ const k = typeof input.k === 'number' ? input.k : 10;
166
+ const opts = {};
167
+ if (typeof input.efSearch === 'number')
168
+ opts.efSearch = input.efSearch;
169
+ const mem = await openWithLineage(api, path);
170
+ try {
171
+ const hits = await mem.query(vector, k, opts);
172
+ return { success: true, path, k, hits };
173
+ }
174
+ finally {
175
+ await mem.close?.();
176
+ }
177
+ },
178
+ },
179
+ {
180
+ name: 'agenticow_diff',
181
+ description: 'agenticow — show what a branch changed relative to its lineage: {added, overridden, deleted} vector-id lists. Use when you are about to promote and want to preview the exact merge, or when auditing what a branch actually wrote. Diffing by re-querying is wrong because deletions (tombstones) are invisible to a read — diff() surfaces them explicitly. Requires the branch was opened with edit tracking (default on).',
182
+ category: 'memory',
183
+ tags: ['agenticow', 'memory', 'cow', 'diff'],
184
+ inputSchema: {
185
+ type: 'object',
186
+ properties: {
187
+ path: { type: 'string', description: 'Path to branch .rvf file' },
188
+ },
189
+ required: ['path'],
190
+ },
191
+ handler: async (input) => {
192
+ const api = await loadAgenticow();
193
+ if (!api)
194
+ return degradedResult('agenticow-not-found');
195
+ const path = resolveMemoryPath(String(input.path));
196
+ const mem = await openWithLineage(api, path);
197
+ try {
198
+ const diff = await mem.diff();
199
+ return { success: true, path, diff };
200
+ }
201
+ finally {
202
+ await mem.close?.();
203
+ }
204
+ },
205
+ },
206
+ {
207
+ name: 'agenticow_lineage',
208
+ description: 'agenticow — walk the COW chain of an .rvf memory file: an ordered list of nodes (role working|checkpoint|base, id, label, parent, createdAt, mutations, tombstones). Use when you need branch history — to find checkpoint ids for a targeted rollback, or to debug a promote. Guessing the chain from filenames is wrong — lineage is the authoritative structure the store maintains.',
209
+ category: 'memory',
210
+ tags: ['agenticow', 'memory', 'cow', 'lineage', 'history'],
211
+ inputSchema: {
212
+ type: 'object',
213
+ properties: {
214
+ path: { type: 'string', description: 'Path to .rvf memory file' },
215
+ },
216
+ required: ['path'],
217
+ },
218
+ handler: async (input) => {
219
+ const api = await loadAgenticow();
220
+ if (!api)
221
+ return degradedResult('agenticow-not-found');
222
+ const path = resolveMemoryPath(String(input.path));
223
+ const mem = await openWithLineage(api, path);
224
+ try {
225
+ const lineage = await mem.lineage();
226
+ return { success: true, path, lineage };
227
+ }
228
+ finally {
229
+ await mem.close?.();
230
+ }
231
+ },
232
+ },
233
+ {
234
+ name: 'agenticow_status',
235
+ description: 'agenticow — health/geometry of an .rvf memory file: {totalVectors, totalSegments, fileSize, currentEpoch, deadSpaceRatio, readOnly, chainDepth, dimension, metric}. Use when you need to check vector count before/after ingest, spot compaction pressure (deadSpaceRatio), or confirm the dimension before ingesting into a shared base. Pure read.',
236
+ category: 'memory',
237
+ tags: ['agenticow', 'memory', 'cow', 'status'],
238
+ inputSchema: {
239
+ type: 'object',
240
+ properties: {
241
+ path: { type: 'string', description: 'Path to .rvf memory file' },
242
+ },
243
+ required: ['path'],
244
+ },
245
+ handler: async (input) => {
246
+ const api = await loadAgenticow();
247
+ if (!api)
248
+ return degradedResult('agenticow-not-found');
249
+ const path = resolveMemoryPath(String(input.path));
250
+ const mem = await openWithLineage(api, path);
251
+ try {
252
+ const status = await mem.status();
253
+ return { success: true, path, status };
254
+ }
255
+ finally {
256
+ await mem.close?.();
257
+ }
258
+ },
259
+ },
155
260
  {
156
261
  name: 'agenticow_checkpoint',
157
262
  description: 'agenticow — freeze a labelled restore point on an .rvf memory file. Subsequent edits stay in a fresh COW child; rollback returns here. Use when you are about to run an experimental Darwin tick or speculative agent edit that may need to be discarded. Relying on the working node alone is wrong because there is no "undo last N writes" semantics — without a checkpoint, a bad ingest contaminates the base. Persists via .agenticow.json lineage manifest so it survives close+reopen.',
@@ -191,6 +296,7 @@ export const agenticowTools = [
191
296
  type: 'object',
192
297
  properties: {
193
298
  path: { type: 'string', description: 'Path to .rvf memory file' },
299
+ checkpointId: { type: 'string', description: 'Target checkpoint id from agenticow_lineage (omit to roll back to the most recent checkpoint)' },
194
300
  },
195
301
  required: ['path'],
196
302
  },
@@ -199,9 +305,10 @@ export const agenticowTools = [
199
305
  if (!api)
200
306
  return degradedResult('agenticow-not-found');
201
307
  const path = resolveMemoryPath(String(input.path));
308
+ const checkpointId = input.checkpointId ? String(input.checkpointId) : undefined;
202
309
  const mem = await openWithLineage(api, path);
203
310
  try {
204
- const r = await mem.rollback();
311
+ const r = checkpointId ? await mem.rollback(checkpointId) : await mem.rollback();
205
312
  await mem.save?.(manifestFor(path));
206
313
  return { success: true, path, rolledBack: true, result: r };
207
314
  }
@@ -27,6 +27,7 @@ export { autopilotTools } from './autopilot-tools.js';
27
27
  export { metaharnessTools } from './metaharness-tools.js';
28
28
  export { testgenTools } from './testgen-tools.js';
29
29
  export { agenticowTools } from './agenticow-tools.js';
30
+ export { agenticowSpeculateTools } from './agenticow-speculate-tools.js';
30
31
  export { agentbbsTools } from './agentbbs-tools.js';
31
32
  export { businessPodTools } from './business-pod-tools.js';
32
33
  export { httpFetchTools } from './http-fetch-tools.js';
@@ -29,6 +29,8 @@ export { metaharnessTools } from './metaharness-tools.js';
29
29
  export { testgenTools } from './testgen-tools.js';
30
30
  // agenticow@~0.2.3 — Copy-On-Write memory branching (162-byte branches)
31
31
  export { agenticowTools } from './agenticow-tools.js';
32
+ // agenticow step 4 — speculative branch-and-promote (A/B memory exploration)
33
+ export { agenticowSpeculateTools } from './agenticow-speculate-tools.js';
32
34
  // ADR-164 — AgentBBS federated business-domain BBS rooms (Phase 1)
33
35
  export { agentbbsTools } from './agentbbs-tools.js';
34
36
  // ADR-164 Phase 2 — Business-pod template validation