claude-flow 3.63.0 → 3.63.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/helpers/.helpers-version +1 -0
- package/.claude/helpers/helpers.manifest.json +13 -0
- package/.claude/proven-config.json +42 -0
- package/.claude/settings.local.json +8 -0
- package/.claude-plugin/marketplace.json +3 -3
- package/README.md +1 -1
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/node_modules/@claude-flow/security/dist/input-validator.d.ts +6 -6
- package/package.json +1 -1
- package/v3/@claude-flow/cli/README.md +1 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-tools.js +7 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-agent-tools.js +3 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/diskann-backend.d.ts +78 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/diskann-backend.js +310 -0
- package/v3/@claude-flow/cli/dist/src/services/flywheel-transaction.js +5 -0
- package/v3/@claude-flow/cli/package.json +1 -1
- package/v3/@claude-flow/shared/dist/events/event-store.test.d.ts +8 -0
- package/v3/@claude-flow/shared/dist/events/event-store.test.js +293 -0
- package/v3/@claude-flow/shared/dist/hooks/hooks.test.d.ts +9 -0
- package/v3/@claude-flow/shared/dist/hooks/hooks.test.js +322 -0
- package/v3/@claude-flow/shared/dist/hooks/verify-exports.test.d.ts +9 -0
- package/v3/@claude-flow/shared/dist/hooks/verify-exports.test.js +93 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.0
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest": {
|
|
3
|
+
"version": "3.32.24",
|
|
4
|
+
"files": {
|
|
5
|
+
"auto-memory-hook.mjs": "68be7e9a9eba7bf9c4e8a230db7bf61a243b965639f8504842799d6c6ca28762",
|
|
6
|
+
"hook-handler.cjs": "50ea92a72651bdc95634f7588d56a5870963168eef5226f66ed14af3b47c8d9a",
|
|
7
|
+
"intelligence.cjs": "bd1f8e4b034944aee1df0391dc47ac2e8cc4b3aa542c65407a59d49620bbf76b",
|
|
8
|
+
"statusline.cjs": "d2a0eac56d1267d8dbed2d70b09feb592299469196ec4b215909f2b052144882"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"signature": "Vi6dza9CfZgo25+eTMJ81fUjsNtQQfUZUZS61MxpBTlilSBTo88cNCCy4ZNC7OfH2r2JOYS7HqqYotf0/AJ9DA==",
|
|
12
|
+
"algorithm": "ed25519"
|
|
13
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"adoptedAt": 1783689629580,
|
|
3
|
+
"championId": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
|
|
4
|
+
"manifest": {
|
|
5
|
+
"schema": "ruflo.proven-config/v1",
|
|
6
|
+
"policy": {
|
|
7
|
+
"ref": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
|
|
8
|
+
"value": {
|
|
9
|
+
"alpha": 0.3,
|
|
10
|
+
"subjectWeight": 1,
|
|
11
|
+
"mmrLambda": 0.5,
|
|
12
|
+
"bodyWeight": 1.5,
|
|
13
|
+
"typePenaltyFactor": 0.5
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"layer": "framework/node-cli",
|
|
17
|
+
"compatibility": {
|
|
18
|
+
"ruflo": ">=3.24.0"
|
|
19
|
+
},
|
|
20
|
+
"benchmark": {
|
|
21
|
+
"corpus": "ADR-081-labelled-v1",
|
|
22
|
+
"corpusHash": "sha256:2f700b5c363e20a3bd88ce2bc9b87bbbbaa61732c6177894c6ec37890f888982"
|
|
23
|
+
},
|
|
24
|
+
"receipt": {
|
|
25
|
+
"heldOutDelta": 0.07381404928570845,
|
|
26
|
+
"redblue": "PASS",
|
|
27
|
+
"drift": 0,
|
|
28
|
+
"canary": {
|
|
29
|
+
"rollbackRate": 0,
|
|
30
|
+
"latencyP95": 244.612458000076,
|
|
31
|
+
"costPerTask": 0
|
|
32
|
+
},
|
|
33
|
+
"receiptCoverage": 1
|
|
34
|
+
},
|
|
35
|
+
"platform": [
|
|
36
|
+
"linux",
|
|
37
|
+
"macOS",
|
|
38
|
+
"windows"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"previous": ""
|
|
42
|
+
}
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
"source": "git-subdir",
|
|
58
58
|
"url": "https://github.com/proffesor-for-testing/agentic-qe.git",
|
|
59
59
|
"path": "plugins/agentic-qe-fleet",
|
|
60
|
-
"ref": "v3.15.
|
|
61
|
-
"sha": "
|
|
60
|
+
"ref": "v3.15.13",
|
|
61
|
+
"sha": "e976a8e451b331fff86fc249f2ba40f19644c9fc"
|
|
62
62
|
},
|
|
63
|
-
"description": "Agentic QE (external: maintained in proffesor-for-testing/agentic-qe, pinned to v3.15.
|
|
63
|
+
"description": "Agentic QE (external: maintained in proffesor-for-testing/agentic-qe, pinned to v3.15.13): a quality-engineering fleet with 11 QE agents, 9 skills, 9 commands, the agentic-qe MCP server pinned to the plugin version, and the aqe-mod guard that refuses destructive operations on AQE learning data"
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
"name": "ruflo-docs",
|
package/README.md
CHANGED
|
@@ -244,7 +244,7 @@ This adds slash commands and agent definitions. `ruflo-core` (installed above) d
|
|
|
244
244
|
| [**ruflo-browser**](plugins/ruflo-browser/README.md) | Automate browser testing with Playwright |
|
|
245
245
|
| [**ruflo-jujutsu**](plugins/ruflo-jujutsu/README.md) | Analyze git diffs, score risk, suggest reviewers |
|
|
246
246
|
| [**ruflo-docs**](plugins/ruflo-docs/README.md) | Generate and maintain documentation automatically |
|
|
247
|
-
| [**agentic-qe-fleet**](https://github.com/proffesor-for-testing/agentic-qe/tree/v3.15.
|
|
247
|
+
| [**agentic-qe-fleet**](https://github.com/proffesor-for-testing/agentic-qe/tree/v3.15.13/plugins/agentic-qe-fleet) | External, maintained by [Agentic QE](https://github.com/proffesor-for-testing/agentic-qe): 11 QE agents, test generation, coverage, chaos and TDD skills, the `agentic-qe` MCP server, and a guard for AQE learning data (`/plugin install agentic-qe-fleet@ruflo`). Install it from one marketplace only, `agentic-qe` or `ruflo`: enabled from both, its mod and commands load twice |
|
|
248
248
|
|
|
249
249
|
#### Security & Compliance
|
|
250
250
|
|
|
File without changes
|
|
File without changes
|
|
@@ -159,14 +159,14 @@ export declare const SpawnAgentSchema: z.ZodObject<{
|
|
|
159
159
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
160
160
|
}, "strip", z.ZodTypeAny, {
|
|
161
161
|
type: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
162
|
+
id?: string | undefined;
|
|
162
163
|
config?: Record<string, unknown> | undefined;
|
|
163
164
|
timeout?: number | undefined;
|
|
164
|
-
id?: string | undefined;
|
|
165
165
|
}, {
|
|
166
166
|
type: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
167
|
+
id?: string | undefined;
|
|
167
168
|
config?: Record<string, unknown> | undefined;
|
|
168
169
|
timeout?: number | undefined;
|
|
169
|
-
id?: string | undefined;
|
|
170
170
|
}>;
|
|
171
171
|
/**
|
|
172
172
|
* Task input schema
|
|
@@ -181,13 +181,13 @@ export declare const TaskInputSchema: z.ZodObject<{
|
|
|
181
181
|
taskId: string;
|
|
182
182
|
content: string;
|
|
183
183
|
agentType: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
184
|
-
priority?: "
|
|
184
|
+
priority?: "low" | "medium" | "high" | "critical" | undefined;
|
|
185
185
|
metadata?: Record<string, unknown> | undefined;
|
|
186
186
|
}, {
|
|
187
187
|
taskId: string;
|
|
188
188
|
content: string;
|
|
189
189
|
agentType: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
190
|
-
priority?: "
|
|
190
|
+
priority?: "low" | "medium" | "high" | "critical" | undefined;
|
|
191
191
|
metadata?: Record<string, unknown> | undefined;
|
|
192
192
|
}>;
|
|
193
193
|
/**
|
|
@@ -234,16 +234,16 @@ export declare const ExecutorConfigSchema: z.ZodObject<{
|
|
|
234
234
|
cwd: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
|
|
235
235
|
allowSudo: z.ZodDefault<z.ZodBoolean>;
|
|
236
236
|
}, "strip", z.ZodTypeAny, {
|
|
237
|
-
allowedCommands: string[];
|
|
238
237
|
timeout: number;
|
|
238
|
+
allowedCommands: string[];
|
|
239
239
|
maxBuffer: number;
|
|
240
240
|
allowSudo: boolean;
|
|
241
241
|
blockedPatterns?: string[] | undefined;
|
|
242
242
|
cwd?: string | undefined;
|
|
243
243
|
}, {
|
|
244
244
|
allowedCommands: string[];
|
|
245
|
-
blockedPatterns?: string[] | undefined;
|
|
246
245
|
timeout?: number | undefined;
|
|
246
|
+
blockedPatterns?: string[] | undefined;
|
|
247
247
|
maxBuffer?: number | undefined;
|
|
248
248
|
cwd?: string | undefined;
|
|
249
249
|
allowSudo?: boolean | undefined;
|
package/package.json
CHANGED
|
@@ -244,7 +244,7 @@ This adds slash commands and agent definitions. `ruflo-core` (installed above) d
|
|
|
244
244
|
| [**ruflo-browser**](plugins/ruflo-browser/README.md) | Automate browser testing with Playwright |
|
|
245
245
|
| [**ruflo-jujutsu**](plugins/ruflo-jujutsu/README.md) | Analyze git diffs, score risk, suggest reviewers |
|
|
246
246
|
| [**ruflo-docs**](plugins/ruflo-docs/README.md) | Generate and maintain documentation automatically |
|
|
247
|
-
| [**agentic-qe-fleet**](https://github.com/proffesor-for-testing/agentic-qe/tree/v3.15.
|
|
247
|
+
| [**agentic-qe-fleet**](https://github.com/proffesor-for-testing/agentic-qe/tree/v3.15.13/plugins/agentic-qe-fleet) | External, maintained by [Agentic QE](https://github.com/proffesor-for-testing/agentic-qe): 11 QE agents, test generation, coverage, chaos and TDD skills, the `agentic-qe` MCP server, and a guard for AQE learning data (`/plugin install agentic-qe-fleet@ruflo`). Install it from one marketplace only, `agentic-qe` or `ruflo`: enabled from both, its mod and commands load twice |
|
|
248
248
|
|
|
249
249
|
#### Security & Compliance
|
|
250
250
|
|
|
@@ -441,14 +441,19 @@ export const agentbbsTools = [
|
|
|
441
441
|
const logPath = roomLogPath(basePath, roomId);
|
|
442
442
|
const all = readEnvelopes(logPath);
|
|
443
443
|
let slice = all;
|
|
444
|
+
let resuming = false;
|
|
444
445
|
if (sinceEnvelopeId) {
|
|
445
446
|
const idx = all.findIndex(e => e.envelopeId === sinceEnvelopeId);
|
|
446
|
-
|
|
447
|
+
resuming = idx >= 0;
|
|
448
|
+
slice = resuming ? all.slice(idx + 1) : all;
|
|
447
449
|
}
|
|
448
450
|
// Dedup by envelopeId before applying the limit — a relay replay or a
|
|
449
451
|
// double-append must not surface the same envelope twice to a watcher.
|
|
450
452
|
const deduped = dedupEnvelopes(slice);
|
|
451
|
-
|
|
453
|
+
// A watcher resuming after sinceEnvelopeId gets the oldest envelopes
|
|
454
|
+
// first, so passing the last returned id pages through the rest when
|
|
455
|
+
// hasMore is set. Without a cursor, it gets the most recent window.
|
|
456
|
+
const envelopes = resuming ? deduped.slice(0, limit) : deduped.slice(-limit);
|
|
452
457
|
return {
|
|
453
458
|
success: true,
|
|
454
459
|
roomId,
|
|
@@ -54,6 +54,9 @@ async function maRequest(method, path, body, timeoutMs = 30_000) {
|
|
|
54
54
|
const key = apiKey();
|
|
55
55
|
if (!key)
|
|
56
56
|
return { ok: false, status: 0, error: NEEDS_KEY.error };
|
|
57
|
+
// encodeURIComponent leaves '.' and '..' alone, so an id of '..' would walk up the API path (/v1/sessions/.. is /v1/).
|
|
58
|
+
if (path.split('?')[0].split('/').some(segment => segment === '.' || segment === '..'))
|
|
59
|
+
return { ok: false, status: 0, error: 'Invalid managed resource id' };
|
|
57
60
|
try {
|
|
58
61
|
const res = await fetch(`${apiBase()}/v1${path}`, {
|
|
59
62
|
method, headers: headers(key), redirect: 'error',
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
|
@@ -0,0 +1,310 @@
|
|
|
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
|
+
// ===== Lazy loading =====
|
|
11
|
+
let diskannInstance = null;
|
|
12
|
+
let diskannAvailable = null;
|
|
13
|
+
let activeBackend = 'cosine-js';
|
|
14
|
+
/**
|
|
15
|
+
* Check if @ruvector/diskann is available
|
|
16
|
+
*/
|
|
17
|
+
export async function isDiskAnnAvailable() {
|
|
18
|
+
if (diskannAvailable !== null)
|
|
19
|
+
return diskannAvailable;
|
|
20
|
+
try {
|
|
21
|
+
const { createRequire } = await import('module');
|
|
22
|
+
const require2 = createRequire(import.meta.url);
|
|
23
|
+
const mod = require2('@ruvector/diskann');
|
|
24
|
+
diskannAvailable = typeof mod.DiskAnn === 'function';
|
|
25
|
+
return diskannAvailable;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
diskannAvailable = false;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create or get a DiskANN index instance
|
|
34
|
+
*/
|
|
35
|
+
export async function getDiskAnnIndex(config) {
|
|
36
|
+
if (diskannInstance)
|
|
37
|
+
return { index: diskannInstance, backend: activeBackend };
|
|
38
|
+
// Try DiskANN first
|
|
39
|
+
if (await isDiskAnnAvailable()) {
|
|
40
|
+
try {
|
|
41
|
+
const { createRequire } = await import('module');
|
|
42
|
+
const require2 = createRequire(import.meta.url);
|
|
43
|
+
const { DiskAnn } = require2('@ruvector/diskann');
|
|
44
|
+
const index = new DiskAnn({
|
|
45
|
+
dim: config.dim,
|
|
46
|
+
maxDegree: config.maxDegree ?? 64,
|
|
47
|
+
buildBeam: config.buildBeam ?? 128,
|
|
48
|
+
searchBeam: config.searchBeam ?? 64,
|
|
49
|
+
alpha: config.alpha ?? 1.2,
|
|
50
|
+
pqSubspaces: config.pqSubspaces ?? 0,
|
|
51
|
+
storagePath: config.storagePath,
|
|
52
|
+
});
|
|
53
|
+
diskannInstance = index;
|
|
54
|
+
activeBackend = 'diskann';
|
|
55
|
+
return { index, backend: 'diskann' };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Fall through
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Try HNSW (@ruvector/router VectorDb) as fallback
|
|
62
|
+
try {
|
|
63
|
+
const { createRequire } = await import('module');
|
|
64
|
+
const require2 = createRequire(import.meta.url);
|
|
65
|
+
const router = require2('@ruvector/router');
|
|
66
|
+
if (router.VectorDb && router.DistanceMetric) {
|
|
67
|
+
const index = new router.VectorDb({
|
|
68
|
+
dimensions: config.dim,
|
|
69
|
+
distanceMetric: router.DistanceMetric.Cosine,
|
|
70
|
+
hnswM: 16,
|
|
71
|
+
hnswEfConstruction: 200,
|
|
72
|
+
hnswEfSearch: 100,
|
|
73
|
+
});
|
|
74
|
+
diskannInstance = index;
|
|
75
|
+
activeBackend = 'hnsw';
|
|
76
|
+
return { index, backend: 'hnsw' };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Fall through
|
|
81
|
+
}
|
|
82
|
+
// Pure JS fallback
|
|
83
|
+
const jsIndex = createJsFallbackIndex(config.dim);
|
|
84
|
+
diskannInstance = jsIndex;
|
|
85
|
+
activeBackend = 'cosine-js';
|
|
86
|
+
return { index: jsIndex, backend: 'cosine-js' };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the active backend name
|
|
90
|
+
*/
|
|
91
|
+
export function getActiveBackend() {
|
|
92
|
+
return activeBackend;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Reset the index (for testing)
|
|
96
|
+
*/
|
|
97
|
+
export function resetIndex() {
|
|
98
|
+
diskannInstance = null;
|
|
99
|
+
diskannAvailable = null;
|
|
100
|
+
activeBackend = 'cosine-js';
|
|
101
|
+
}
|
|
102
|
+
// ===== Unified search interface =====
|
|
103
|
+
/**
|
|
104
|
+
* Insert a vector into the active backend
|
|
105
|
+
*/
|
|
106
|
+
export async function insertVector(id, vector, config = { dim: 384 }) {
|
|
107
|
+
const { index, backend } = await getDiskAnnIndex(config);
|
|
108
|
+
if (backend === 'diskann') {
|
|
109
|
+
index.insert(id, vector);
|
|
110
|
+
}
|
|
111
|
+
else if (backend === 'hnsw') {
|
|
112
|
+
index.insert(id, vector);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
index.insert(id, vector);
|
|
116
|
+
}
|
|
117
|
+
return { backend };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Build the index (required for DiskANN before search)
|
|
121
|
+
*/
|
|
122
|
+
export async function buildIndex(config = { dim: 384 }) {
|
|
123
|
+
const { index, backend } = await getDiskAnnIndex(config);
|
|
124
|
+
if (backend === 'diskann' && typeof index.build === 'function') {
|
|
125
|
+
index.build();
|
|
126
|
+
}
|
|
127
|
+
// HNSW and JS fallback don't need explicit build
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Search for k nearest neighbors
|
|
131
|
+
*/
|
|
132
|
+
export async function searchVectors(query, k, config = { dim: 384 }) {
|
|
133
|
+
const { index, backend } = await getDiskAnnIndex(config);
|
|
134
|
+
if (backend === 'diskann') {
|
|
135
|
+
const results = index.search(query, k);
|
|
136
|
+
return results.map((r) => ({
|
|
137
|
+
id: r.id,
|
|
138
|
+
distance: r.distance,
|
|
139
|
+
score: 1 / (1 + r.distance), // Convert L2 distance to similarity
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
if (backend === 'hnsw') {
|
|
143
|
+
const results = index.search(query, k);
|
|
144
|
+
return results.map((r) => ({
|
|
145
|
+
id: r.id,
|
|
146
|
+
distance: r.score, // VectorDb returns distance as 'score'
|
|
147
|
+
score: 1 / (1 + r.score),
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
// JS fallback
|
|
151
|
+
return index.search(query, k);
|
|
152
|
+
}
|
|
153
|
+
// ===== Pure JS fallback =====
|
|
154
|
+
function createJsFallbackIndex(dim) {
|
|
155
|
+
const vectors = new Map();
|
|
156
|
+
return {
|
|
157
|
+
insert(id, vector) {
|
|
158
|
+
vectors.set(id, new Float32Array(vector));
|
|
159
|
+
},
|
|
160
|
+
search(query, k) {
|
|
161
|
+
const results = [];
|
|
162
|
+
for (const [id, vec] of vectors) {
|
|
163
|
+
let dot = 0, normQ = 0, normV = 0;
|
|
164
|
+
for (let i = 0; i < dim; i++) {
|
|
165
|
+
dot += query[i] * vec[i];
|
|
166
|
+
normQ += query[i] * query[i];
|
|
167
|
+
normV += vec[i] * vec[i];
|
|
168
|
+
}
|
|
169
|
+
const cosine = dot / (Math.sqrt(normQ) * Math.sqrt(normV) || 1);
|
|
170
|
+
const distance = 1 - cosine;
|
|
171
|
+
results.push({ id, distance, score: cosine });
|
|
172
|
+
}
|
|
173
|
+
return results.sort((a, b) => a.distance - b.distance).slice(0, k);
|
|
174
|
+
},
|
|
175
|
+
count() { return vectors.size; },
|
|
176
|
+
delete(id) { return vectors.delete(id); },
|
|
177
|
+
build() { },
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Run a benchmark comparing available backends
|
|
182
|
+
*/
|
|
183
|
+
export async function benchmark(opts = {}) {
|
|
184
|
+
const dim = opts.dim ?? 384;
|
|
185
|
+
const n = opts.vectorCount ?? 1000;
|
|
186
|
+
const k = opts.k ?? 10;
|
|
187
|
+
const queryCount = opts.queries ?? 100;
|
|
188
|
+
const results = [];
|
|
189
|
+
// Generate random vectors
|
|
190
|
+
const vectors = [];
|
|
191
|
+
for (let i = 0; i < n; i++) {
|
|
192
|
+
const v = new Float32Array(dim);
|
|
193
|
+
for (let j = 0; j < dim; j++)
|
|
194
|
+
v[j] = Math.random() * 2 - 1;
|
|
195
|
+
// Normalize
|
|
196
|
+
let norm = 0;
|
|
197
|
+
for (let j = 0; j < dim; j++)
|
|
198
|
+
norm += v[j] * v[j];
|
|
199
|
+
norm = Math.sqrt(norm);
|
|
200
|
+
for (let j = 0; j < dim; j++)
|
|
201
|
+
v[j] /= norm;
|
|
202
|
+
vectors.push([`vec-${i}`, v]);
|
|
203
|
+
}
|
|
204
|
+
// Generate query vectors
|
|
205
|
+
const queries = [];
|
|
206
|
+
for (let i = 0; i < queryCount; i++) {
|
|
207
|
+
const q = new Float32Array(dim);
|
|
208
|
+
for (let j = 0; j < dim; j++)
|
|
209
|
+
q[j] = Math.random() * 2 - 1;
|
|
210
|
+
let norm = 0;
|
|
211
|
+
for (let j = 0; j < dim; j++)
|
|
212
|
+
norm += q[j] * q[j];
|
|
213
|
+
norm = Math.sqrt(norm);
|
|
214
|
+
for (let j = 0; j < dim; j++)
|
|
215
|
+
q[j] /= norm;
|
|
216
|
+
queries.push(q);
|
|
217
|
+
}
|
|
218
|
+
// Brute force ground truth
|
|
219
|
+
function bruteForceSearch(query) {
|
|
220
|
+
const scores = [];
|
|
221
|
+
for (const [id, vec] of vectors) {
|
|
222
|
+
let dist = 0;
|
|
223
|
+
for (let j = 0; j < dim; j++) {
|
|
224
|
+
const d = query[j] - vec[j];
|
|
225
|
+
dist += d * d;
|
|
226
|
+
}
|
|
227
|
+
scores.push({ id, dist });
|
|
228
|
+
}
|
|
229
|
+
scores.sort((a, b) => a.dist - b.dist);
|
|
230
|
+
return scores.slice(0, k).map(s => s.id);
|
|
231
|
+
}
|
|
232
|
+
const groundTruth = queries.map(q => bruteForceSearch(q));
|
|
233
|
+
// Test each available backend
|
|
234
|
+
for (const backendName of ['diskann', 'hnsw', 'cosine-js']) {
|
|
235
|
+
resetIndex();
|
|
236
|
+
try {
|
|
237
|
+
let index;
|
|
238
|
+
const memBefore = process.memoryUsage().heapUsed;
|
|
239
|
+
if (backendName === 'diskann') {
|
|
240
|
+
if (!(await isDiskAnnAvailable()))
|
|
241
|
+
continue;
|
|
242
|
+
const { createRequire } = await import('module');
|
|
243
|
+
const require2 = createRequire(import.meta.url);
|
|
244
|
+
const { DiskAnn } = require2('@ruvector/diskann');
|
|
245
|
+
index = new DiskAnn({ dim, maxDegree: 64, buildBeam: 128, searchBeam: 64 });
|
|
246
|
+
}
|
|
247
|
+
else if (backendName === 'hnsw') {
|
|
248
|
+
try {
|
|
249
|
+
const { createRequire } = await import('module');
|
|
250
|
+
const require2 = createRequire(import.meta.url);
|
|
251
|
+
const router = require2('@ruvector/router');
|
|
252
|
+
if (!router.VectorDb)
|
|
253
|
+
continue;
|
|
254
|
+
index = new router.VectorDb({ dimensions: dim, distanceMetric: router.DistanceMetric.Cosine });
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
index = createJsFallbackIndex(dim);
|
|
262
|
+
}
|
|
263
|
+
// Insert
|
|
264
|
+
const insertStart = performance.now();
|
|
265
|
+
for (const [id, vec] of vectors) {
|
|
266
|
+
index.insert(id, vec);
|
|
267
|
+
}
|
|
268
|
+
const insertTime = performance.now() - insertStart;
|
|
269
|
+
// Build
|
|
270
|
+
const buildStart = performance.now();
|
|
271
|
+
if (typeof index.build === 'function')
|
|
272
|
+
index.build();
|
|
273
|
+
const buildTime = performance.now() - buildStart;
|
|
274
|
+
// Search
|
|
275
|
+
const searchStart = performance.now();
|
|
276
|
+
const searchResults = [];
|
|
277
|
+
for (const q of queries) {
|
|
278
|
+
const r = index.search(q, k);
|
|
279
|
+
searchResults.push(r.map((x) => x.id));
|
|
280
|
+
}
|
|
281
|
+
const searchTime = performance.now() - searchStart;
|
|
282
|
+
// Recall vs ground truth
|
|
283
|
+
let totalRecall = 0;
|
|
284
|
+
for (let i = 0; i < queryCount; i++) {
|
|
285
|
+
const truth = new Set(groundTruth[i]);
|
|
286
|
+
const found = searchResults[i].filter(id => truth.has(id)).length;
|
|
287
|
+
totalRecall += found / k;
|
|
288
|
+
}
|
|
289
|
+
const recall = totalRecall / queryCount;
|
|
290
|
+
const memAfter = process.memoryUsage().heapUsed;
|
|
291
|
+
results.push({
|
|
292
|
+
backend: backendName,
|
|
293
|
+
dim,
|
|
294
|
+
vectorCount: n,
|
|
295
|
+
insertTimeMs: Math.round(insertTime * 100) / 100,
|
|
296
|
+
buildTimeMs: Math.round(buildTime * 100) / 100,
|
|
297
|
+
searchTimeMs: Math.round(searchTime * 100) / 100,
|
|
298
|
+
searchesPerSecond: Math.round(queryCount / (searchTime / 1000)),
|
|
299
|
+
recall: Math.round(recall * 1000) / 1000,
|
|
300
|
+
memoryMB: Math.round((memAfter - memBefore) / 1024 / 1024 * 100) / 100,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// Backend failed, skip
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
resetIndex();
|
|
308
|
+
return results;
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=diskann-backend.js.map
|
|
@@ -91,6 +91,11 @@ function atomicWriteJson(file, value) {
|
|
|
91
91
|
fs.closeSync(fd);
|
|
92
92
|
}
|
|
93
93
|
fs.renameSync(tmp, file);
|
|
94
|
+
// Windows cannot flush a directory handle (FlushFileBuffers fails with EPERM on NTFS), and there the journaled rename needs no such step.
|
|
95
|
+
// Skipping it there is the same rule policy-runtime's syncDirectory follows (#3398). On POSIX a failure here means the rename may not be
|
|
96
|
+
// durable, so it still reaches the caller (#4492).
|
|
97
|
+
if (process.platform === 'win32')
|
|
98
|
+
return;
|
|
94
99
|
const dirFd = fs.openSync(path.dirname(file), fs.constants.O_RDONLY);
|
|
95
100
|
try {
|
|
96
101
|
fs.fsyncSync(dirFd);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.63.
|
|
3
|
+
"version": "3.63.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",
|