agentic-qe 3.7.21 → 3.8.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/.claude/helpers/brain-checkpoint.cjs +4 -1
- package/.claude/helpers/statusline-v3.cjs +3 -1
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +45 -0
- package/README.md +2 -14
- package/assets/helpers/statusline-v3.cjs +3 -1
- package/dist/cli/brain-commands.js +6 -10
- package/dist/cli/bundle.js +7441 -4327
- package/dist/cli/commands/audit.d.ts +43 -0
- package/dist/cli/commands/audit.js +125 -0
- package/dist/cli/commands/hooks.js +29 -6
- package/dist/cli/commands/init.js +1 -73
- package/dist/cli/commands/learning.js +270 -13
- package/dist/cli/commands/ruvector-commands.d.ts +15 -0
- package/dist/cli/commands/ruvector-commands.js +271 -0
- package/dist/cli/handlers/init-handler.d.ts +0 -1
- package/dist/cli/handlers/init-handler.js +0 -6
- package/dist/cli/index.js +4 -2
- package/dist/context/sources/defect-source.js +2 -2
- package/dist/context/sources/memory-source.js +2 -2
- package/dist/context/sources/requirements-source.js +2 -2
- package/dist/coordination/behavior-tree/decorators.d.ts +108 -0
- package/dist/coordination/behavior-tree/decorators.js +251 -0
- package/dist/coordination/behavior-tree/index.d.ts +12 -0
- package/dist/coordination/behavior-tree/index.js +15 -0
- package/dist/coordination/behavior-tree/nodes.d.ts +165 -0
- package/dist/coordination/behavior-tree/nodes.js +338 -0
- package/dist/coordination/behavior-tree/qe-trees.d.ts +105 -0
- package/dist/coordination/behavior-tree/qe-trees.js +181 -0
- package/dist/coordination/coherence-action-gate.d.ts +284 -0
- package/dist/coordination/coherence-action-gate.js +512 -0
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.js +8 -0
- package/dist/coordination/reasoning-qec.d.ts +315 -0
- package/dist/coordination/reasoning-qec.js +585 -0
- package/dist/coordination/task-executor.d.ts +16 -0
- package/dist/coordination/task-executor.js +99 -0
- package/dist/coordination/workflow-orchestrator.d.ts +29 -0
- package/dist/coordination/workflow-orchestrator.js +42 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.d.ts +135 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.js +327 -0
- package/dist/domains/visual-accessibility/index.d.ts +1 -0
- package/dist/domains/visual-accessibility/index.js +4 -0
- package/dist/governance/coherence-validator.d.ts +112 -0
- package/dist/governance/coherence-validator.js +180 -0
- package/dist/governance/index.d.ts +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/witness-chain.d.ts +311 -0
- package/dist/governance/witness-chain.js +509 -0
- package/dist/init/index.d.ts +0 -2
- package/dist/init/index.js +0 -1
- package/dist/init/init-wizard-steps.d.ts +10 -0
- package/dist/init/init-wizard-steps.js +87 -1
- package/dist/init/init-wizard.d.ts +1 -9
- package/dist/init/init-wizard.js +3 -69
- package/dist/init/orchestrator.js +0 -1
- package/dist/init/phases/01-detection.js +0 -27
- package/dist/init/phases/07-hooks.js +6 -4
- package/dist/init/phases/phase-interface.d.ts +0 -1
- package/dist/init/settings-merge.js +1 -1
- package/dist/integrations/browser/qe-dashboard/clustering.d.ts +48 -0
- package/dist/integrations/browser/qe-dashboard/clustering.js +183 -0
- package/dist/integrations/browser/qe-dashboard/index.d.ts +12 -0
- package/dist/integrations/browser/qe-dashboard/index.js +15 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.d.ts +165 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.js +260 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.d.ts +144 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.js +277 -0
- package/dist/integrations/ruvector/cognitive-container-codec.d.ts +51 -0
- package/dist/integrations/ruvector/cognitive-container-codec.js +180 -0
- package/dist/integrations/ruvector/cognitive-container.d.ts +125 -0
- package/dist/integrations/ruvector/cognitive-container.js +306 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +309 -0
- package/dist/integrations/ruvector/coherence-gate.js +631 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.d.ts +176 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.js +301 -0
- package/dist/integrations/ruvector/dither-adapter.d.ts +122 -0
- package/dist/integrations/ruvector/dither-adapter.js +295 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +129 -0
- package/dist/integrations/ruvector/domain-transfer.js +220 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +214 -2
- package/dist/integrations/ruvector/feature-flags.js +167 -2
- package/dist/integrations/ruvector/filter-adapter.d.ts +71 -0
- package/dist/integrations/ruvector/filter-adapter.js +285 -0
- package/dist/integrations/ruvector/gnn-wrapper.d.ts +20 -0
- package/dist/integrations/ruvector/gnn-wrapper.js +40 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.d.ts +237 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.js +394 -0
- package/dist/integrations/ruvector/index.d.ts +8 -2
- package/dist/integrations/ruvector/index.js +18 -2
- package/dist/integrations/ruvector/interfaces.d.ts +40 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +54 -0
- package/dist/integrations/ruvector/sona-persistence.js +162 -0
- package/dist/integrations/ruvector/sona-three-loop.d.ts +392 -0
- package/dist/integrations/ruvector/sona-three-loop.js +814 -0
- package/dist/integrations/ruvector/sona-wrapper.d.ts +97 -0
- package/dist/integrations/ruvector/sona-wrapper.js +147 -3
- package/dist/integrations/ruvector/spectral-math.d.ts +101 -0
- package/dist/integrations/ruvector/spectral-math.js +254 -0
- package/dist/integrations/ruvector/temporal-compression.d.ts +163 -0
- package/dist/integrations/ruvector/temporal-compression.js +318 -0
- package/dist/integrations/ruvector/thompson-sampler.d.ts +61 -0
- package/dist/integrations/ruvector/thompson-sampler.js +118 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.d.ts +80 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.js +63 -0
- package/dist/integrations/ruvector/transfer-verification.d.ts +119 -0
- package/dist/integrations/ruvector/transfer-verification.js +115 -0
- package/dist/kernel/hnsw-adapter.d.ts +52 -1
- package/dist/kernel/hnsw-adapter.js +139 -4
- package/dist/kernel/hnsw-index-provider.d.ts +5 -0
- package/dist/kernel/native-hnsw-backend.d.ts +110 -0
- package/dist/kernel/native-hnsw-backend.js +408 -0
- package/dist/kernel/unified-memory.js +5 -6
- package/dist/learning/aqe-learning-engine.d.ts +2 -0
- package/dist/learning/aqe-learning-engine.js +65 -0
- package/dist/learning/experience-capture-middleware.js +20 -0
- package/dist/learning/experience-capture.d.ts +10 -0
- package/dist/learning/experience-capture.js +34 -0
- package/dist/learning/index.d.ts +2 -2
- package/dist/learning/index.js +4 -4
- package/dist/learning/metrics-tracker.d.ts +11 -0
- package/dist/learning/metrics-tracker.js +29 -13
- package/dist/learning/pattern-lifecycle.d.ts +30 -1
- package/dist/learning/pattern-lifecycle.js +92 -20
- package/dist/learning/pattern-store.d.ts +8 -0
- package/dist/learning/pattern-store.js +8 -2
- package/dist/learning/qe-unified-memory.js +1 -28
- package/dist/learning/regret-tracker.d.ts +201 -0
- package/dist/learning/regret-tracker.js +361 -0
- package/dist/mcp/bundle.js +5915 -474
- package/dist/routing/index.d.ts +4 -2
- package/dist/routing/index.js +3 -1
- package/dist/routing/neural-tiny-dancer-router.d.ts +268 -0
- package/dist/routing/neural-tiny-dancer-router.js +514 -0
- package/dist/routing/queen-integration.js +5 -5
- package/dist/routing/routing-config.d.ts +6 -0
- package/dist/routing/routing-config.js +1 -0
- package/dist/routing/simple-neural-router.d.ts +76 -0
- package/dist/routing/simple-neural-router.js +202 -0
- package/dist/routing/tiny-dancer-router.d.ts +20 -1
- package/dist/routing/tiny-dancer-router.js +21 -2
- package/dist/test-scheduling/dag-attention-scheduler.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-scheduler.js +358 -0
- package/dist/test-scheduling/dag-attention-types.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-types.js +10 -0
- package/dist/test-scheduling/index.d.ts +1 -0
- package/dist/test-scheduling/index.js +4 -0
- package/dist/test-scheduling/pipeline.d.ts +8 -0
- package/dist/test-scheduling/pipeline.js +28 -0
- package/package.json +6 -2
- package/dist/cli/commands/migrate.d.ts +0 -9
- package/dist/cli/commands/migrate.js +0 -566
- package/dist/init/init-wizard-migration.d.ts +0 -52
- package/dist/init/init-wizard-migration.js +0 -345
- package/dist/init/migration/config-migrator.d.ts +0 -31
- package/dist/init/migration/config-migrator.js +0 -149
- package/dist/init/migration/data-migrator.d.ts +0 -72
- package/dist/init/migration/data-migrator.js +0 -232
- package/dist/init/migration/detector.d.ts +0 -44
- package/dist/init/migration/detector.js +0 -105
- package/dist/init/migration/index.d.ts +0 -8
- package/dist/init/migration/index.js +0 -8
- package/dist/learning/v2-to-v3-migration.d.ts +0 -86
- package/dist/learning/v2-to-v3-migration.js +0 -529
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native HNSW Backend via @ruvector/router VectorDb
|
|
3
|
+
*
|
|
4
|
+
* Provides a high-performance HNSW index backed by the Rust-based
|
|
5
|
+
* @ruvector/router library. This backend implements the same
|
|
6
|
+
* IHnswIndexProvider interface as ProgressiveHnswBackend but delegates
|
|
7
|
+
* vector storage and search to native code for lower latency and
|
|
8
|
+
* higher throughput.
|
|
9
|
+
*
|
|
10
|
+
* When @ruvector/router is unavailable (e.g., unsupported platform,
|
|
11
|
+
* missing binary), construction throws NativeHnswUnavailableError so the
|
|
12
|
+
* caller (HnswAdapter factory) can fall back to the JS backend.
|
|
13
|
+
*
|
|
14
|
+
* @see ADR-081: Native HNSW NAPI Integration
|
|
15
|
+
* @module kernel/native-hnsw-backend
|
|
16
|
+
*/
|
|
17
|
+
import type { IHnswIndexProvider, SearchResult, HnswConfig } from './hnsw-index-provider.js';
|
|
18
|
+
/**
|
|
19
|
+
* Thrown when @ruvector/router native binary is not available.
|
|
20
|
+
* The HnswAdapter factory catches this to fall back to the JS backend.
|
|
21
|
+
*/
|
|
22
|
+
export declare class NativeHnswUnavailableError extends Error {
|
|
23
|
+
constructor(reason: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reset the native module loader state.
|
|
27
|
+
* Useful for testing fallback behavior.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resetNativeModuleLoader(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Check if the native module is available without throwing.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isNativeModuleAvailable(): boolean;
|
|
34
|
+
export interface NativeHnswMetrics {
|
|
35
|
+
totalSearches: number;
|
|
36
|
+
totalAdds: number;
|
|
37
|
+
totalRemoves: number;
|
|
38
|
+
avgSearchLatencyMs: number;
|
|
39
|
+
maxSearchLatencyMs: number;
|
|
40
|
+
lastSearchLatencyMs: number;
|
|
41
|
+
fallbackSearchCount: number;
|
|
42
|
+
bruteForceSearchCount: number;
|
|
43
|
+
nativeSearchCount: number;
|
|
44
|
+
fallbackRate: number;
|
|
45
|
+
allSearchesBruteForce: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Native HNSW backend using @ruvector/router VectorDb.
|
|
49
|
+
*
|
|
50
|
+
* Provides the same IHnswIndexProvider interface as ProgressiveHnswBackend
|
|
51
|
+
* but delegates all vector operations to a Rust-based HNSW implementation
|
|
52
|
+
* for improved performance.
|
|
53
|
+
*
|
|
54
|
+
* Note: @ruvector/router VectorDb uses string IDs while IHnswIndexProvider
|
|
55
|
+
* uses numeric IDs. Conversion is done via String(id) and Number(id).
|
|
56
|
+
* VectorDb search returns distance scores (lower = closer) which are
|
|
57
|
+
* converted to similarity scores (higher = closer) for consistency.
|
|
58
|
+
*/
|
|
59
|
+
export declare class NativeHnswBackend implements IHnswIndexProvider {
|
|
60
|
+
private readonly config;
|
|
61
|
+
private nativeDb;
|
|
62
|
+
private readonly metadataStore;
|
|
63
|
+
private readonly vectorStore;
|
|
64
|
+
private readonly normStore;
|
|
65
|
+
private operationLock;
|
|
66
|
+
private highFallbackWarningEmitted;
|
|
67
|
+
private _metrics;
|
|
68
|
+
/**
|
|
69
|
+
* Create a NativeHnswBackend.
|
|
70
|
+
*
|
|
71
|
+
* @param config - HNSW configuration overrides
|
|
72
|
+
* @throws {NativeHnswUnavailableError} If @ruvector/router is not available
|
|
73
|
+
*/
|
|
74
|
+
constructor(config?: Partial<HnswConfig>);
|
|
75
|
+
add(id: number, vector: Float32Array, metadata?: Record<string, unknown>): void;
|
|
76
|
+
search(query: Float32Array, k: number): SearchResult[];
|
|
77
|
+
remove(id: number): boolean;
|
|
78
|
+
size(): number;
|
|
79
|
+
dimensions(): number;
|
|
80
|
+
recall(): number;
|
|
81
|
+
/**
|
|
82
|
+
* Get performance metrics for this index.
|
|
83
|
+
*/
|
|
84
|
+
getMetrics(): Readonly<NativeHnswMetrics>;
|
|
85
|
+
/**
|
|
86
|
+
* Get last search latency in milliseconds.
|
|
87
|
+
*/
|
|
88
|
+
get lastSearchLatencyMs(): number;
|
|
89
|
+
/**
|
|
90
|
+
* Get the current configuration.
|
|
91
|
+
*/
|
|
92
|
+
getConfig(): Readonly<HnswConfig>;
|
|
93
|
+
/**
|
|
94
|
+
* Clear all vectors from the index.
|
|
95
|
+
* Deletes all entries from the native VectorDb.
|
|
96
|
+
*/
|
|
97
|
+
clear(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Check if the native backend is operational.
|
|
100
|
+
*/
|
|
101
|
+
isNativeAvailable(): boolean;
|
|
102
|
+
private normalizeVector;
|
|
103
|
+
private updateSearchMetrics;
|
|
104
|
+
/**
|
|
105
|
+
* Brute-force fallback search over locally stored vectors.
|
|
106
|
+
* Used when native search throws an unexpected error.
|
|
107
|
+
*/
|
|
108
|
+
private bruteForceSearch;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=native-hnsw-backend.d.ts.map
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native HNSW Backend via @ruvector/router VectorDb
|
|
3
|
+
*
|
|
4
|
+
* Provides a high-performance HNSW index backed by the Rust-based
|
|
5
|
+
* @ruvector/router library. This backend implements the same
|
|
6
|
+
* IHnswIndexProvider interface as ProgressiveHnswBackend but delegates
|
|
7
|
+
* vector storage and search to native code for lower latency and
|
|
8
|
+
* higher throughput.
|
|
9
|
+
*
|
|
10
|
+
* When @ruvector/router is unavailable (e.g., unsupported platform,
|
|
11
|
+
* missing binary), construction throws NativeHnswUnavailableError so the
|
|
12
|
+
* caller (HnswAdapter factory) can fall back to the JS backend.
|
|
13
|
+
*
|
|
14
|
+
* @see ADR-081: Native HNSW NAPI Integration
|
|
15
|
+
* @module kernel/native-hnsw-backend
|
|
16
|
+
*/
|
|
17
|
+
import { createRequire } from 'module';
|
|
18
|
+
import { DEFAULT_HNSW_CONFIG } from './hnsw-index-provider.js';
|
|
19
|
+
// Use createRequire for @ruvector/router (native CJS/NAPI module)
|
|
20
|
+
const esmRequire = createRequire(import.meta.url);
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Error Types
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Thrown when @ruvector/router native binary is not available.
|
|
26
|
+
* The HnswAdapter factory catches this to fall back to the JS backend.
|
|
27
|
+
*/
|
|
28
|
+
export class NativeHnswUnavailableError extends Error {
|
|
29
|
+
constructor(reason) {
|
|
30
|
+
super(`Native HNSW backend unavailable: ${reason}`);
|
|
31
|
+
this.name = 'NativeHnswUnavailableError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Native Module Loader
|
|
36
|
+
// ============================================================================
|
|
37
|
+
let nativeModule = null;
|
|
38
|
+
let nativeLoadAttempted = false;
|
|
39
|
+
let nativeLoadError = null;
|
|
40
|
+
/**
|
|
41
|
+
* Attempt to load the @ruvector/router native module.
|
|
42
|
+
* Caches the result so subsequent calls are instant.
|
|
43
|
+
*/
|
|
44
|
+
function loadNativeModule() {
|
|
45
|
+
if (nativeLoadAttempted) {
|
|
46
|
+
if (nativeModule)
|
|
47
|
+
return nativeModule;
|
|
48
|
+
throw new NativeHnswUnavailableError(nativeLoadError ?? 'Unknown load error');
|
|
49
|
+
}
|
|
50
|
+
nativeLoadAttempted = true;
|
|
51
|
+
try {
|
|
52
|
+
// Use createRequire for the optional native NAPI dependency
|
|
53
|
+
const mod = esmRequire('@ruvector/router');
|
|
54
|
+
// Verify the module has the expected API
|
|
55
|
+
if (!mod.VectorDb || !mod.DistanceMetric) {
|
|
56
|
+
throw new Error('@ruvector/router module missing VectorDb or DistanceMetric exports');
|
|
57
|
+
}
|
|
58
|
+
nativeModule = mod;
|
|
59
|
+
return nativeModule;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
63
|
+
nativeLoadError = message;
|
|
64
|
+
throw new NativeHnswUnavailableError(message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reset the native module loader state.
|
|
69
|
+
* Useful for testing fallback behavior.
|
|
70
|
+
*/
|
|
71
|
+
export function resetNativeModuleLoader() {
|
|
72
|
+
nativeModule = null;
|
|
73
|
+
nativeLoadAttempted = false;
|
|
74
|
+
nativeLoadError = null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if the native module is available without throwing.
|
|
78
|
+
*/
|
|
79
|
+
export function isNativeModuleAvailable() {
|
|
80
|
+
try {
|
|
81
|
+
loadNativeModule();
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Vector Math Helpers
|
|
90
|
+
// ============================================================================
|
|
91
|
+
function computeNorm(v) {
|
|
92
|
+
let sum = 0;
|
|
93
|
+
for (let i = 0; i < v.length; i++)
|
|
94
|
+
sum += v[i] * v[i];
|
|
95
|
+
return Math.sqrt(sum);
|
|
96
|
+
}
|
|
97
|
+
function fastCosineSimilarity(a, b, normA, normB) {
|
|
98
|
+
const denom = normA * normB;
|
|
99
|
+
if (denom === 0)
|
|
100
|
+
return 0;
|
|
101
|
+
let dot = 0;
|
|
102
|
+
const len = Math.min(a.length, b.length);
|
|
103
|
+
for (let i = 0; i < len; i++)
|
|
104
|
+
dot += a[i] * b[i];
|
|
105
|
+
return dot / denom;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resize a vector to the target dimensions.
|
|
109
|
+
* Shrink: average adjacent values. Grow: zero-pad.
|
|
110
|
+
*/
|
|
111
|
+
function resizeVector(vector, targetDim) {
|
|
112
|
+
if (vector.length === targetDim)
|
|
113
|
+
return vector;
|
|
114
|
+
const result = new Float32Array(targetDim);
|
|
115
|
+
if (vector.length > targetDim) {
|
|
116
|
+
const ratio = vector.length / targetDim;
|
|
117
|
+
for (let i = 0; i < targetDim; i++) {
|
|
118
|
+
const start = Math.floor(i * ratio);
|
|
119
|
+
const end = Math.floor((i + 1) * ratio);
|
|
120
|
+
let sum = 0;
|
|
121
|
+
for (let j = start; j < end; j++) {
|
|
122
|
+
sum += vector[j];
|
|
123
|
+
}
|
|
124
|
+
result[i] = sum / (end - start);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
for (let i = 0; i < vector.length; i++) {
|
|
129
|
+
result[i] = vector[i];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// NativeHnswBackend
|
|
136
|
+
// ============================================================================
|
|
137
|
+
/**
|
|
138
|
+
* Native HNSW backend using @ruvector/router VectorDb.
|
|
139
|
+
*
|
|
140
|
+
* Provides the same IHnswIndexProvider interface as ProgressiveHnswBackend
|
|
141
|
+
* but delegates all vector operations to a Rust-based HNSW implementation
|
|
142
|
+
* for improved performance.
|
|
143
|
+
*
|
|
144
|
+
* Note: @ruvector/router VectorDb uses string IDs while IHnswIndexProvider
|
|
145
|
+
* uses numeric IDs. Conversion is done via String(id) and Number(id).
|
|
146
|
+
* VectorDb search returns distance scores (lower = closer) which are
|
|
147
|
+
* converted to similarity scores (higher = closer) for consistency.
|
|
148
|
+
*/
|
|
149
|
+
export class NativeHnswBackend {
|
|
150
|
+
config;
|
|
151
|
+
nativeDb = null;
|
|
152
|
+
metadataStore = new Map();
|
|
153
|
+
vectorStore = new Map();
|
|
154
|
+
normStore = new Map();
|
|
155
|
+
operationLock = Promise.resolve();
|
|
156
|
+
highFallbackWarningEmitted = false;
|
|
157
|
+
_metrics = {
|
|
158
|
+
totalSearches: 0,
|
|
159
|
+
totalAdds: 0,
|
|
160
|
+
totalRemoves: 0,
|
|
161
|
+
avgSearchLatencyMs: 0,
|
|
162
|
+
maxSearchLatencyMs: 0,
|
|
163
|
+
lastSearchLatencyMs: 0,
|
|
164
|
+
fallbackSearchCount: 0,
|
|
165
|
+
bruteForceSearchCount: 0,
|
|
166
|
+
nativeSearchCount: 0,
|
|
167
|
+
fallbackRate: 0,
|
|
168
|
+
allSearchesBruteForce: false,
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Create a NativeHnswBackend.
|
|
172
|
+
*
|
|
173
|
+
* @param config - HNSW configuration overrides
|
|
174
|
+
* @throws {NativeHnswUnavailableError} If @ruvector/router is not available
|
|
175
|
+
*/
|
|
176
|
+
constructor(config) {
|
|
177
|
+
this.config = { ...DEFAULT_HNSW_CONFIG, ...config };
|
|
178
|
+
// Attempt to load native module and create VectorDb immediately.
|
|
179
|
+
// If the native module is unavailable or the database can't be opened,
|
|
180
|
+
// this throws NativeHnswUnavailableError so the factory can fall back
|
|
181
|
+
// to ProgressiveHnswBackend.
|
|
182
|
+
const native = loadNativeModule();
|
|
183
|
+
const distanceMetric = this.config.metric === 'cosine'
|
|
184
|
+
? native.DistanceMetric.Cosine
|
|
185
|
+
: native.DistanceMetric.Euclidean;
|
|
186
|
+
try {
|
|
187
|
+
this.nativeDb = new native.VectorDb({
|
|
188
|
+
dimensions: this.config.dimensions,
|
|
189
|
+
distanceMetric,
|
|
190
|
+
hnswM: this.config.M,
|
|
191
|
+
hnswEfConstruction: this.config.efConstruction,
|
|
192
|
+
hnswEfSearch: this.config.efSearch,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
197
|
+
throw new NativeHnswUnavailableError(`VectorDb creation failed: ${message}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// IHnswIndexProvider Implementation
|
|
202
|
+
// ============================================================================
|
|
203
|
+
add(id, vector, metadata) {
|
|
204
|
+
const normalized = this.normalizeVector(vector);
|
|
205
|
+
const norm = computeNorm(normalized);
|
|
206
|
+
// Remove existing entry if updating
|
|
207
|
+
if (this.vectorStore.has(id)) {
|
|
208
|
+
try {
|
|
209
|
+
this.nativeDb.delete(String(id));
|
|
210
|
+
}
|
|
211
|
+
catch { /* ignore if not found */ }
|
|
212
|
+
}
|
|
213
|
+
this.nativeDb.insert(String(id), normalized);
|
|
214
|
+
this.vectorStore.set(id, normalized);
|
|
215
|
+
this.normStore.set(id, norm);
|
|
216
|
+
if (metadata) {
|
|
217
|
+
this.metadataStore.set(id, metadata);
|
|
218
|
+
}
|
|
219
|
+
this._metrics.totalAdds++;
|
|
220
|
+
}
|
|
221
|
+
search(query, k) {
|
|
222
|
+
const start = performance.now();
|
|
223
|
+
if (this.vectorStore.size === 0)
|
|
224
|
+
return [];
|
|
225
|
+
const normalizedQuery = this.normalizeVector(query);
|
|
226
|
+
const queryNorm = computeNorm(normalizedQuery);
|
|
227
|
+
const actualK = Math.min(k, this.vectorStore.size);
|
|
228
|
+
let results;
|
|
229
|
+
try {
|
|
230
|
+
const nativeResults = this.nativeDb.search(normalizedQuery, actualK);
|
|
231
|
+
// Convert native distance scores to similarity scores.
|
|
232
|
+
// @ruvector/router returns { id: string, score: number } where
|
|
233
|
+
// score is a distance (lower = closer). We convert string IDs
|
|
234
|
+
// back to numbers and compute cosine similarity for consistency.
|
|
235
|
+
results = nativeResults.map((nr) => {
|
|
236
|
+
const numericId = Number(nr.id);
|
|
237
|
+
const storedVector = this.vectorStore.get(numericId);
|
|
238
|
+
const storedNorm = this.normStore.get(numericId) ?? 0;
|
|
239
|
+
// Compute cosine similarity for consistent scoring with ProgressiveHnswBackend
|
|
240
|
+
let score;
|
|
241
|
+
if (this.config.metric === 'cosine' && storedVector) {
|
|
242
|
+
score = fastCosineSimilarity(normalizedQuery, storedVector, queryNorm, storedNorm);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// For euclidean, negate the distance (higher = closer)
|
|
246
|
+
score = -nr.score;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
id: numericId,
|
|
250
|
+
score,
|
|
251
|
+
metadata: this.metadataStore.get(numericId),
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
// Sort by descending score for consistency
|
|
255
|
+
results.sort((a, b) => b.score - a.score);
|
|
256
|
+
this._metrics.nativeSearchCount++;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// If native search fails, fall back to brute-force over stored vectors
|
|
260
|
+
this._metrics.fallbackSearchCount++;
|
|
261
|
+
this._metrics.bruteForceSearchCount++;
|
|
262
|
+
console.warn(`[NativeHNSW] FALLBACK: Using brute-force linear scan (@ruvector/router search failed). Index size: ${this.vectorStore.size}`);
|
|
263
|
+
results = this.bruteForceSearch(normalizedQuery, queryNorm, actualK);
|
|
264
|
+
}
|
|
265
|
+
const elapsed = performance.now() - start;
|
|
266
|
+
this.updateSearchMetrics(elapsed);
|
|
267
|
+
// Compute derived fallback metrics
|
|
268
|
+
this._metrics.fallbackRate =
|
|
269
|
+
this._metrics.fallbackSearchCount / this._metrics.totalSearches;
|
|
270
|
+
this._metrics.allSearchesBruteForce =
|
|
271
|
+
this._metrics.nativeSearchCount === 0 && this._metrics.totalSearches > 0;
|
|
272
|
+
// One-time warning when fallback rate is dangerously high
|
|
273
|
+
if (!this.highFallbackWarningEmitted &&
|
|
274
|
+
this._metrics.fallbackRate > 0.5 &&
|
|
275
|
+
this._metrics.totalSearches >= 10) {
|
|
276
|
+
this.highFallbackWarningEmitted = true;
|
|
277
|
+
console.error(`[NativeHNSW] WARNING: ${(this._metrics.fallbackRate * 100).toFixed(0)}% of searches are using brute-force fallback. Native HNSW may not be functioning correctly. Consider rebuilding the index or checking @ruvector/router installation.`);
|
|
278
|
+
}
|
|
279
|
+
if (elapsed > 50) {
|
|
280
|
+
console.warn(`[NativeHNSW] search took ${elapsed.toFixed(1)}ms (k=${k}, results=${results.length})`);
|
|
281
|
+
}
|
|
282
|
+
return results;
|
|
283
|
+
}
|
|
284
|
+
remove(id) {
|
|
285
|
+
if (!this.vectorStore.has(id))
|
|
286
|
+
return false;
|
|
287
|
+
try {
|
|
288
|
+
this.nativeDb.delete(String(id));
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// Native remove failed; clean up local state anyway
|
|
292
|
+
}
|
|
293
|
+
this.vectorStore.delete(id);
|
|
294
|
+
this.normStore.delete(id);
|
|
295
|
+
this.metadataStore.delete(id);
|
|
296
|
+
this._metrics.totalRemoves++;
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
size() {
|
|
300
|
+
return this.vectorStore.size;
|
|
301
|
+
}
|
|
302
|
+
dimensions() {
|
|
303
|
+
return this.config.dimensions;
|
|
304
|
+
}
|
|
305
|
+
recall() {
|
|
306
|
+
// HNSW is approximate, estimate recall based on efSearch/M ratio
|
|
307
|
+
// Higher efSearch relative to M means better recall
|
|
308
|
+
const ratio = this.config.efSearch / this.config.M;
|
|
309
|
+
if (ratio >= 10)
|
|
310
|
+
return 0.99;
|
|
311
|
+
if (ratio >= 5)
|
|
312
|
+
return 0.97;
|
|
313
|
+
if (ratio >= 3)
|
|
314
|
+
return 0.95;
|
|
315
|
+
return 0.90;
|
|
316
|
+
}
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Public Utilities
|
|
319
|
+
// ============================================================================
|
|
320
|
+
/**
|
|
321
|
+
* Get performance metrics for this index.
|
|
322
|
+
*/
|
|
323
|
+
getMetrics() {
|
|
324
|
+
return { ...this._metrics };
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get last search latency in milliseconds.
|
|
328
|
+
*/
|
|
329
|
+
get lastSearchLatencyMs() {
|
|
330
|
+
return this._metrics.lastSearchLatencyMs;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get the current configuration.
|
|
334
|
+
*/
|
|
335
|
+
getConfig() {
|
|
336
|
+
return { ...this.config };
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Clear all vectors from the index.
|
|
340
|
+
* Deletes all entries from the native VectorDb.
|
|
341
|
+
*/
|
|
342
|
+
clear() {
|
|
343
|
+
// Delete all entries from native DB
|
|
344
|
+
for (const id of this.vectorStore.keys()) {
|
|
345
|
+
try {
|
|
346
|
+
this.nativeDb.delete(String(id));
|
|
347
|
+
}
|
|
348
|
+
catch { /* ignore */ }
|
|
349
|
+
}
|
|
350
|
+
this.vectorStore.clear();
|
|
351
|
+
this.normStore.clear();
|
|
352
|
+
this.metadataStore.clear();
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Check if the native backend is operational.
|
|
356
|
+
*/
|
|
357
|
+
isNativeAvailable() {
|
|
358
|
+
return this.nativeDb !== null;
|
|
359
|
+
}
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Private Helpers
|
|
362
|
+
// ============================================================================
|
|
363
|
+
normalizeVector(vector) {
|
|
364
|
+
if (vector.length === this.config.dimensions)
|
|
365
|
+
return vector;
|
|
366
|
+
return resizeVector(vector, this.config.dimensions);
|
|
367
|
+
}
|
|
368
|
+
updateSearchMetrics(latencyMs) {
|
|
369
|
+
this._metrics.totalSearches++;
|
|
370
|
+
this._metrics.lastSearchLatencyMs = latencyMs;
|
|
371
|
+
if (latencyMs > this._metrics.maxSearchLatencyMs) {
|
|
372
|
+
this._metrics.maxSearchLatencyMs = latencyMs;
|
|
373
|
+
}
|
|
374
|
+
// Running average
|
|
375
|
+
const n = this._metrics.totalSearches;
|
|
376
|
+
this._metrics.avgSearchLatencyMs =
|
|
377
|
+
this._metrics.avgSearchLatencyMs * ((n - 1) / n) + latencyMs / n;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Brute-force fallback search over locally stored vectors.
|
|
381
|
+
* Used when native search throws an unexpected error.
|
|
382
|
+
*/
|
|
383
|
+
bruteForceSearch(query, queryNorm, k) {
|
|
384
|
+
const scored = [];
|
|
385
|
+
for (const [id, vector] of this.vectorStore) {
|
|
386
|
+
const norm = this.normStore.get(id) ?? computeNorm(vector);
|
|
387
|
+
const score = this.config.metric === 'cosine'
|
|
388
|
+
? fastCosineSimilarity(query, vector, queryNorm, norm)
|
|
389
|
+
: -(function () {
|
|
390
|
+
let sum = 0;
|
|
391
|
+
const len = Math.min(query.length, vector.length);
|
|
392
|
+
for (let i = 0; i < len; i++) {
|
|
393
|
+
const diff = query[i] - vector[i];
|
|
394
|
+
sum += diff * diff;
|
|
395
|
+
}
|
|
396
|
+
return Math.sqrt(sum);
|
|
397
|
+
})();
|
|
398
|
+
scored.push({
|
|
399
|
+
id,
|
|
400
|
+
score,
|
|
401
|
+
metadata: this.metadataStore.get(id),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
scored.sort((a, b) => b.score - a.score);
|
|
405
|
+
return scored.slice(0, k);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
//# sourceMappingURL=native-hnsw-backend.js.map
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* - unified-memory-hnsw.ts: HNSW index + BinaryHeap
|
|
22
22
|
* - unified-memory.ts: UnifiedMemoryManager class (this file, facade)
|
|
23
23
|
*/
|
|
24
|
-
import
|
|
24
|
+
import { openDatabase as openSafeDatabase } from '../shared/safe-db.js';
|
|
25
25
|
import { safeJsonParse } from '../shared/safe-json.js';
|
|
26
26
|
import { toErrorMessage } from '../shared/error-utils.js';
|
|
27
27
|
import * as fs from 'fs';
|
|
@@ -278,13 +278,12 @@ export class UnifiedMemoryManager {
|
|
|
278
278
|
dbSizeBefore = newest.size;
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
|
-
this.db =
|
|
282
|
-
|
|
283
|
-
this.
|
|
284
|
-
}
|
|
281
|
+
this.db = openSafeDatabase(this.config.dbPath, {
|
|
282
|
+
walMode: this.config.walMode,
|
|
283
|
+
busyTimeout: this.config.busyTimeout,
|
|
284
|
+
});
|
|
285
285
|
this.db.pragma(`mmap_size = ${this.config.mmapSize}`);
|
|
286
286
|
this.db.pragma(`cache_size = ${this.config.cacheSize}`);
|
|
287
|
-
this.db.pragma(`busy_timeout = ${this.config.busyTimeout}`);
|
|
288
287
|
this.db.pragma('foreign_keys = ON');
|
|
289
288
|
await this.runMigrations();
|
|
290
289
|
// DATA LOSS PREVENTION: After migration, if the DB existed before and was
|
|
@@ -123,6 +123,8 @@ export declare class AQELearningEngine {
|
|
|
123
123
|
private patternStore?;
|
|
124
124
|
private coherenceService?;
|
|
125
125
|
private initialized;
|
|
126
|
+
private _domainTransferEngine;
|
|
127
|
+
private _domainTransferInterval;
|
|
126
128
|
private activeTasks;
|
|
127
129
|
private completedTasks;
|
|
128
130
|
private claudeFlowErrors;
|
|
@@ -73,6 +73,9 @@ export class AQELearningEngine {
|
|
|
73
73
|
patternStore;
|
|
74
74
|
coherenceService;
|
|
75
75
|
initialized = false;
|
|
76
|
+
// Cross-domain transfer (Y-2 wiring)
|
|
77
|
+
_domainTransferEngine = null;
|
|
78
|
+
_domainTransferInterval = null;
|
|
76
79
|
// Task tracking
|
|
77
80
|
activeTasks = new Map();
|
|
78
81
|
completedTasks = 0;
|
|
@@ -125,6 +128,63 @@ export class AQELearningEngine {
|
|
|
125
128
|
promotionThreshold: this.config.promotionThreshold,
|
|
126
129
|
});
|
|
127
130
|
await this.experienceCapture.initialize();
|
|
131
|
+
// Wire persistent witness chain when feature flag is on
|
|
132
|
+
try {
|
|
133
|
+
const { isWitnessChainFeatureEnabled } = await import('../governance/witness-chain.js');
|
|
134
|
+
if (isWitnessChainFeatureEnabled()) {
|
|
135
|
+
const { createPersistentWitnessChain, createWitnessChainSQLitePersistence } = await import('../governance/witness-chain.js');
|
|
136
|
+
const { getUnifiedPersistence } = await import('../kernel/unified-persistence.js');
|
|
137
|
+
const persistence = getUnifiedPersistence();
|
|
138
|
+
if (persistence.isInitialized()) {
|
|
139
|
+
const db = persistence.getDatabase();
|
|
140
|
+
const chainPersistence = createWitnessChainSQLitePersistence(db);
|
|
141
|
+
const chain = createPersistentWitnessChain(chainPersistence);
|
|
142
|
+
this.experienceCapture.setWitnessChain(chain);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
if (process.env.DEBUG)
|
|
148
|
+
console.debug('[AQELearningEngine] Witness chain wiring skipped:', e instanceof Error ? e.message : e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Wire cross-domain transfer with real performance provider (Y-2 fix)
|
|
152
|
+
try {
|
|
153
|
+
const { isCrossDomainTransferEnabled } = await import('../integrations/ruvector/feature-flags.js');
|
|
154
|
+
if (isCrossDomainTransferEnabled() && this.patternStore) {
|
|
155
|
+
const { createDomainTransferEngine } = await import('../integrations/ruvector/domain-transfer.js');
|
|
156
|
+
const transferEngine = createDomainTransferEngine();
|
|
157
|
+
// Pre-fetch stats for the synchronous performance provider callback
|
|
158
|
+
const store = this.patternStore;
|
|
159
|
+
let cachedStats = await store.getStats();
|
|
160
|
+
// Refresh cache periodically in background
|
|
161
|
+
this._domainTransferInterval = setInterval(async () => {
|
|
162
|
+
try {
|
|
163
|
+
cachedStats = await store.getStats();
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
if (process.env.DEBUG)
|
|
167
|
+
console.debug('[AQELearningEngine] Stats refresh failed:', e instanceof Error ? e.message : e);
|
|
168
|
+
}
|
|
169
|
+
}, 60_000);
|
|
170
|
+
this._domainTransferInterval.unref?.();
|
|
171
|
+
transferEngine.setPerformanceProvider((domain) => {
|
|
172
|
+
const domainPatterns = cachedStats.byDomain?.[domain] ?? 0;
|
|
173
|
+
const totalPatterns = cachedStats.totalPatterns ?? 0;
|
|
174
|
+
return {
|
|
175
|
+
domain,
|
|
176
|
+
successRate: totalPatterns > 0 ? domainPatterns / totalPatterns : 0.5,
|
|
177
|
+
avgConfidence: cachedStats.avgConfidence ?? 0.5,
|
|
178
|
+
patternCount: domainPatterns,
|
|
179
|
+
timestamp: Date.now(),
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
this._domainTransferEngine = transferEngine;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
if (process.env.DEBUG)
|
|
187
|
+
console.debug('[AQELearningEngine] Domain transfer wiring skipped:', e instanceof Error ? e.message : e);
|
|
128
188
|
}
|
|
129
189
|
// Try to initialize Claude Flow bridge (optional)
|
|
130
190
|
if (this.config.enableClaudeFlow) {
|
|
@@ -802,6 +862,11 @@ export class AQELearningEngine {
|
|
|
802
862
|
* Dispose the engine
|
|
803
863
|
*/
|
|
804
864
|
async dispose() {
|
|
865
|
+
if (this._domainTransferInterval) {
|
|
866
|
+
clearInterval(this._domainTransferInterval);
|
|
867
|
+
this._domainTransferInterval = null;
|
|
868
|
+
}
|
|
869
|
+
this._domainTransferEngine = null;
|
|
805
870
|
if (this.experienceCapture) {
|
|
806
871
|
await this.experienceCapture.dispose();
|
|
807
872
|
}
|
|
@@ -170,6 +170,26 @@ async function doInitialize() {
|
|
|
170
170
|
CREATE INDEX IF NOT EXISTS idx_captured_exp_agent ON captured_experiences(agent);
|
|
171
171
|
CREATE INDEX IF NOT EXISTS idx_captured_exp_completed ON captured_experiences(completed_at DESC);
|
|
172
172
|
`);
|
|
173
|
+
// Add consolidation columns if missing (needed by ExperienceConsolidator)
|
|
174
|
+
const colNames = new Set(db.prepare('PRAGMA table_info(captured_experiences)').all().map(c => c.name));
|
|
175
|
+
const consolidationCols = [
|
|
176
|
+
['application_count', 'INTEGER DEFAULT 0'],
|
|
177
|
+
['avg_token_savings', 'REAL DEFAULT 0'],
|
|
178
|
+
['embedding', 'BLOB'],
|
|
179
|
+
['embedding_dimension', 'INTEGER'],
|
|
180
|
+
['tags', 'TEXT'],
|
|
181
|
+
['last_applied_at', 'TEXT'],
|
|
182
|
+
['consolidated_into', 'TEXT DEFAULT NULL'],
|
|
183
|
+
['consolidation_count', 'INTEGER DEFAULT 1'],
|
|
184
|
+
['quality_updated_at', 'TEXT DEFAULT NULL'],
|
|
185
|
+
['reuse_success_count', 'INTEGER DEFAULT 0'],
|
|
186
|
+
['reuse_failure_count', 'INTEGER DEFAULT 0'],
|
|
187
|
+
];
|
|
188
|
+
for (const [col, def] of consolidationCols) {
|
|
189
|
+
if (!colNames.has(col)) {
|
|
190
|
+
db.exec(`ALTER TABLE captured_experiences ADD COLUMN ${col} ${def}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
173
193
|
}
|
|
174
194
|
// Start periodic cleanup of stale experiences
|
|
175
195
|
startCleanupTimer();
|