agentic-qe 1.9.3 → 2.0.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/agents/qe-api-contract-validator.md +95 -1336
- package/.claude/agents/qe-chaos-engineer.md +152 -1211
- package/.claude/agents/qe-code-complexity.md +144 -707
- package/.claude/agents/qe-coverage-analyzer.md +147 -743
- package/.claude/agents/qe-deployment-readiness.md +143 -1496
- package/.claude/agents/qe-flaky-test-hunter.md +132 -1529
- package/.claude/agents/qe-fleet-commander.md +12 -12
- package/.claude/agents/qe-performance-tester.md +150 -886
- package/.claude/agents/qe-production-intelligence.md +155 -1396
- package/.claude/agents/qe-quality-analyzer.md +6 -6
- package/.claude/agents/qe-quality-gate.md +151 -648
- package/.claude/agents/qe-regression-risk-analyzer.md +132 -1150
- package/.claude/agents/qe-requirements-validator.md +149 -932
- package/.claude/agents/qe-security-scanner.md +157 -797
- package/.claude/agents/qe-test-data-architect.md +96 -1365
- package/.claude/agents/qe-test-executor.md +8 -8
- package/.claude/agents/qe-test-generator.md +145 -1540
- package/.claude/agents/qe-visual-tester.md +153 -1257
- package/.claude/agents/qx-partner.md +235 -0
- package/.claude/agents/subagents/qe-code-reviewer.md +40 -136
- package/.claude/agents/subagents/qe-coverage-gap-analyzer.md +40 -480
- package/.claude/agents/subagents/qe-data-generator.md +41 -125
- package/.claude/agents/subagents/qe-flaky-investigator.md +55 -411
- package/.claude/agents/subagents/qe-integration-tester.md +53 -141
- package/.claude/agents/subagents/qe-performance-validator.md +54 -130
- package/.claude/agents/subagents/qe-security-auditor.md +56 -114
- package/.claude/agents/subagents/qe-test-data-architect-sub.md +57 -548
- package/.claude/agents/subagents/qe-test-implementer.md +58 -551
- package/.claude/agents/subagents/qe-test-refactorer.md +65 -722
- package/.claude/agents/subagents/qe-test-writer.md +63 -726
- package/.claude/skills/skills-manifest.json +632 -0
- package/.claude/skills/testability-scoring/README.md +71 -0
- package/.claude/skills/testability-scoring/SKILL.md +611 -0
- package/.claude/skills/testability-scoring/resources/templates/config.template.js +84 -0
- package/.claude/skills/testability-scoring/resources/templates/testability-scoring.spec.template.js +532 -0
- package/.claude/skills/testability-scoring/scripts/generate-html-report.js +1007 -0
- package/.claude/skills/testability-scoring/scripts/run-assessment.sh +70 -0
- package/CHANGELOG.md +116 -0
- package/README.md +59 -7
- package/config/.env.otel.example +25 -0
- package/config/OTEL-QUICK-REFERENCE.md +137 -0
- package/config/README-OTEL.md +222 -0
- package/config/alerting-rules.yml +518 -0
- package/config/docker-compose.otel.yml +187 -0
- package/config/grafana/dashboards/agentic-qe-overview.json +286 -0
- package/config/grafana/provisioning/dashboards/dashboards.yml +19 -0
- package/config/grafana/provisioning/datasources/datasources.yml +53 -0
- package/config/otel-collector-config.yaml.example +145 -0
- package/config/prometheus.yml.example +106 -0
- package/dist/agents/QXPartnerAgent.d.ts +139 -0
- package/dist/agents/QXPartnerAgent.d.ts.map +1 -0
- package/dist/agents/QXPartnerAgent.js +769 -0
- package/dist/agents/QXPartnerAgent.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +82 -2
- package/dist/agents/index.js.map +1 -1
- package/dist/alerting/AlertManager.d.ts +120 -0
- package/dist/alerting/AlertManager.d.ts.map +1 -0
- package/dist/alerting/AlertManager.js +345 -0
- package/dist/alerting/AlertManager.js.map +1 -0
- package/dist/alerting/FeedbackRouter.d.ts +98 -0
- package/dist/alerting/FeedbackRouter.d.ts.map +1 -0
- package/dist/alerting/FeedbackRouter.js +331 -0
- package/dist/alerting/FeedbackRouter.js.map +1 -0
- package/dist/alerting/StrategyApplicator.d.ts +120 -0
- package/dist/alerting/StrategyApplicator.d.ts.map +1 -0
- package/dist/alerting/StrategyApplicator.js +299 -0
- package/dist/alerting/StrategyApplicator.js.map +1 -0
- package/dist/alerting/index.d.ts +68 -0
- package/dist/alerting/index.d.ts.map +1 -0
- package/dist/alerting/index.js +112 -0
- package/dist/alerting/index.js.map +1 -0
- package/dist/alerting/types.d.ts +118 -0
- package/dist/alerting/types.d.ts.map +1 -0
- package/dist/alerting/types.js +11 -0
- package/dist/alerting/types.js.map +1 -0
- package/dist/cli/commands/debug/agent.d.ts.map +1 -1
- package/dist/cli/commands/debug/agent.js +19 -6
- package/dist/cli/commands/debug/agent.js.map +1 -1
- package/dist/cli/commands/debug/health-check.js +20 -7
- package/dist/cli/commands/debug/health-check.js.map +1 -1
- package/dist/cli/commands/init-claude-md-template.d.ts +1 -0
- package/dist/cli/commands/init-claude-md-template.d.ts.map +1 -1
- package/dist/cli/commands/init-claude-md-template.js +4 -3
- package/dist/cli/commands/init-claude-md-template.js.map +1 -1
- package/dist/cli/commands/workflow/cancel.d.ts.map +1 -1
- package/dist/cli/commands/workflow/cancel.js +4 -3
- package/dist/cli/commands/workflow/cancel.js.map +1 -1
- package/dist/cli/commands/workflow/list.d.ts.map +1 -1
- package/dist/cli/commands/workflow/list.js +4 -3
- package/dist/cli/commands/workflow/list.js.map +1 -1
- package/dist/cli/commands/workflow/pause.d.ts.map +1 -1
- package/dist/cli/commands/workflow/pause.js +4 -3
- package/dist/cli/commands/workflow/pause.js.map +1 -1
- package/dist/cli/init/claude-config.d.ts.map +1 -1
- package/dist/cli/init/claude-config.js +13 -13
- package/dist/cli/init/claude-config.js.map +1 -1
- package/dist/cli/init/claude-md.d.ts.map +1 -1
- package/dist/cli/init/claude-md.js +44 -2
- package/dist/cli/init/claude-md.js.map +1 -1
- package/dist/cli/init/database-init.js +1 -1
- package/dist/cli/init/index.d.ts.map +1 -1
- package/dist/cli/init/index.js +13 -6
- package/dist/cli/init/index.js.map +1 -1
- package/dist/cli/init/skills.d.ts.map +1 -1
- package/dist/cli/init/skills.js +2 -1
- package/dist/cli/init/skills.js.map +1 -1
- package/dist/core/memory/AgentDBIntegration.d.ts +24 -6
- package/dist/core/memory/AgentDBIntegration.d.ts.map +1 -1
- package/dist/core/memory/AgentDBIntegration.js +66 -10
- package/dist/core/memory/AgentDBIntegration.js.map +1 -1
- package/dist/core/memory/IPatternStore.d.ts +209 -0
- package/dist/core/memory/IPatternStore.d.ts.map +1 -0
- package/dist/core/memory/IPatternStore.js +15 -0
- package/dist/core/memory/IPatternStore.js.map +1 -0
- package/dist/core/memory/MigrationTools.d.ts +192 -0
- package/dist/core/memory/MigrationTools.d.ts.map +1 -0
- package/dist/core/memory/MigrationTools.js +615 -0
- package/dist/core/memory/MigrationTools.js.map +1 -0
- package/dist/core/memory/NeuralEnhancement.d.ts +154 -0
- package/dist/core/memory/NeuralEnhancement.d.ts.map +1 -0
- package/dist/core/memory/NeuralEnhancement.js +598 -0
- package/dist/core/memory/NeuralEnhancement.js.map +1 -0
- package/dist/core/memory/PatternStoreFactory.d.ts +143 -0
- package/dist/core/memory/PatternStoreFactory.d.ts.map +1 -0
- package/dist/core/memory/PatternStoreFactory.js +370 -0
- package/dist/core/memory/PatternStoreFactory.js.map +1 -0
- package/dist/core/memory/RealAgentDBAdapter.d.ts +1 -0
- package/dist/core/memory/RealAgentDBAdapter.d.ts.map +1 -1
- package/dist/core/memory/RealAgentDBAdapter.js +28 -20
- package/dist/core/memory/RealAgentDBAdapter.js.map +1 -1
- package/dist/core/memory/RuVectorPatternStore.d.ts +198 -0
- package/dist/core/memory/RuVectorPatternStore.d.ts.map +1 -0
- package/dist/core/memory/RuVectorPatternStore.js +605 -0
- package/dist/core/memory/RuVectorPatternStore.js.map +1 -0
- package/dist/core/memory/SelfHealingMonitor.d.ts +186 -0
- package/dist/core/memory/SelfHealingMonitor.d.ts.map +1 -0
- package/dist/core/memory/SelfHealingMonitor.js +451 -0
- package/dist/core/memory/SelfHealingMonitor.js.map +1 -0
- package/dist/core/memory/SwarmMemoryManager.d.ts +62 -0
- package/dist/core/memory/SwarmMemoryManager.d.ts.map +1 -1
- package/dist/core/memory/SwarmMemoryManager.js +97 -0
- package/dist/core/memory/SwarmMemoryManager.js.map +1 -1
- package/dist/core/memory/UnifiedMemoryCoordinator.d.ts +341 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.d.ts.map +1 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.js +986 -0
- package/dist/core/memory/UnifiedMemoryCoordinator.js.map +1 -0
- package/dist/core/memory/index.d.ts +16 -0
- package/dist/core/memory/index.d.ts.map +1 -1
- package/dist/core/memory/index.js +58 -1
- package/dist/core/memory/index.js.map +1 -1
- package/dist/core/optimization/SwarmOptimizer.d.ts +185 -0
- package/dist/core/optimization/SwarmOptimizer.d.ts.map +1 -0
- package/dist/core/optimization/SwarmOptimizer.js +631 -0
- package/dist/core/optimization/SwarmOptimizer.js.map +1 -0
- package/dist/core/optimization/index.d.ts +9 -0
- package/dist/core/optimization/index.d.ts.map +1 -0
- package/dist/core/optimization/index.js +25 -0
- package/dist/core/optimization/index.js.map +1 -0
- package/dist/core/optimization/types.d.ts +53 -0
- package/dist/core/optimization/types.d.ts.map +1 -0
- package/dist/core/optimization/types.js +6 -0
- package/dist/core/optimization/types.js.map +1 -0
- package/dist/core/orchestration/PriorityQueue.d.ts +54 -0
- package/dist/core/orchestration/PriorityQueue.d.ts.map +1 -0
- package/dist/core/orchestration/PriorityQueue.js +122 -0
- package/dist/core/orchestration/PriorityQueue.js.map +1 -0
- package/dist/core/orchestration/WorkflowOrchestrator.d.ts +176 -0
- package/dist/core/orchestration/WorkflowOrchestrator.d.ts.map +1 -0
- package/dist/core/orchestration/WorkflowOrchestrator.js +813 -0
- package/dist/core/orchestration/WorkflowOrchestrator.js.map +1 -0
- package/dist/core/orchestration/index.d.ts +7 -0
- package/dist/core/orchestration/index.d.ts.map +1 -0
- package/dist/core/orchestration/index.js +11 -0
- package/dist/core/orchestration/index.js.map +1 -0
- package/dist/core/orchestration/types.d.ts +96 -0
- package/dist/core/orchestration/types.d.ts.map +1 -0
- package/dist/core/orchestration/types.js +6 -0
- package/dist/core/orchestration/types.js.map +1 -0
- package/dist/core/skills/DynamicSkillLoader.d.ts +96 -0
- package/dist/core/skills/DynamicSkillLoader.d.ts.map +1 -0
- package/dist/core/skills/DynamicSkillLoader.js +353 -0
- package/dist/core/skills/DynamicSkillLoader.js.map +1 -0
- package/dist/core/skills/types.d.ts +118 -0
- package/dist/core/skills/types.d.ts.map +1 -0
- package/dist/core/skills/types.js +7 -0
- package/dist/core/skills/types.js.map +1 -0
- package/dist/core/transport/QUICTransport.d.ts +320 -0
- package/dist/core/transport/QUICTransport.d.ts.map +1 -0
- package/dist/core/transport/QUICTransport.js +711 -0
- package/dist/core/transport/QUICTransport.js.map +1 -0
- package/dist/core/transport/index.d.ts +40 -0
- package/dist/core/transport/index.d.ts.map +1 -0
- package/dist/core/transport/index.js +46 -0
- package/dist/core/transport/index.js.map +1 -0
- package/dist/core/transport/quic-loader.d.ts +123 -0
- package/dist/core/transport/quic-loader.d.ts.map +1 -0
- package/dist/core/transport/quic-loader.js +293 -0
- package/dist/core/transport/quic-loader.js.map +1 -0
- package/dist/core/transport/quic.d.ts +154 -0
- package/dist/core/transport/quic.d.ts.map +1 -0
- package/dist/core/transport/quic.js +214 -0
- package/dist/core/transport/quic.js.map +1 -0
- package/dist/mcp/services/AgentRegistry.d.ts.map +1 -1
- package/dist/mcp/services/AgentRegistry.js +4 -1
- package/dist/mcp/services/AgentRegistry.js.map +1 -1
- package/dist/reasoning/RuVectorReasoningAdapter.d.ts +232 -0
- package/dist/reasoning/RuVectorReasoningAdapter.d.ts.map +1 -0
- package/dist/reasoning/RuVectorReasoningAdapter.js +585 -0
- package/dist/reasoning/RuVectorReasoningAdapter.js.map +1 -0
- package/dist/reasoning/index.d.ts +2 -0
- package/dist/reasoning/index.d.ts.map +1 -1
- package/dist/reasoning/index.js +6 -1
- package/dist/reasoning/index.js.map +1 -1
- package/dist/reporting/ResultAggregator.d.ts +107 -0
- package/dist/reporting/ResultAggregator.d.ts.map +1 -0
- package/dist/reporting/ResultAggregator.js +435 -0
- package/dist/reporting/ResultAggregator.js.map +1 -0
- package/dist/reporting/index.d.ts +48 -0
- package/dist/reporting/index.d.ts.map +1 -0
- package/dist/reporting/index.js +154 -0
- package/dist/reporting/index.js.map +1 -0
- package/dist/reporting/reporters/ControlLoopReporter.d.ts +128 -0
- package/dist/reporting/reporters/ControlLoopReporter.d.ts.map +1 -0
- package/dist/reporting/reporters/ControlLoopReporter.js +417 -0
- package/dist/reporting/reporters/ControlLoopReporter.js.map +1 -0
- package/dist/reporting/reporters/HumanReadableReporter.d.ts +140 -0
- package/dist/reporting/reporters/HumanReadableReporter.d.ts.map +1 -0
- package/dist/reporting/reporters/HumanReadableReporter.js +524 -0
- package/dist/reporting/reporters/HumanReadableReporter.js.map +1 -0
- package/dist/reporting/reporters/JSONReporter.d.ts +193 -0
- package/dist/reporting/reporters/JSONReporter.d.ts.map +1 -0
- package/dist/reporting/reporters/JSONReporter.js +324 -0
- package/dist/reporting/reporters/JSONReporter.js.map +1 -0
- package/dist/reporting/reporters/index.d.ts +14 -0
- package/dist/reporting/reporters/index.d.ts.map +1 -0
- package/dist/reporting/reporters/index.js +19 -0
- package/dist/reporting/reporters/index.js.map +1 -0
- package/dist/reporting/types.d.ts +427 -0
- package/dist/reporting/types.d.ts.map +1 -0
- package/dist/reporting/types.js +12 -0
- package/dist/reporting/types.js.map +1 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/qx.d.ts +397 -0
- package/dist/types/qx.d.ts.map +1 -0
- package/dist/types/qx.js +71 -0
- package/dist/types/qx.js.map +1 -0
- package/dist/visualization/api/RestEndpoints.js +1 -1
- package/dist/visualization/api/RestEndpoints.js.map +1 -1
- package/dist/visualization/api/WebSocketServer.d.ts +44 -0
- package/dist/visualization/api/WebSocketServer.d.ts.map +1 -1
- package/dist/visualization/api/WebSocketServer.js +144 -23
- package/dist/visualization/api/WebSocketServer.js.map +1 -1
- package/dist/visualization/core/DataTransformer.d.ts +10 -0
- package/dist/visualization/core/DataTransformer.d.ts.map +1 -1
- package/dist/visualization/core/DataTransformer.js +60 -5
- package/dist/visualization/core/DataTransformer.js.map +1 -1
- package/dist/visualization/emit-event.d.ts +75 -0
- package/dist/visualization/emit-event.d.ts.map +1 -0
- package/dist/visualization/emit-event.js +213 -0
- package/dist/visualization/emit-event.js.map +1 -0
- package/dist/visualization/index.d.ts +1 -0
- package/dist/visualization/index.d.ts.map +1 -1
- package/dist/visualization/index.js +7 -1
- package/dist/visualization/index.js.map +1 -1
- package/docs/reference/skills.md +63 -1
- package/package.json +12 -4
|
@@ -0,0 +1,986 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* UnifiedMemoryCoordinator - Single interface for all memory systems
|
|
4
|
+
*
|
|
5
|
+
* Integrates:
|
|
6
|
+
* - SwarmMemoryManager (cross-agent coordination with SQLite)
|
|
7
|
+
* - AgentDBService (vector database operations)
|
|
8
|
+
* - RuVectorPatternStore (high-performance pattern storage)
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Automatic SQLite ↔ JSON fallback
|
|
12
|
+
* - Memory health monitoring
|
|
13
|
+
* - Unified namespace management
|
|
14
|
+
* - Cross-system synchronization
|
|
15
|
+
* - Transparent backend switching
|
|
16
|
+
* - Metrics collection
|
|
17
|
+
*
|
|
18
|
+
* @module core/memory/UnifiedMemoryCoordinator
|
|
19
|
+
* @version 1.0.0
|
|
20
|
+
*/
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.NamespacedCoordinator = exports.UnifiedMemoryCoordinator = void 0;
|
|
56
|
+
exports.createUnifiedMemoryCoordinator = createUnifiedMemoryCoordinator;
|
|
57
|
+
const Logger_1 = require("../../utils/Logger");
|
|
58
|
+
const SwarmMemoryManager_1 = require("./SwarmMemoryManager");
|
|
59
|
+
const AgentDBService_1 = require("./AgentDBService");
|
|
60
|
+
const RuVectorPatternStore_1 = require("./RuVectorPatternStore");
|
|
61
|
+
const RuVectorPatternStore_2 = require("./RuVectorPatternStore");
|
|
62
|
+
const path = __importStar(require("path"));
|
|
63
|
+
const fs = __importStar(require("fs-extra"));
|
|
64
|
+
/**
|
|
65
|
+
* UnifiedMemoryCoordinator - Unified interface for all memory systems
|
|
66
|
+
*/
|
|
67
|
+
class UnifiedMemoryCoordinator {
|
|
68
|
+
constructor(config = {}) {
|
|
69
|
+
this.logger = Logger_1.Logger.getInstance();
|
|
70
|
+
this.config = {
|
|
71
|
+
preferredBackend: config.preferredBackend ?? 'auto',
|
|
72
|
+
enableFallback: config.enableFallback ?? true,
|
|
73
|
+
syncInterval: config.syncInterval ?? 60000, // 1 minute
|
|
74
|
+
healthCheckInterval: config.healthCheckInterval ?? 30000, // 30 seconds
|
|
75
|
+
maxRetries: config.maxRetries ?? 3,
|
|
76
|
+
namespace: config.namespace ?? 'default',
|
|
77
|
+
dbPaths: config.dbPaths ?? {},
|
|
78
|
+
enableVectorOps: config.enableVectorOps ?? true,
|
|
79
|
+
vectorDimension: config.vectorDimension ?? 384,
|
|
80
|
+
};
|
|
81
|
+
this.jsonStore = new Map();
|
|
82
|
+
this.healthStatus = new Map();
|
|
83
|
+
this.syncInProgress = false;
|
|
84
|
+
this.activeBackend = 'json'; // Start with fallback
|
|
85
|
+
this.fallbackChain = [];
|
|
86
|
+
this.metrics = {
|
|
87
|
+
totalOperations: 0,
|
|
88
|
+
operationsByBackend: new Map(),
|
|
89
|
+
averageLatency: 0,
|
|
90
|
+
failoverCount: 0,
|
|
91
|
+
cacheHitRate: 0,
|
|
92
|
+
syncCount: 0,
|
|
93
|
+
errorCount: 0,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Initialize the memory coordinator
|
|
98
|
+
*/
|
|
99
|
+
async initialize() {
|
|
100
|
+
this.logger.info('Initializing UnifiedMemoryCoordinator...');
|
|
101
|
+
try {
|
|
102
|
+
// Detect available backends
|
|
103
|
+
const availableBackends = await this.detectAvailableBackends();
|
|
104
|
+
this.logger.info('Available backends:', availableBackends);
|
|
105
|
+
// Initialize SwarmMemoryManager (always available)
|
|
106
|
+
await this.initializeSwarmMemory();
|
|
107
|
+
// Initialize AgentDB if available
|
|
108
|
+
if (availableBackends.includes('agentdb')) {
|
|
109
|
+
await this.initializeAgentDB();
|
|
110
|
+
}
|
|
111
|
+
// Initialize RuVector if available
|
|
112
|
+
if (availableBackends.includes('vector') && this.config.enableVectorOps) {
|
|
113
|
+
await this.initializeVectorStore();
|
|
114
|
+
}
|
|
115
|
+
// Select optimal backend
|
|
116
|
+
this.activeBackend = await this.selectOptimalBackend();
|
|
117
|
+
this.logger.info(`Active backend: ${this.activeBackend}`);
|
|
118
|
+
// Build fallback chain
|
|
119
|
+
this.fallbackChain = this.buildFallbackChain(this.activeBackend);
|
|
120
|
+
this.logger.info('Fallback chain:', this.fallbackChain);
|
|
121
|
+
// Start health monitoring
|
|
122
|
+
this.startHealthMonitoring();
|
|
123
|
+
// Start periodic sync
|
|
124
|
+
if (this.config.syncInterval > 0) {
|
|
125
|
+
this.startPeriodicSync();
|
|
126
|
+
}
|
|
127
|
+
this.logger.info('UnifiedMemoryCoordinator initialized successfully');
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
this.logger.error('Failed to initialize UnifiedMemoryCoordinator:', error);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Detect available backends
|
|
136
|
+
*/
|
|
137
|
+
async detectAvailableBackends() {
|
|
138
|
+
const backends = [];
|
|
139
|
+
// SQLite is always available via SwarmMemoryManager
|
|
140
|
+
backends.push('sqlite');
|
|
141
|
+
// Check AgentDB
|
|
142
|
+
try {
|
|
143
|
+
require('agentdb');
|
|
144
|
+
backends.push('agentdb');
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
this.logger.debug('AgentDB not available');
|
|
148
|
+
}
|
|
149
|
+
// Check RuVector
|
|
150
|
+
if ((0, RuVectorPatternStore_2.isRuVectorAvailable)()) {
|
|
151
|
+
backends.push('vector');
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
this.logger.debug('RuVector not available');
|
|
155
|
+
}
|
|
156
|
+
// JSON is always available as fallback
|
|
157
|
+
backends.push('json');
|
|
158
|
+
return backends;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Initialize SwarmMemoryManager
|
|
162
|
+
*/
|
|
163
|
+
async initializeSwarmMemory() {
|
|
164
|
+
try {
|
|
165
|
+
const dbPath = this.config.dbPaths?.swarm ??
|
|
166
|
+
path.join(process.cwd(), '.agentic-qe', 'memory.db');
|
|
167
|
+
await fs.ensureDir(path.dirname(dbPath));
|
|
168
|
+
this.swarmMemory = new SwarmMemoryManager_1.SwarmMemoryManager(dbPath);
|
|
169
|
+
await this.swarmMemory.initialize();
|
|
170
|
+
this.updateHealthStatus('sqlite', {
|
|
171
|
+
backend: 'sqlite',
|
|
172
|
+
status: 'healthy',
|
|
173
|
+
latency: 0,
|
|
174
|
+
lastCheck: new Date(),
|
|
175
|
+
errorCount: 0,
|
|
176
|
+
details: { dbPath },
|
|
177
|
+
});
|
|
178
|
+
this.logger.debug('SwarmMemoryManager initialized');
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
this.logger.error('Failed to initialize SwarmMemoryManager:', error);
|
|
182
|
+
this.updateHealthStatus('sqlite', {
|
|
183
|
+
backend: 'sqlite',
|
|
184
|
+
status: 'failed',
|
|
185
|
+
latency: 0,
|
|
186
|
+
lastCheck: new Date(),
|
|
187
|
+
errorCount: 1,
|
|
188
|
+
details: { error: String(error) },
|
|
189
|
+
});
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Initialize AgentDB
|
|
195
|
+
*/
|
|
196
|
+
async initializeAgentDB() {
|
|
197
|
+
try {
|
|
198
|
+
const dbPath = this.config.dbPaths?.agentdb ??
|
|
199
|
+
path.join(process.cwd(), '.agentic-qe', 'agentdb.db');
|
|
200
|
+
this.agentDB = new AgentDBService_1.AgentDBService({
|
|
201
|
+
dbPath,
|
|
202
|
+
embeddingDim: this.config.vectorDimension,
|
|
203
|
+
enableHNSW: true,
|
|
204
|
+
enableCache: true,
|
|
205
|
+
});
|
|
206
|
+
await this.agentDB.initialize();
|
|
207
|
+
this.updateHealthStatus('agentdb', {
|
|
208
|
+
backend: 'agentdb',
|
|
209
|
+
status: 'healthy',
|
|
210
|
+
latency: 0,
|
|
211
|
+
lastCheck: new Date(),
|
|
212
|
+
errorCount: 0,
|
|
213
|
+
details: { dbPath },
|
|
214
|
+
});
|
|
215
|
+
this.logger.debug('AgentDB initialized');
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
this.logger.error('Failed to initialize AgentDB:', error);
|
|
219
|
+
this.updateHealthStatus('agentdb', {
|
|
220
|
+
backend: 'agentdb',
|
|
221
|
+
status: 'failed',
|
|
222
|
+
latency: 0,
|
|
223
|
+
lastCheck: new Date(),
|
|
224
|
+
errorCount: 1,
|
|
225
|
+
details: { error: String(error) },
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Initialize RuVector store
|
|
231
|
+
*/
|
|
232
|
+
async initializeVectorStore() {
|
|
233
|
+
try {
|
|
234
|
+
const dbPath = this.config.dbPaths?.ruvector ??
|
|
235
|
+
path.join(process.cwd(), '.agentic-qe', 'vectors.db');
|
|
236
|
+
this.vectorStore = new RuVectorPatternStore_1.RuVectorPatternStore({
|
|
237
|
+
dimension: this.config.vectorDimension,
|
|
238
|
+
metric: 'cosine',
|
|
239
|
+
storagePath: dbPath,
|
|
240
|
+
autoPersist: true,
|
|
241
|
+
enableMetrics: true,
|
|
242
|
+
});
|
|
243
|
+
await this.vectorStore.initialize();
|
|
244
|
+
this.updateHealthStatus('vector', {
|
|
245
|
+
backend: 'vector',
|
|
246
|
+
status: 'healthy',
|
|
247
|
+
latency: 0,
|
|
248
|
+
lastCheck: new Date(),
|
|
249
|
+
errorCount: 0,
|
|
250
|
+
details: { dbPath },
|
|
251
|
+
});
|
|
252
|
+
this.logger.debug('RuVector store initialized');
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
this.logger.error('Failed to initialize RuVector store:', error);
|
|
256
|
+
this.updateHealthStatus('vector', {
|
|
257
|
+
backend: 'vector',
|
|
258
|
+
status: 'failed',
|
|
259
|
+
latency: 0,
|
|
260
|
+
lastCheck: new Date(),
|
|
261
|
+
errorCount: 1,
|
|
262
|
+
details: { error: String(error) },
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Select optimal backend
|
|
268
|
+
*/
|
|
269
|
+
async selectOptimalBackend() {
|
|
270
|
+
if (this.config.preferredBackend !== 'auto') {
|
|
271
|
+
const health = this.healthStatus.get(this.config.preferredBackend);
|
|
272
|
+
if (health && health.status === 'healthy') {
|
|
273
|
+
return this.config.preferredBackend;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Auto-select based on health and capabilities
|
|
277
|
+
const priorities = ['sqlite', 'agentdb', 'vector', 'json'];
|
|
278
|
+
for (const backend of priorities) {
|
|
279
|
+
const health = this.healthStatus.get(backend);
|
|
280
|
+
if (health && health.status === 'healthy') {
|
|
281
|
+
return backend;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Fallback to JSON
|
|
285
|
+
return 'json';
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Build fallback chain
|
|
289
|
+
*/
|
|
290
|
+
buildFallbackChain(primary) {
|
|
291
|
+
const chain = [primary];
|
|
292
|
+
const alternatives = ['sqlite', 'agentdb', 'vector', 'json'];
|
|
293
|
+
for (const backend of alternatives) {
|
|
294
|
+
if (backend !== primary) {
|
|
295
|
+
const health = this.healthStatus.get(backend);
|
|
296
|
+
if (health && health.status !== 'failed') {
|
|
297
|
+
chain.push(backend);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Always have JSON as final fallback
|
|
302
|
+
if (!chain.includes('json')) {
|
|
303
|
+
chain.push('json');
|
|
304
|
+
}
|
|
305
|
+
return chain;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Store a key-value pair
|
|
309
|
+
*/
|
|
310
|
+
async store(key, value, ttl) {
|
|
311
|
+
const startTime = Date.now();
|
|
312
|
+
const fullKey = this.prefixKey(key);
|
|
313
|
+
try {
|
|
314
|
+
await this.executeWithFallback(async (backend) => {
|
|
315
|
+
await this.storeInBackend(backend, fullKey, value, ttl);
|
|
316
|
+
});
|
|
317
|
+
this.updateMetrics('store', startTime, this.activeBackend);
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
this.logger.error(`Failed to store key ${fullKey}:`, error);
|
|
321
|
+
this.metrics.errorCount++;
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Store in specific backend
|
|
327
|
+
*/
|
|
328
|
+
async storeInBackend(backend, key, value, ttl) {
|
|
329
|
+
switch (backend) {
|
|
330
|
+
case 'sqlite':
|
|
331
|
+
await this.swarmMemory.store(key, value, { ttl, partition: this.config.namespace });
|
|
332
|
+
break;
|
|
333
|
+
case 'agentdb':
|
|
334
|
+
// AgentDB stores patterns, not key-value pairs
|
|
335
|
+
// Fall through to JSON for key-value storage
|
|
336
|
+
throw new Error('AgentDB not suitable for key-value storage');
|
|
337
|
+
case 'json':
|
|
338
|
+
this.jsonStore.set(key, {
|
|
339
|
+
value,
|
|
340
|
+
ttl,
|
|
341
|
+
createdAt: Date.now(),
|
|
342
|
+
});
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
throw new Error(`Unknown backend: ${backend}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Retrieve a value by key
|
|
350
|
+
*/
|
|
351
|
+
async retrieve(key) {
|
|
352
|
+
const startTime = Date.now();
|
|
353
|
+
const fullKey = this.prefixKey(key);
|
|
354
|
+
try {
|
|
355
|
+
const result = await this.executeWithFallback(async (backend) => {
|
|
356
|
+
return await this.retrieveFromBackend(backend, fullKey);
|
|
357
|
+
});
|
|
358
|
+
this.updateMetrics('retrieve', startTime, this.activeBackend);
|
|
359
|
+
return result;
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
this.logger.error(`Failed to retrieve key ${fullKey}:`, error);
|
|
363
|
+
this.metrics.errorCount++;
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Retrieve from specific backend
|
|
369
|
+
*/
|
|
370
|
+
async retrieveFromBackend(backend, key) {
|
|
371
|
+
switch (backend) {
|
|
372
|
+
case 'sqlite':
|
|
373
|
+
return await this.swarmMemory.retrieve(key, { partition: this.config.namespace });
|
|
374
|
+
case 'json': {
|
|
375
|
+
const entry = this.jsonStore.get(key);
|
|
376
|
+
if (!entry)
|
|
377
|
+
return null;
|
|
378
|
+
// Check TTL
|
|
379
|
+
if (entry.ttl && Date.now() - entry.createdAt > entry.ttl * 1000) {
|
|
380
|
+
this.jsonStore.delete(key);
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
return entry.value;
|
|
384
|
+
}
|
|
385
|
+
default:
|
|
386
|
+
throw new Error(`Backend ${backend} not suitable for key-value retrieval`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Delete a key
|
|
391
|
+
*/
|
|
392
|
+
async delete(key) {
|
|
393
|
+
const startTime = Date.now();
|
|
394
|
+
const fullKey = this.prefixKey(key);
|
|
395
|
+
try {
|
|
396
|
+
const result = await this.executeWithFallback(async (backend) => {
|
|
397
|
+
return await this.deleteFromBackend(backend, fullKey);
|
|
398
|
+
});
|
|
399
|
+
this.updateMetrics('delete', startTime, this.activeBackend);
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
this.logger.error(`Failed to delete key ${fullKey}:`, error);
|
|
404
|
+
this.metrics.errorCount++;
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Delete from specific backend
|
|
410
|
+
*/
|
|
411
|
+
async deleteFromBackend(backend, key) {
|
|
412
|
+
switch (backend) {
|
|
413
|
+
case 'sqlite':
|
|
414
|
+
await this.swarmMemory.delete(key, this.config.namespace);
|
|
415
|
+
return true;
|
|
416
|
+
case 'json':
|
|
417
|
+
return this.jsonStore.delete(key);
|
|
418
|
+
default:
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Check if key exists
|
|
424
|
+
*/
|
|
425
|
+
async exists(key) {
|
|
426
|
+
const value = await this.retrieve(key);
|
|
427
|
+
return value !== null;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* List keys matching pattern
|
|
431
|
+
*/
|
|
432
|
+
async list(pattern) {
|
|
433
|
+
const startTime = Date.now();
|
|
434
|
+
try {
|
|
435
|
+
const result = await this.executeWithFallback(async (backend) => {
|
|
436
|
+
return await this.listFromBackend(backend, pattern);
|
|
437
|
+
});
|
|
438
|
+
this.updateMetrics('list', startTime, this.activeBackend);
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
this.logger.error('Failed to list keys:', error);
|
|
443
|
+
this.metrics.errorCount++;
|
|
444
|
+
return [];
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* List from specific backend
|
|
449
|
+
*/
|
|
450
|
+
async listFromBackend(backend, pattern) {
|
|
451
|
+
switch (backend) {
|
|
452
|
+
case 'sqlite': {
|
|
453
|
+
const sqlPattern = pattern ? pattern.replace(/\*/g, '%') : '%';
|
|
454
|
+
const entries = await this.swarmMemory.query(sqlPattern, { partition: this.config.namespace });
|
|
455
|
+
return entries.map(e => e.key);
|
|
456
|
+
}
|
|
457
|
+
case 'json': {
|
|
458
|
+
const keys = Array.from(this.jsonStore.keys());
|
|
459
|
+
if (!pattern)
|
|
460
|
+
return keys;
|
|
461
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
|
462
|
+
return keys.filter(k => regex.test(k));
|
|
463
|
+
}
|
|
464
|
+
default:
|
|
465
|
+
return [];
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Search for entries
|
|
470
|
+
*/
|
|
471
|
+
async search(query, options = {}) {
|
|
472
|
+
const startTime = Date.now();
|
|
473
|
+
try {
|
|
474
|
+
const results = await this.executeWithFallback(async (backend) => {
|
|
475
|
+
return await this.searchInBackend(backend, query, options);
|
|
476
|
+
});
|
|
477
|
+
this.updateMetrics('search', startTime, this.activeBackend);
|
|
478
|
+
return results;
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
this.logger.error('Failed to search:', error);
|
|
482
|
+
this.metrics.errorCount++;
|
|
483
|
+
return [];
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Search in specific backend
|
|
488
|
+
*/
|
|
489
|
+
async searchInBackend(backend, query, options) {
|
|
490
|
+
switch (backend) {
|
|
491
|
+
case 'sqlite': {
|
|
492
|
+
const sqlPattern = `%${query}%`;
|
|
493
|
+
const entries = await this.swarmMemory.query(sqlPattern, { partition: this.config.namespace });
|
|
494
|
+
const keys = entries.map(e => e.key);
|
|
495
|
+
const results = [];
|
|
496
|
+
for (const key of keys.slice(options.offset ?? 0, (options.offset ?? 0) + (options.limit ?? 10))) {
|
|
497
|
+
const value = await this.swarmMemory.retrieve(key, { partition: this.config.namespace });
|
|
498
|
+
if (value) {
|
|
499
|
+
results.push({ key, value, backend: 'sqlite' });
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return results;
|
|
503
|
+
}
|
|
504
|
+
case 'json': {
|
|
505
|
+
const results = [];
|
|
506
|
+
const limit = options.limit ?? 10;
|
|
507
|
+
const offset = options.offset ?? 0;
|
|
508
|
+
let count = 0;
|
|
509
|
+
for (const [key, entry] of this.jsonStore.entries()) {
|
|
510
|
+
if (key.includes(query)) {
|
|
511
|
+
if (count >= offset && count < offset + limit) {
|
|
512
|
+
results.push({ key, value: entry.value, backend: 'json' });
|
|
513
|
+
}
|
|
514
|
+
count++;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return results;
|
|
518
|
+
}
|
|
519
|
+
default:
|
|
520
|
+
return [];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Store multiple entries in batch
|
|
525
|
+
*/
|
|
526
|
+
async storeBatch(entries) {
|
|
527
|
+
const startTime = Date.now();
|
|
528
|
+
try {
|
|
529
|
+
await this.executeWithFallback(async (backend) => {
|
|
530
|
+
for (const entry of entries) {
|
|
531
|
+
const fullKey = this.prefixKey(entry.key);
|
|
532
|
+
await this.storeInBackend(backend, fullKey, entry.value, entry.ttl);
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
this.updateMetrics('storeBatch', startTime, this.activeBackend);
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
this.logger.error('Failed to store batch:', error);
|
|
539
|
+
this.metrics.errorCount++;
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Retrieve multiple entries in batch
|
|
545
|
+
*/
|
|
546
|
+
async retrieveBatch(keys) {
|
|
547
|
+
const startTime = Date.now();
|
|
548
|
+
const results = new Map();
|
|
549
|
+
try {
|
|
550
|
+
await this.executeWithFallback(async (backend) => {
|
|
551
|
+
for (const key of keys) {
|
|
552
|
+
const fullKey = this.prefixKey(key);
|
|
553
|
+
const value = await this.retrieveFromBackend(backend, fullKey);
|
|
554
|
+
results.set(key, value);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
this.updateMetrics('retrieveBatch', startTime, this.activeBackend);
|
|
558
|
+
return results;
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
this.logger.error('Failed to retrieve batch:', error);
|
|
562
|
+
this.metrics.errorCount++;
|
|
563
|
+
return results;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Store a vector
|
|
568
|
+
*/
|
|
569
|
+
async storeVector(key, vector, metadata) {
|
|
570
|
+
if (!this.vectorStore) {
|
|
571
|
+
throw new Error('Vector operations not enabled');
|
|
572
|
+
}
|
|
573
|
+
const pattern = {
|
|
574
|
+
id: this.prefixKey(key),
|
|
575
|
+
type: 'vector',
|
|
576
|
+
domain: this.config.namespace,
|
|
577
|
+
content: '',
|
|
578
|
+
embedding: vector,
|
|
579
|
+
metadata,
|
|
580
|
+
};
|
|
581
|
+
await this.vectorStore.storePattern(pattern);
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Search for similar vectors
|
|
585
|
+
*/
|
|
586
|
+
async searchSimilar(vector, limit = 10) {
|
|
587
|
+
if (!this.vectorStore) {
|
|
588
|
+
throw new Error('Vector operations not enabled');
|
|
589
|
+
}
|
|
590
|
+
const results = await this.vectorStore.searchSimilar(vector, { k: limit });
|
|
591
|
+
return results.map(r => ({
|
|
592
|
+
key: r.pattern.id,
|
|
593
|
+
similarity: r.score,
|
|
594
|
+
metadata: r.pattern.metadata,
|
|
595
|
+
vector: r.pattern.embedding,
|
|
596
|
+
}));
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Store a pattern
|
|
600
|
+
*/
|
|
601
|
+
async storePattern(pattern) {
|
|
602
|
+
if (this.vectorStore && pattern.embedding) {
|
|
603
|
+
const testPattern = {
|
|
604
|
+
id: pattern.id,
|
|
605
|
+
type: pattern.type,
|
|
606
|
+
domain: this.config.namespace,
|
|
607
|
+
content: pattern.content,
|
|
608
|
+
embedding: pattern.embedding,
|
|
609
|
+
metadata: pattern.metadata,
|
|
610
|
+
};
|
|
611
|
+
await this.vectorStore.storePattern(testPattern);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
// Store as key-value
|
|
615
|
+
await this.store(`pattern:${pattern.id}`, pattern);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Query patterns
|
|
620
|
+
*/
|
|
621
|
+
async queryPatterns(filter) {
|
|
622
|
+
const results = [];
|
|
623
|
+
if (this.vectorStore) {
|
|
624
|
+
// Use vector store
|
|
625
|
+
const keys = await this.list('pattern:*');
|
|
626
|
+
for (const key of keys.slice(0, filter.limit ?? 100)) {
|
|
627
|
+
const patternId = key.replace('pattern:', '');
|
|
628
|
+
const pattern = await this.vectorStore.getPattern(patternId);
|
|
629
|
+
if (pattern) {
|
|
630
|
+
if (filter.type && pattern.type !== filter.type)
|
|
631
|
+
continue;
|
|
632
|
+
if (filter.domain && pattern.domain !== filter.domain)
|
|
633
|
+
continue;
|
|
634
|
+
results.push({
|
|
635
|
+
id: pattern.id,
|
|
636
|
+
type: pattern.type,
|
|
637
|
+
content: pattern.content,
|
|
638
|
+
confidence: 1.0,
|
|
639
|
+
metadata: pattern.metadata ?? {},
|
|
640
|
+
embedding: pattern.embedding,
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
// Fall back to key-value search
|
|
647
|
+
const keys = await this.list('pattern:*');
|
|
648
|
+
for (const key of keys.slice(0, filter.limit ?? 100)) {
|
|
649
|
+
const pattern = await this.retrieve(key);
|
|
650
|
+
if (pattern) {
|
|
651
|
+
if (filter.type && pattern.type !== filter.type)
|
|
652
|
+
continue;
|
|
653
|
+
if (filter.minConfidence && pattern.confidence < filter.minConfidence)
|
|
654
|
+
continue;
|
|
655
|
+
results.push(pattern);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return results;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Get backend-specific instance
|
|
663
|
+
*/
|
|
664
|
+
getSwarmMemory() {
|
|
665
|
+
return this.swarmMemory;
|
|
666
|
+
}
|
|
667
|
+
getAgentDB() {
|
|
668
|
+
return this.agentDB;
|
|
669
|
+
}
|
|
670
|
+
getVectorStore() {
|
|
671
|
+
return this.vectorStore;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Check health of all backends
|
|
675
|
+
*/
|
|
676
|
+
async checkHealth() {
|
|
677
|
+
const backends = ['sqlite', 'agentdb', 'json', 'vector'];
|
|
678
|
+
for (const backend of backends) {
|
|
679
|
+
const startTime = Date.now();
|
|
680
|
+
try {
|
|
681
|
+
await this.healthCheckBackend(backend);
|
|
682
|
+
const latency = Date.now() - startTime;
|
|
683
|
+
this.updateHealthStatus(backend, {
|
|
684
|
+
backend,
|
|
685
|
+
status: 'healthy',
|
|
686
|
+
latency,
|
|
687
|
+
lastCheck: new Date(),
|
|
688
|
+
errorCount: 0,
|
|
689
|
+
details: {},
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
catch (error) {
|
|
693
|
+
const health = this.healthStatus.get(backend);
|
|
694
|
+
const errorCount = (health?.errorCount ?? 0) + 1;
|
|
695
|
+
this.updateHealthStatus(backend, {
|
|
696
|
+
backend,
|
|
697
|
+
status: errorCount > 3 ? 'failed' : 'degraded',
|
|
698
|
+
latency: 0,
|
|
699
|
+
lastCheck: new Date(),
|
|
700
|
+
errorCount,
|
|
701
|
+
details: { error: String(error) },
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return this.healthStatus;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Health check for specific backend
|
|
709
|
+
*/
|
|
710
|
+
async healthCheckBackend(backend) {
|
|
711
|
+
switch (backend) {
|
|
712
|
+
case 'sqlite':
|
|
713
|
+
if (!this.swarmMemory)
|
|
714
|
+
throw new Error('SwarmMemory not initialized');
|
|
715
|
+
// Try a simple operation - query with wildcard to verify connectivity
|
|
716
|
+
await this.swarmMemory.query('%', { partition: this.config.namespace });
|
|
717
|
+
break;
|
|
718
|
+
case 'agentdb':
|
|
719
|
+
if (!this.agentDB)
|
|
720
|
+
throw new Error('AgentDB not initialized');
|
|
721
|
+
// AgentDB health check would go here
|
|
722
|
+
break;
|
|
723
|
+
case 'vector':
|
|
724
|
+
if (!this.vectorStore)
|
|
725
|
+
throw new Error('VectorStore not initialized');
|
|
726
|
+
await this.vectorStore.getStats();
|
|
727
|
+
break;
|
|
728
|
+
case 'json':
|
|
729
|
+
// JSON store is always healthy
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Get health status
|
|
735
|
+
*/
|
|
736
|
+
getHealthStatus() {
|
|
737
|
+
return new Map(this.healthStatus);
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Check if coordinator is healthy
|
|
741
|
+
*/
|
|
742
|
+
isHealthy() {
|
|
743
|
+
const primaryHealth = this.healthStatus.get(this.activeBackend);
|
|
744
|
+
return primaryHealth?.status === 'healthy';
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Execute with fallback
|
|
748
|
+
*/
|
|
749
|
+
async executeWithFallback(operation, backends) {
|
|
750
|
+
const chain = backends ?? this.fallbackChain;
|
|
751
|
+
let lastError = null;
|
|
752
|
+
for (const backend of chain) {
|
|
753
|
+
try {
|
|
754
|
+
const result = await operation(backend);
|
|
755
|
+
// If we're not using the primary backend, consider switching
|
|
756
|
+
if (backend !== this.activeBackend) {
|
|
757
|
+
this.metrics.failoverCount++;
|
|
758
|
+
this.logger.warn(`Using fallback backend: ${backend}`);
|
|
759
|
+
}
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
catch (error) {
|
|
763
|
+
lastError = error;
|
|
764
|
+
this.logger.debug(`Backend ${backend} failed:`, error);
|
|
765
|
+
// Update health status
|
|
766
|
+
const health = this.healthStatus.get(backend);
|
|
767
|
+
if (health) {
|
|
768
|
+
health.errorCount++;
|
|
769
|
+
health.status = 'degraded';
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
throw new Error(`All backends failed: ${lastError?.message}`);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Switch backend
|
|
777
|
+
*/
|
|
778
|
+
async switchBackend(from, to) {
|
|
779
|
+
this.logger.info(`Switching backend from ${from} to ${to}`);
|
|
780
|
+
this.activeBackend = to;
|
|
781
|
+
this.fallbackChain = this.buildFallbackChain(to);
|
|
782
|
+
// Optionally sync data
|
|
783
|
+
if (this.config.enableFallback) {
|
|
784
|
+
await this.syncBackends();
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Sync data between backends
|
|
789
|
+
*/
|
|
790
|
+
async syncBackends() {
|
|
791
|
+
if (this.syncInProgress) {
|
|
792
|
+
return {
|
|
793
|
+
success: false,
|
|
794
|
+
recordsSynced: 0,
|
|
795
|
+
conflicts: 0,
|
|
796
|
+
duration: 0,
|
|
797
|
+
errors: ['Sync already in progress'],
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
this.syncInProgress = true;
|
|
801
|
+
const startTime = Date.now();
|
|
802
|
+
let recordsSynced = 0;
|
|
803
|
+
const errors = [];
|
|
804
|
+
try {
|
|
805
|
+
this.logger.debug('Starting backend synchronization...');
|
|
806
|
+
// Sync SQLite to JSON
|
|
807
|
+
if (this.swarmMemory) {
|
|
808
|
+
try {
|
|
809
|
+
const entries = await this.swarmMemory.query('%', { partition: this.config.namespace });
|
|
810
|
+
const keys = entries.map(e => e.key);
|
|
811
|
+
for (const key of keys) {
|
|
812
|
+
try {
|
|
813
|
+
const value = await this.swarmMemory.retrieve(key, { partition: this.config.namespace });
|
|
814
|
+
if (value) {
|
|
815
|
+
this.jsonStore.set(key, {
|
|
816
|
+
value,
|
|
817
|
+
createdAt: Date.now(),
|
|
818
|
+
});
|
|
819
|
+
recordsSynced++;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
catch (error) {
|
|
823
|
+
errors.push(`Failed to sync key ${key}: ${error}`);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
catch (error) {
|
|
828
|
+
errors.push(`Failed to sync from SQLite: ${error}`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
const duration = Date.now() - startTime;
|
|
832
|
+
this.metrics.syncCount++;
|
|
833
|
+
this.metrics.lastSync = new Date();
|
|
834
|
+
this.logger.info(`Sync completed: ${recordsSynced} records in ${duration}ms`);
|
|
835
|
+
return {
|
|
836
|
+
success: errors.length === 0,
|
|
837
|
+
recordsSynced,
|
|
838
|
+
conflicts: 0,
|
|
839
|
+
duration,
|
|
840
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
finally {
|
|
844
|
+
this.syncInProgress = false;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Create namespaced coordinator
|
|
849
|
+
*/
|
|
850
|
+
createNamespace(name) {
|
|
851
|
+
return new NamespacedCoordinator(this, name);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Get metrics
|
|
855
|
+
*/
|
|
856
|
+
getMetrics() {
|
|
857
|
+
return {
|
|
858
|
+
...this.metrics,
|
|
859
|
+
operationsByBackend: new Map(this.metrics.operationsByBackend),
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Start health monitoring
|
|
864
|
+
*/
|
|
865
|
+
startHealthMonitoring() {
|
|
866
|
+
if (this.healthCheckTimer) {
|
|
867
|
+
clearInterval(this.healthCheckTimer);
|
|
868
|
+
}
|
|
869
|
+
this.healthCheckTimer = setInterval(async () => {
|
|
870
|
+
try {
|
|
871
|
+
await this.checkHealth();
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
this.logger.error('Health check failed:', error);
|
|
875
|
+
}
|
|
876
|
+
}, this.config.healthCheckInterval);
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Start periodic sync
|
|
880
|
+
*/
|
|
881
|
+
startPeriodicSync() {
|
|
882
|
+
if (this.syncTimer) {
|
|
883
|
+
clearInterval(this.syncTimer);
|
|
884
|
+
}
|
|
885
|
+
this.syncTimer = setInterval(async () => {
|
|
886
|
+
try {
|
|
887
|
+
await this.syncBackends();
|
|
888
|
+
}
|
|
889
|
+
catch (error) {
|
|
890
|
+
this.logger.error('Periodic sync failed:', error);
|
|
891
|
+
}
|
|
892
|
+
}, this.config.syncInterval);
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Update health status
|
|
896
|
+
*/
|
|
897
|
+
updateHealthStatus(backend, health) {
|
|
898
|
+
this.healthStatus.set(backend, health);
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Update metrics
|
|
902
|
+
*/
|
|
903
|
+
updateMetrics(operation, startTime, backend) {
|
|
904
|
+
const duration = Date.now() - startTime;
|
|
905
|
+
this.metrics.totalOperations++;
|
|
906
|
+
const backendOps = this.metrics.operationsByBackend.get(backend) ?? 0;
|
|
907
|
+
this.metrics.operationsByBackend.set(backend, backendOps + 1);
|
|
908
|
+
// Update average latency
|
|
909
|
+
const totalLatency = this.metrics.averageLatency * (this.metrics.totalOperations - 1) + duration;
|
|
910
|
+
this.metrics.averageLatency = totalLatency / this.metrics.totalOperations;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Prefix key with namespace
|
|
914
|
+
*/
|
|
915
|
+
prefixKey(key) {
|
|
916
|
+
return `${this.config.namespace}:${key}`;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Shutdown coordinator
|
|
920
|
+
*/
|
|
921
|
+
async shutdown() {
|
|
922
|
+
this.logger.info('Shutting down UnifiedMemoryCoordinator...');
|
|
923
|
+
// Stop timers
|
|
924
|
+
if (this.healthCheckTimer) {
|
|
925
|
+
clearInterval(this.healthCheckTimer);
|
|
926
|
+
}
|
|
927
|
+
if (this.syncTimer) {
|
|
928
|
+
clearInterval(this.syncTimer);
|
|
929
|
+
}
|
|
930
|
+
// Final sync
|
|
931
|
+
if (this.config.enableFallback) {
|
|
932
|
+
await this.syncBackends();
|
|
933
|
+
}
|
|
934
|
+
// Shutdown backends
|
|
935
|
+
if (this.vectorStore) {
|
|
936
|
+
await this.vectorStore.shutdown();
|
|
937
|
+
}
|
|
938
|
+
if (this.agentDB) {
|
|
939
|
+
await this.agentDB.close();
|
|
940
|
+
}
|
|
941
|
+
if (this.swarmMemory) {
|
|
942
|
+
await this.swarmMemory.close();
|
|
943
|
+
}
|
|
944
|
+
this.logger.info('UnifiedMemoryCoordinator shut down');
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
exports.UnifiedMemoryCoordinator = UnifiedMemoryCoordinator;
|
|
948
|
+
/**
|
|
949
|
+
* Namespaced wrapper for isolated operations
|
|
950
|
+
*/
|
|
951
|
+
class NamespacedCoordinator {
|
|
952
|
+
constructor(coordinator, namespace) {
|
|
953
|
+
this.coordinator = coordinator;
|
|
954
|
+
this.namespace = namespace;
|
|
955
|
+
}
|
|
956
|
+
async store(key, value, ttl) {
|
|
957
|
+
return this.coordinator.store(this.prefixKey(key), value, ttl);
|
|
958
|
+
}
|
|
959
|
+
async retrieve(key) {
|
|
960
|
+
return this.coordinator.retrieve(this.prefixKey(key));
|
|
961
|
+
}
|
|
962
|
+
async delete(key) {
|
|
963
|
+
return this.coordinator.delete(this.prefixKey(key));
|
|
964
|
+
}
|
|
965
|
+
async list(pattern) {
|
|
966
|
+
const keys = await this.coordinator.list(pattern ? this.prefixKey(pattern) : undefined);
|
|
967
|
+
return keys.map(k => this.unprefixKey(k));
|
|
968
|
+
}
|
|
969
|
+
prefixKey(key) {
|
|
970
|
+
return `${this.namespace}:${key}`;
|
|
971
|
+
}
|
|
972
|
+
unprefixKey(key) {
|
|
973
|
+
const prefix = `${this.namespace}:`;
|
|
974
|
+
return key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
exports.NamespacedCoordinator = NamespacedCoordinator;
|
|
978
|
+
/**
|
|
979
|
+
* Convenience function to create a coordinator
|
|
980
|
+
*/
|
|
981
|
+
async function createUnifiedMemoryCoordinator(config) {
|
|
982
|
+
const coordinator = new UnifiedMemoryCoordinator(config);
|
|
983
|
+
await coordinator.initialize();
|
|
984
|
+
return coordinator;
|
|
985
|
+
}
|
|
986
|
+
//# sourceMappingURL=UnifiedMemoryCoordinator.js.map
|