claude-flow 3.41.4 → 3.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.proven-config-version +1 -0
- package/.claude/proven-config.json +42 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +38 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
- package/node_modules/@claude-flow/codex/package.json +2 -1
- package/node_modules/@claude-flow/security/dist/index.d.ts +1 -1
- package/node_modules/@claude-flow/security/dist/index.d.ts.map +1 -1
- package/node_modules/@claude-flow/security/dist/index.js +1 -1
- package/node_modules/@claude-flow/security/dist/index.js.map +1 -1
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.d.ts +11 -0
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.d.ts.map +1 -1
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.js +0 -0
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.js.map +1 -1
- package/node_modules/@claude-flow/security/package.json +1 -0
- package/package.json +1 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
- package/v3/@claude-flow/cli/dist/src/commands/doctor.d.ts +7 -0
- package/v3/@claude-flow/cli/dist/src/commands/doctor.js +58 -35
- package/v3/@claude-flow/cli/dist/src/commands/hive-mind.js +4 -3
- package/v3/@claude-flow/cli/dist/src/commands/swarm.js +89 -18
- package/v3/@claude-flow/cli/dist/src/init/claudemd-generator.js +2 -2
- package/v3/@claude-flow/cli/dist/src/mcp-server.js +12 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-tools.d.ts +15 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-tools.js +98 -14
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +17 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-mind-tools.d.ts +8 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-mind-tools.js +80 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +102 -31
- package/v3/@claude-flow/cli/dist/src/mcp-tools/policy-enforcer.d.ts +126 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/policy-enforcer.js +177 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/seraphina-tools.js +7 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-tools.d.ts +12 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-tools.js +87 -5
- package/v3/@claude-flow/cli/dist/src/memory/intelligence.d.ts +14 -2
- package/v3/@claude-flow/cli/dist/src/memory/intelligence.js +33 -12
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +10 -2
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +19 -2
- package/v3/@claude-flow/cli/dist/src/ruvector/graph-backend.js +112 -26
- package/v3/@claude-flow/cli/dist/src/services/policy-runtime.js +63 -5
- package/v3/@claude-flow/cli/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/ruvector/diskann-backend.d.ts +0 -78
- package/v3/@claude-flow/cli/dist/src/ruvector/diskann-backend.js +0 -310
|
@@ -250,10 +250,20 @@ class LocalSonaCoordinator {
|
|
|
250
250
|
const oldConfidence = pattern.confidence;
|
|
251
251
|
// Check EWC penalty before applying update
|
|
252
252
|
if (ewcConsolidator) {
|
|
253
|
-
const oldWeights = [oldConfidence];
|
|
254
253
|
const proposedConfidence = Math.min(1.0, oldConfidence + this.config.loraLearningRate * reward);
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
// Use computeConfidencePenalty (averages the full Fisher diagonal),
|
|
255
|
+
// not getPenalty([oldConf],[newConf]) — that call shape collapses
|
|
256
|
+
// to fisherDiag[0] only (Math.min(1,1,384) === 1), an arbitrary
|
|
257
|
+
// single dimension instead of the full accumulated Fisher signal.
|
|
258
|
+
// computeConfidencePenalty exists precisely for this
|
|
259
|
+
// scalar-confidence case (see its docstring) but was unwired.
|
|
260
|
+
// Note: neither call shape differentiates between patterns — both
|
|
261
|
+
// take only a confidence delta, not a per-pattern embedding, so
|
|
262
|
+
// two patterns with the same delta under the same consolidator
|
|
263
|
+
// state get the same penalty either way. This fix corrects which
|
|
264
|
+
// shared Fisher signal informs that penalty; it does not add
|
|
265
|
+
// per-pattern discrimination (see ewc-distill-confidence-gate.test.ts).
|
|
266
|
+
const penalty = ewcConsolidator.computeConfidencePenalty(oldConfidence, proposedConfidence);
|
|
257
267
|
totalEwcPenalty += penalty;
|
|
258
268
|
// If penalty is too high, reduce the update magnitude
|
|
259
269
|
if (penalty > this.config.ewcLambda) {
|
|
@@ -279,13 +289,14 @@ class LocalSonaCoordinator {
|
|
|
279
289
|
}
|
|
280
290
|
}
|
|
281
291
|
}
|
|
282
|
-
// Update EWC Fisher matrix with confidence changes
|
|
292
|
+
// Update EWC Fisher matrix with confidence changes. updateFisherFromConfidences
|
|
293
|
+
// takes the full per-pattern embedding + confidence-delta batch directly (it
|
|
294
|
+
// computes the same squared confidence-delta-scaled-embedding gradient proxy
|
|
295
|
+
// internally) — replaces the previous per-change recordGradient loop, which
|
|
296
|
+
// updated the full 384-dim globalFisher but fed a signal that getPenalty's
|
|
297
|
+
// 1-element call shape then read back only at index 0.
|
|
283
298
|
if (ewcConsolidator && confidenceChanges.length > 0) {
|
|
284
|
-
|
|
285
|
-
// Use confidence delta as gradient proxy
|
|
286
|
-
const gradient = change.embedding.map(e => e * Math.abs(change.newConf - change.oldConf));
|
|
287
|
-
ewcConsolidator.recordGradient(change.id, gradient, true);
|
|
288
|
-
}
|
|
299
|
+
ewcConsolidator.updateFisherFromConfidences(confidenceChanges);
|
|
289
300
|
}
|
|
290
301
|
// Persist updated patterns
|
|
291
302
|
bank.flushToDisk();
|
|
@@ -467,7 +478,17 @@ class LocalReasoningBank {
|
|
|
467
478
|
this.saveToDisk();
|
|
468
479
|
}
|
|
469
480
|
/**
|
|
470
|
-
* Find similar patterns by embedding
|
|
481
|
+
* Find similar patterns by embedding.
|
|
482
|
+
*
|
|
483
|
+
* `confidence` on each result is the pattern's own learned reliability
|
|
484
|
+
* (unchanged from storage) — NOT how well it matches this query. The
|
|
485
|
+
* per-query cosine score is returned separately as `similarity`. Callers
|
|
486
|
+
* that want "how good a semantic match is this" must read `.similarity`;
|
|
487
|
+
* callers that want "how reliable has this pattern proven to be" read
|
|
488
|
+
* `.confidence`. Prior to this fix both were conflated (confidence was
|
|
489
|
+
* overwritten with the cosine score), which silently broke any consumer
|
|
490
|
+
* that needed to tell them apart (found during the 2026-09-12 dream-cycle
|
|
491
|
+
* intelligence-surface review).
|
|
471
492
|
*/
|
|
472
493
|
findSimilar(queryEmbedding, options) {
|
|
473
494
|
const { k = 5, threshold = 0.5, type } = options;
|
|
@@ -489,7 +510,7 @@ class LocalReasoningBank {
|
|
|
489
510
|
// Update usage
|
|
490
511
|
s.pattern.usageCount++;
|
|
491
512
|
s.pattern.lastUsedAt = Date.now();
|
|
492
|
-
return { ...s.pattern,
|
|
513
|
+
return { ...s.pattern, similarity: s.score };
|
|
493
514
|
});
|
|
494
515
|
}
|
|
495
516
|
/**
|
|
@@ -985,7 +1006,7 @@ export async function findSimilarPatterns(query, options) {
|
|
|
985
1006
|
usageCount: r.usageCount,
|
|
986
1007
|
createdAt: r.createdAt,
|
|
987
1008
|
lastUsedAt: r.lastUsedAt,
|
|
988
|
-
similarity: r.similarity
|
|
1009
|
+
similarity: r.similarity
|
|
989
1010
|
}));
|
|
990
1011
|
}
|
|
991
1012
|
catch {
|
|
@@ -1866,7 +1866,12 @@ export async function bridgeStorePattern(options) {
|
|
|
1866
1866
|
}
|
|
1867
1867
|
catch { /* HNSW is best-effort */ }
|
|
1868
1868
|
}
|
|
1869
|
-
|
|
1869
|
+
// #3324: bridgeStoreEntry's `result.id` is its OWN internally generated
|
|
1870
|
+
// row id (generateId('entry')), a different value from the `key` the row
|
|
1871
|
+
// was actually stored under. getEntry/memory_retrieve look up by `key`,
|
|
1872
|
+
// so returning result.id here handed the caller a handle that can never
|
|
1873
|
+
// be read back — return `patternId` (the real key) instead.
|
|
1874
|
+
return { success: true, patternId, controller: 'bridge-fallback' };
|
|
1870
1875
|
}
|
|
1871
1876
|
catch {
|
|
1872
1877
|
return null;
|
|
@@ -1915,10 +1920,13 @@ export async function bridgeSearchPatterns(options) {
|
|
|
1915
1920
|
const qEmb = await generateEmbedding(options.query);
|
|
1916
1921
|
if (qEmb && Array.isArray(qEmb.embedding) && qEmb.embedding.length > 0) {
|
|
1917
1922
|
const hits = reasoningBank.findSimilar(qEmb.embedding, { k, threshold });
|
|
1923
|
+
// findSimilar() no longer overwrites confidence with the query-match
|
|
1924
|
+
// score — prefer similarity (the actual match strength) for search ranking,
|
|
1925
|
+
// falling back to confidence/score only for older/foreign result shapes.
|
|
1918
1926
|
mapped = (Array.isArray(hits) ? hits : []).map((r) => ({
|
|
1919
1927
|
id: r.id ?? '',
|
|
1920
1928
|
content: r.content ?? '',
|
|
1921
|
-
score: r.confidence ?? r.score ?? 0,
|
|
1929
|
+
score: r.similarity ?? r.confidence ?? r.score ?? 0,
|
|
1922
1930
|
}));
|
|
1923
1931
|
}
|
|
1924
1932
|
}
|
|
@@ -1855,12 +1855,15 @@ export async function checkMemoryInitialization(dbPath) {
|
|
|
1855
1855
|
if (!fs.existsSync(path_)) {
|
|
1856
1856
|
return { initialized: false };
|
|
1857
1857
|
}
|
|
1858
|
+
// #3249: declared outside the try so the handle can be released on the
|
|
1859
|
+
// failure path as well as the success path.
|
|
1860
|
+
let db;
|
|
1858
1861
|
try {
|
|
1859
1862
|
// Try to load with sql.js
|
|
1860
1863
|
const initSqlJs = (await import('sql.js')).default;
|
|
1861
1864
|
const SQL = await initSqlJs();
|
|
1862
1865
|
const fileBuffer = fs.readFileSync(path_);
|
|
1863
|
-
|
|
1866
|
+
db = new SQL.Database(fileBuffer);
|
|
1864
1867
|
// Check for metadata table
|
|
1865
1868
|
const tables = db.exec("SELECT name FROM sqlite_master WHERE type='table'");
|
|
1866
1869
|
const tableNames = tables[0]?.values?.map(v => v[0]) || [];
|
|
@@ -1876,7 +1879,6 @@ export async function checkMemoryInitialization(dbPath) {
|
|
|
1876
1879
|
catch {
|
|
1877
1880
|
// Metadata table might not exist
|
|
1878
1881
|
}
|
|
1879
|
-
db.close();
|
|
1880
1882
|
return {
|
|
1881
1883
|
initialized: true,
|
|
1882
1884
|
version,
|
|
@@ -1893,6 +1895,21 @@ export async function checkMemoryInitialization(dbPath) {
|
|
|
1893
1895
|
// Could not read database
|
|
1894
1896
|
return { initialized: false };
|
|
1895
1897
|
}
|
|
1898
|
+
finally {
|
|
1899
|
+
// #3249: release the handle on every path. An RFE1-encrypted image is not
|
|
1900
|
+
// parseable as SQLite, so the schema query above throws and the catch
|
|
1901
|
+
// returns — which used to skip the inline db.close() entirely, leaving the
|
|
1902
|
+
// sql.js Database and its MEMFS copy open for the life of the process.
|
|
1903
|
+
// Every memory MCP tool call runs this check, so a long session accumulates
|
|
1904
|
+
// one unclosed handle per call. Measured retention is a few hundred KiB per
|
|
1905
|
+
// leaked handle (it does not scale with image size).
|
|
1906
|
+
try {
|
|
1907
|
+
db?.close();
|
|
1908
|
+
}
|
|
1909
|
+
catch {
|
|
1910
|
+
// Already closed, or never successfully constructed.
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1896
1913
|
}
|
|
1897
1914
|
/**
|
|
1898
1915
|
* Apply temporal decay to patterns
|
|
@@ -13,51 +13,137 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { join } from 'path';
|
|
15
15
|
// Lazy-loaded graph-node module
|
|
16
|
-
let
|
|
17
|
-
let
|
|
18
|
-
let graphBackendLoaded = false;
|
|
16
|
+
let graphNodeModulePromise = null;
|
|
17
|
+
let graphDbPromise = null;
|
|
19
18
|
let graphBackendAvailable = false;
|
|
20
19
|
const DEFAULT_EMBEDDING_DIM = 8; // Minimal embedding for graph structure
|
|
20
|
+
const DEFAULT_DISTANCE_METRIC = 'Cosine';
|
|
21
21
|
/**
|
|
22
|
-
* Load @ruvector/graph-node via createRequire (CJS package)
|
|
22
|
+
* Load @ruvector/graph-node via createRequire (CJS package).
|
|
23
|
+
*
|
|
24
|
+
* The promise is the memo rather than a "loaded" flag. The flag was set
|
|
25
|
+
* BEFORE its own `await`, so a caller arriving inside that window would take
|
|
26
|
+
* the early return and read `graphNodeModule` while it was still `null`.
|
|
27
|
+
* I could not make that window observable -- `import('module')` resolves a
|
|
28
|
+
* builtin before another caller gets a turn -- so this is hardening on the
|
|
29
|
+
* same shape as the open memo below, not a defect with a reproduction behind
|
|
30
|
+
* it.
|
|
23
31
|
*/
|
|
24
32
|
async function loadGraphNode() {
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
if (!graphNodeModulePromise) {
|
|
34
|
+
graphNodeModulePromise = (async () => {
|
|
35
|
+
try {
|
|
36
|
+
const { createRequire } = await import('module');
|
|
37
|
+
const requireCjs = createRequire(import.meta.url);
|
|
38
|
+
const mod = requireCjs('@ruvector/graph-node');
|
|
39
|
+
graphBackendAvailable = true;
|
|
40
|
+
return mod;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
graphBackendAvailable = false;
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
})();
|
|
34
47
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
return graphNodeModulePromise;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Report a graph-backend problem.
|
|
52
|
+
*
|
|
53
|
+
* No "already warned" flag: the open runs once per process because
|
|
54
|
+
* `getGraphDb` memoizes the promise, and a second flag would only hide it if
|
|
55
|
+
* that ever stopped being true.
|
|
56
|
+
*/
|
|
57
|
+
function warnGraphInit(message) {
|
|
58
|
+
console.warn(`[graph-backend] ${message}`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Whether this handle is actually backed by the file we asked for.
|
|
62
|
+
*
|
|
63
|
+
* `@ruvector/graph-node` 2.1.0 accepts a bare path STRING without throwing
|
|
64
|
+
* and hands back a volatile in-memory instance -- `isPersistent() === false`,
|
|
65
|
+
* `getStoragePath() === null`. Nothing downstream notices: writes succeed,
|
|
66
|
+
* reads succeed, and the graph is gone at exit. Passing the options object
|
|
67
|
+
* fixes that, and this check is what makes the fix self-reporting instead of
|
|
68
|
+
* something a future signature change can quietly undo.
|
|
69
|
+
*
|
|
70
|
+
* A build that exposes neither accessor cannot be interrogated, so it is
|
|
71
|
+
* accepted rather than refused -- this guards against a silent downgrade, not
|
|
72
|
+
* against an unfamiliar version.
|
|
73
|
+
*/
|
|
74
|
+
function isPersistentAt(db, storagePath) {
|
|
75
|
+
const canReport = typeof db?.isPersistent === 'function' || typeof db?.getStoragePath === 'function';
|
|
76
|
+
if (!canReport)
|
|
77
|
+
return true;
|
|
78
|
+
if (typeof db.isPersistent === 'function' && db.isPersistent() !== true)
|
|
79
|
+
return false;
|
|
80
|
+
if (typeof db.getStoragePath === 'function' && db.getStoragePath() !== storagePath) {
|
|
81
|
+
return false;
|
|
38
82
|
}
|
|
83
|
+
return true;
|
|
39
84
|
}
|
|
40
85
|
/**
|
|
41
|
-
*
|
|
86
|
+
* Open the graph database, or return null with a stated reason.
|
|
87
|
+
*
|
|
88
|
+
* The old fallback replaced an open failure with `new mod.GraphDatabase()` --
|
|
89
|
+
* an empty in-memory graph that answers every query successfully and persists
|
|
90
|
+
* nothing. A permission error, a lock held by another process and a healthy
|
|
91
|
+
* database were indistinguishable from the outside. Callers already handle
|
|
92
|
+
* `null` by degrading to `backend: 'unavailable'`, which is the honest shape
|
|
93
|
+
* for "the graph is not there".
|
|
42
94
|
*/
|
|
43
|
-
async function
|
|
44
|
-
if (graphDb)
|
|
45
|
-
return graphDb;
|
|
95
|
+
async function openGraphDb() {
|
|
46
96
|
const mod = await loadGraphNode();
|
|
47
97
|
if (!mod)
|
|
48
98
|
return null;
|
|
49
|
-
// Use persistent path if available, otherwise in-memory
|
|
50
99
|
const dataDir = join(process.cwd(), '.claude-flow', 'graph');
|
|
100
|
+
const storagePath = join(dataDir, 'agents.db');
|
|
101
|
+
let db;
|
|
51
102
|
try {
|
|
52
103
|
const fs = await import('fs');
|
|
53
104
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
54
|
-
|
|
105
|
+
// The options object, not the path string: see `isPersistentAt`.
|
|
106
|
+
db = new mod.GraphDatabase({
|
|
107
|
+
storagePath,
|
|
108
|
+
dimensions: DEFAULT_EMBEDDING_DIM,
|
|
109
|
+
distanceMetric: DEFAULT_DISTANCE_METRIC,
|
|
110
|
+
});
|
|
55
111
|
}
|
|
56
|
-
catch {
|
|
57
|
-
|
|
58
|
-
|
|
112
|
+
catch (error) {
|
|
113
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
114
|
+
warnGraphInit(`could not open ${storagePath}: ${reason}. Graph backend disabled.`);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (!isPersistentAt(db, storagePath)) {
|
|
118
|
+
const reported = typeof db?.getStoragePath === 'function' ? db.getStoragePath() : 'unknown';
|
|
119
|
+
warnGraphInit(`opened a non-persistent graph (storage path ${String(reported)}, wanted ${storagePath}). ` +
|
|
120
|
+
'Graph backend disabled rather than writing to a graph that vanishes at exit.');
|
|
121
|
+
try {
|
|
122
|
+
db?.close?.();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// A handle we are already discarding.
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return db;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get or create the singleton graph database instance.
|
|
133
|
+
*
|
|
134
|
+
* The promise is the singleton, not the handle: two callers racing the first
|
|
135
|
+
* call used to each run the constructor, and the second overwrote the first's
|
|
136
|
+
* `graphDb` while nodes were already being written through it.
|
|
137
|
+
*/
|
|
138
|
+
async function getGraphDb() {
|
|
139
|
+
if (!graphDbPromise) {
|
|
140
|
+
graphDbPromise = openGraphDb().catch((error) => {
|
|
141
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
142
|
+
warnGraphInit(`initialization failed: ${reason}. Graph backend disabled.`);
|
|
143
|
+
return null;
|
|
144
|
+
});
|
|
59
145
|
}
|
|
60
|
-
return
|
|
146
|
+
return graphDbPromise;
|
|
61
147
|
}
|
|
62
148
|
/**
|
|
63
149
|
* Create a minimal embedding for non-vector graph operations.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgenticPolicyEngine, createLegacyCompatibleState, } from '@claude-flow/security';
|
|
1
|
+
import { AgenticPolicyEngine, createLegacyCompatibleState, isMcpCallerAuthEnabled, decodeTokenEnvelope, publicKeyFromHex, verifyInvocationToken, } from '@claude-flow/security';
|
|
2
2
|
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
4
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
@@ -257,6 +257,67 @@ export async function revokePolicyApproval(id, projectRoot = process.cwd()) {
|
|
|
257
257
|
export async function verifyPolicyLedger(projectRoot = process.cwd()) {
|
|
258
258
|
return withPolicyTransaction(projectRoot, (engine) => engine.verifyLedger());
|
|
259
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Resolve the caller identity for `authorizeMcpTool`.
|
|
262
|
+
*
|
|
263
|
+
* ADR-377 Phase 3 (dream-cycle candidate, 2026-08-26) — off by default. By
|
|
264
|
+
* default (`isMcpCallerAuthEnabled()` false), this reproduces the exact
|
|
265
|
+
* pre-existing two-line behavior: the plain, unsigned `CLAUDE_FLOW_PRINCIPAL_ID`
|
|
266
|
+
* env var is trusted outright. That is the byte-identical-when-disabled
|
|
267
|
+
* requirement for this candidate — nothing about this branch's shape or
|
|
268
|
+
* output changes when the flag is off.
|
|
269
|
+
*
|
|
270
|
+
* When the flag is on, `CLAUDE_FLOW_PRINCIPAL_ID` alone is no longer
|
|
271
|
+
* sufficient — any process can set an arbitrary env var with zero proof.
|
|
272
|
+
* Instead this requires (and verifies) an Ed25519-signed `InvocationToken`
|
|
273
|
+
* that only a private-key holder (the DualModeOrchestrator that spawned this
|
|
274
|
+
* worker) could have minted, per `mcp-caller-identity.ts`. Missing, corrupt,
|
|
275
|
+
* forged, or expired tokens fail closed — this function throws rather than
|
|
276
|
+
* falling back to the unverified env var, matching the existing fail-closed
|
|
277
|
+
* pattern already used above in this function (`invalid-worker-capability-envelope`,
|
|
278
|
+
* `authoritative-worker-policy-root-unavailable`).
|
|
279
|
+
*
|
|
280
|
+
* Scope trade-off (deliberate — see mcp-caller-identity.ts's file header and
|
|
281
|
+
* orchestrator.ts's `workerEnvironment` for the fuller rationale): the token
|
|
282
|
+
* is minted once per worker spawn with a worker-lifetime TTL and a wildcard
|
|
283
|
+
* `toolName: '*'`, not the single-tool, 30s-TTL shape `InvocationToken` was
|
|
284
|
+
* originally designed for — so `opts.toolName` is deliberately not passed to
|
|
285
|
+
* `verifyInvocationToken` here, and the tool-mismatch check is not exercised
|
|
286
|
+
* for this use case. What this retains: a process that never received the
|
|
287
|
+
* signed token cannot forge one, so it cannot impersonate `agent:<workerId>`.
|
|
288
|
+
* What it does NOT retain: a compromised worker process can still use its
|
|
289
|
+
* own valid token for every MCP call for the rest of its lifetime — the
|
|
290
|
+
* token does not limit blast radius to a single call.
|
|
291
|
+
*/
|
|
292
|
+
function resolveMcpCallerIdentity() {
|
|
293
|
+
if (!isMcpCallerAuthEnabled()) {
|
|
294
|
+
return {
|
|
295
|
+
id: process.env.CLAUDE_FLOW_PRINCIPAL_ID ?? 'legacy-cli',
|
|
296
|
+
type: process.env.CLAUDE_FLOW_PRINCIPAL_ID ? 'agent' : 'legacy',
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const encodedToken = process.env.CLAUDE_FLOW_MCP_INVOCATION_TOKEN;
|
|
300
|
+
const publicKeyHex = process.env.CLAUDE_FLOW_MCP_CALLER_PUBKEY;
|
|
301
|
+
if (!encodedToken || !publicKeyHex) {
|
|
302
|
+
throw new Error('mcp-caller-auth-enabled-but-no-token');
|
|
303
|
+
}
|
|
304
|
+
const token = decodeTokenEnvelope(encodedToken);
|
|
305
|
+
if (!token) {
|
|
306
|
+
throw new Error('mcp-caller-auth-enabled-but-no-token');
|
|
307
|
+
}
|
|
308
|
+
let publicKey;
|
|
309
|
+
try {
|
|
310
|
+
publicKey = publicKeyFromHex(publicKeyHex);
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
throw new Error('mcp-caller-auth-enabled-but-no-token');
|
|
314
|
+
}
|
|
315
|
+
const result = verifyInvocationToken(token, publicKey, {});
|
|
316
|
+
if (!result.valid) {
|
|
317
|
+
throw new Error(`mcp-caller-auth-verification-failed:${result.reason}`);
|
|
318
|
+
}
|
|
319
|
+
return { id: token.callerId, type: 'agent' };
|
|
320
|
+
}
|
|
260
321
|
export async function authorizeMcpTool(toolName, input, context = {}, attributes = {}) {
|
|
261
322
|
let projectRoot = typeof context.projectRoot === 'string' ? context.projectRoot : process.cwd();
|
|
262
323
|
let processEnvelope;
|
|
@@ -284,10 +345,7 @@ export async function authorizeMcpTool(toolName, input, context = {}, attributes
|
|
|
284
345
|
}
|
|
285
346
|
}
|
|
286
347
|
return evaluatePolicyRequest({
|
|
287
|
-
identity:
|
|
288
|
-
id: process.env.CLAUDE_FLOW_PRINCIPAL_ID ?? 'legacy-cli',
|
|
289
|
-
type: process.env.CLAUDE_FLOW_PRINCIPAL_ID ? 'agent' : 'legacy',
|
|
290
|
-
},
|
|
348
|
+
identity: resolveMcpCallerIdentity(),
|
|
291
349
|
action: {
|
|
292
350
|
type: attributes.actionType ?? 'mcp.tool.call',
|
|
293
351
|
resource: toolName,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.42.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DiskANN Vector Search Backend
|
|
3
|
-
*
|
|
4
|
-
* SSD-friendly approximate nearest neighbor search using Vamana graph.
|
|
5
|
-
* Falls back gracefully to HNSW (@ruvector/router VectorDb) or
|
|
6
|
-
* pure-JS cosine similarity when DiskANN is unavailable.
|
|
7
|
-
*
|
|
8
|
-
* @module v3/cli/ruvector/diskann-backend
|
|
9
|
-
*/
|
|
10
|
-
export interface DiskAnnConfig {
|
|
11
|
-
dim: number;
|
|
12
|
-
maxDegree?: number;
|
|
13
|
-
buildBeam?: number;
|
|
14
|
-
searchBeam?: number;
|
|
15
|
-
alpha?: number;
|
|
16
|
-
pqSubspaces?: number;
|
|
17
|
-
storagePath?: string;
|
|
18
|
-
}
|
|
19
|
-
export interface SearchResult {
|
|
20
|
-
id: string;
|
|
21
|
-
distance: number;
|
|
22
|
-
score: number;
|
|
23
|
-
}
|
|
24
|
-
export type VectorBackend = 'diskann' | 'hnsw' | 'cosine-js';
|
|
25
|
-
/**
|
|
26
|
-
* Check if @ruvector/diskann is available
|
|
27
|
-
*/
|
|
28
|
-
export declare function isDiskAnnAvailable(): Promise<boolean>;
|
|
29
|
-
/**
|
|
30
|
-
* Create or get a DiskANN index instance
|
|
31
|
-
*/
|
|
32
|
-
export declare function getDiskAnnIndex(config: DiskAnnConfig): Promise<{
|
|
33
|
-
index: any;
|
|
34
|
-
backend: VectorBackend;
|
|
35
|
-
}>;
|
|
36
|
-
/**
|
|
37
|
-
* Get the active backend name
|
|
38
|
-
*/
|
|
39
|
-
export declare function getActiveBackend(): VectorBackend;
|
|
40
|
-
/**
|
|
41
|
-
* Reset the index (for testing)
|
|
42
|
-
*/
|
|
43
|
-
export declare function resetIndex(): void;
|
|
44
|
-
/**
|
|
45
|
-
* Insert a vector into the active backend
|
|
46
|
-
*/
|
|
47
|
-
export declare function insertVector(id: string, vector: Float32Array, config?: DiskAnnConfig): Promise<{
|
|
48
|
-
backend: VectorBackend;
|
|
49
|
-
}>;
|
|
50
|
-
/**
|
|
51
|
-
* Build the index (required for DiskANN before search)
|
|
52
|
-
*/
|
|
53
|
-
export declare function buildIndex(config?: DiskAnnConfig): Promise<void>;
|
|
54
|
-
/**
|
|
55
|
-
* Search for k nearest neighbors
|
|
56
|
-
*/
|
|
57
|
-
export declare function searchVectors(query: Float32Array, k: number, config?: DiskAnnConfig): Promise<SearchResult[]>;
|
|
58
|
-
export interface BenchmarkResult {
|
|
59
|
-
backend: VectorBackend;
|
|
60
|
-
dim: number;
|
|
61
|
-
vectorCount: number;
|
|
62
|
-
insertTimeMs: number;
|
|
63
|
-
buildTimeMs: number;
|
|
64
|
-
searchTimeMs: number;
|
|
65
|
-
searchesPerSecond: number;
|
|
66
|
-
recall: number;
|
|
67
|
-
memoryMB: number;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Run a benchmark comparing available backends
|
|
71
|
-
*/
|
|
72
|
-
export declare function benchmark(opts?: {
|
|
73
|
-
dim?: number;
|
|
74
|
-
vectorCount?: number;
|
|
75
|
-
k?: number;
|
|
76
|
-
queries?: number;
|
|
77
|
-
}): Promise<BenchmarkResult[]>;
|
|
78
|
-
//# sourceMappingURL=diskann-backend.d.ts.map
|