@mxf-dev/core 1.0.1 → 2.0.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/README.md +18 -0
- package/dist/adapters/FrameworkAdapter.js +1 -1
- package/dist/adapters/FrameworkAdapter.js.map +1 -1
- package/dist/config/ConfigManager.d.ts +9 -8
- package/dist/config/ConfigManager.d.ts.map +1 -1
- package/dist/config/ConfigManager.js +47 -17
- package/dist/config/ConfigManager.js.map +1 -1
- package/dist/config/memory-strata.config.d.ts +25 -0
- package/dist/config/memory-strata.config.d.ts.map +1 -0
- package/dist/config/memory-strata.config.js +107 -0
- package/dist/config/memory-strata.config.js.map +1 -0
- package/dist/constants/ToolNames.d.ts +1 -29
- package/dist/constants/ToolNames.d.ts.map +1 -1
- package/dist/constants/ToolNames.js +4 -17
- package/dist/constants/ToolNames.js.map +1 -1
- package/dist/database/DatabaseAdapterFactory.d.ts +14 -2
- package/dist/database/DatabaseAdapterFactory.d.ts.map +1 -1
- package/dist/database/DatabaseAdapterFactory.js +21 -6
- package/dist/database/DatabaseAdapterFactory.js.map +1 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts +7 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts.map +1 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.js +12 -2
- package/dist/database/adapters/mongodb/MongoBaseRepository.js.map +1 -1
- package/dist/events/ClientEventBus.d.ts +16 -77
- package/dist/events/ClientEventBus.d.ts.map +1 -1
- package/dist/events/ClientEventBus.js +38 -311
- package/dist/events/ClientEventBus.js.map +1 -1
- package/dist/events/EventBus.d.ts +1 -1
- package/dist/events/EventBus.d.ts.map +1 -1
- package/dist/events/EventBus.js +5 -4
- package/dist/events/EventBus.js.map +1 -1
- package/dist/events/EventBusBase.d.ts +139 -34
- package/dist/events/EventBusBase.d.ts.map +1 -1
- package/dist/events/EventBusBase.js +366 -88
- package/dist/events/EventBusBase.js.map +1 -1
- package/dist/events/EventNames.d.ts +10 -1
- package/dist/events/EventNames.d.ts.map +1 -1
- package/dist/events/EventNames.js +3 -0
- package/dist/events/EventNames.js.map +1 -1
- package/dist/events/PublicEvents.d.ts +6 -2
- package/dist/events/PublicEvents.d.ts.map +1 -1
- package/dist/events/PublicEvents.js +7 -4
- package/dist/events/PublicEvents.js.map +1 -1
- package/dist/events/ServerEventBus.d.ts +23 -112
- package/dist/events/ServerEventBus.d.ts.map +1 -1
- package/dist/events/ServerEventBus.js +58 -323
- package/dist/events/ServerEventBus.js.map +1 -1
- package/dist/events/event-definitions/LlmBudgetEvents.d.ts +68 -0
- package/dist/events/event-definitions/LlmBudgetEvents.d.ts.map +1 -0
- package/dist/events/event-definitions/LlmBudgetEvents.js +35 -0
- package/dist/events/event-definitions/LlmBudgetEvents.js.map +1 -0
- package/dist/interfaces/IMemoryPersistence.d.ts +41 -0
- package/dist/interfaces/IMemoryPersistence.d.ts.map +1 -0
- package/dist/interfaces/IMemoryPersistence.js +15 -0
- package/dist/interfaces/IMemoryPersistence.js.map +1 -0
- package/dist/middleware/MxpMiddleware.d.ts +8 -1
- package/dist/middleware/MxpMiddleware.d.ts.map +1 -1
- package/dist/middleware/MxpMiddleware.js +42 -20
- package/dist/middleware/MxpMiddleware.js.map +1 -1
- package/dist/models/channelKey.d.ts +1 -0
- package/dist/models/channelKey.d.ts.map +1 -1
- package/dist/models/channelKey.js +5 -0
- package/dist/models/channelKey.js.map +1 -1
- package/dist/models/memoryUtility.d.ts +39 -0
- package/dist/models/memoryUtility.d.ts.map +1 -0
- package/dist/models/memoryUtility.js +39 -0
- package/dist/models/memoryUtility.js.map +1 -0
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +23 -1
- package/dist/models/user.js.map +1 -1
- package/dist/mxp/BinaryProtocolLayer.d.ts +24 -8
- package/dist/mxp/BinaryProtocolLayer.d.ts.map +1 -1
- package/dist/mxp/BinaryProtocolLayer.js +90 -103
- package/dist/mxp/BinaryProtocolLayer.js.map +1 -1
- package/dist/mxp/ContextCompressionEngine.d.ts +57 -10
- package/dist/mxp/ContextCompressionEngine.d.ts.map +1 -1
- package/dist/mxp/ContextCompressionEngine.js +170 -128
- package/dist/mxp/ContextCompressionEngine.js.map +1 -1
- package/dist/mxp/MxpTokenOptimizer.d.ts +7 -3
- package/dist/mxp/MxpTokenOptimizer.d.ts.map +1 -1
- package/dist/mxp/MxpTokenOptimizer.js +8 -5
- package/dist/mxp/MxpTokenOptimizer.js.map +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.d.ts +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.d.ts.map +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.js +47 -57
- package/dist/prompts/MxfAgentSystemPrompt.js.map +1 -1
- package/dist/protocols/mcp/ToolError.d.ts +123 -0
- package/dist/protocols/mcp/ToolError.d.ts.map +1 -0
- package/dist/protocols/mcp/ToolError.js +108 -0
- package/dist/protocols/mcp/ToolError.js.map +1 -0
- package/dist/protocols/mcp/defineTool.d.ts +133 -0
- package/dist/protocols/mcp/defineTool.d.ts.map +1 -0
- package/dist/protocols/mcp/defineTool.js +236 -0
- package/dist/protocols/mcp/defineTool.js.map +1 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.d.ts +38 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.d.ts.map +1 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.js +227 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.js.map +1 -0
- package/dist/protocols/mcp/security/McpSecurityGuard.d.ts +9 -1
- package/dist/protocols/mcp/security/McpSecurityGuard.d.ts.map +1 -1
- package/dist/protocols/mcp/security/McpSecurityGuard.js +10 -2
- package/dist/protocols/mcp/security/McpSecurityGuard.js.map +1 -1
- package/dist/protocols/mcp/security/McpToolPolicy.d.ts +81 -0
- package/dist/protocols/mcp/security/McpToolPolicy.d.ts.map +1 -0
- package/dist/protocols/mcp/security/McpToolPolicy.js +202 -0
- package/dist/protocols/mcp/security/McpToolPolicy.js.map +1 -0
- package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts +51 -18
- package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts.map +1 -1
- package/dist/protocols/mcp/services/ExternalMcpServerManager.js +256 -283
- package/dist/protocols/mcp/services/ExternalMcpServerManager.js.map +1 -1
- package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts +39 -6
- package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts.map +1 -1
- package/dist/protocols/mcp/services/ExternalServerConfigs.js +76 -18
- package/dist/protocols/mcp/services/ExternalServerConfigs.js.map +1 -1
- package/dist/protocols/mcp/tools/AnalyticsTools.js +1 -1
- package/dist/protocols/mcp/tools/AnalyticsTools.js.map +1 -1
- package/dist/protocols/mcp/tools/CodeAnalysisTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/CodeAnalysisTools.js +15 -0
- package/dist/protocols/mcp/tools/CodeAnalysisTools.js.map +1 -1
- package/dist/protocols/mcp/tools/EffectivenessTools.js +3 -3
- package/dist/protocols/mcp/tools/EffectivenessTools.js.map +1 -1
- package/dist/protocols/mcp/tools/GitTools.d.ts +9 -369
- package/dist/protocols/mcp/tools/GitTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/GitTools.js +222 -517
- package/dist/protocols/mcp/tools/GitTools.js.map +1 -1
- package/dist/protocols/mcp/tools/InfrastructureTools.d.ts +56 -24
- package/dist/protocols/mcp/tools/InfrastructureTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/InfrastructureTools.js +117 -26
- package/dist/protocols/mcp/tools/InfrastructureTools.js.map +1 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.d.ts +0 -3
- package/dist/protocols/mcp/tools/MemorySearchTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.js +58 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.js.map +1 -1
- package/dist/protocols/mcp/tools/OrparTools.d.ts +8 -0
- package/dist/protocols/mcp/tools/OrparTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/OrparTools.js +66 -0
- package/dist/protocols/mcp/tools/OrparTools.js.map +1 -1
- package/dist/protocols/mcp/tools/SafetyTools.d.ts +7 -525
- package/dist/protocols/mcp/tools/SafetyTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/SafetyTools.js +349 -514
- package/dist/protocols/mcp/tools/SafetyTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TaskPlanningTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TaskPlanningTools.js +56 -26
- package/dist/protocols/mcp/tools/TaskPlanningTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TestTools.d.ts +15 -376
- package/dist/protocols/mcp/tools/TestTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TestTools.js +241 -363
- package/dist/protocols/mcp/tools/TestTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TypeScriptTools.d.ts +6 -276
- package/dist/protocols/mcp/tools/TypeScriptTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TypeScriptTools.js +223 -337
- package/dist/protocols/mcp/tools/TypeScriptTools.js.map +1 -1
- package/dist/protocols/mcp/tools/WebTools.d.ts +15 -728
- package/dist/protocols/mcp/tools/WebTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/WebTools.js +151 -257
- package/dist/protocols/mcp/tools/WebTools.js.map +1 -1
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts +5 -3
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js +31 -6
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js.map +1 -1
- package/dist/schemas/EventPayloadSchema.d.ts +21 -0
- package/dist/schemas/EventPayloadSchema.d.ts.map +1 -1
- package/dist/schemas/EventPayloadSchema.js +14 -0
- package/dist/schemas/EventPayloadSchema.js.map +1 -1
- package/dist/services/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/services/BackgroundTaskManager.js +12 -4
- package/dist/services/BackgroundTaskManager.js.map +1 -1
- package/dist/services/BrowserManager.d.ts +1 -1
- package/dist/services/BrowserManager.d.ts.map +1 -1
- package/dist/services/BrowserManager.js +26 -3
- package/dist/services/BrowserManager.js.map +1 -1
- package/dist/services/MemoryCompressor.d.ts +11 -2
- package/dist/services/MemoryCompressor.d.ts.map +1 -1
- package/dist/services/MemoryCompressor.js +8 -26
- package/dist/services/MemoryCompressor.js.map +1 -1
- package/dist/services/MemoryService.d.ts +16 -3
- package/dist/services/MemoryService.d.ts.map +1 -1
- package/dist/services/MemoryService.js +79 -55
- package/dist/services/MemoryService.js.map +1 -1
- package/dist/services/MxfMLService.d.ts +5 -0
- package/dist/services/MxfMLService.d.ts.map +1 -1
- package/dist/services/MxfMLService.js +32 -6
- package/dist/services/MxfMLService.js.map +1 -1
- package/dist/services/PredictiveAnalyticsService.d.ts +45 -9
- package/dist/services/PredictiveAnalyticsService.d.ts.map +1 -1
- package/dist/services/PredictiveAnalyticsService.js +82 -44
- package/dist/services/PredictiveAnalyticsService.js.map +1 -1
- package/dist/services/QValueManager.d.ts +7 -0
- package/dist/services/QValueManager.d.ts.map +1 -1
- package/dist/services/QValueManager.js +9 -0
- package/dist/services/QValueManager.js.map +1 -1
- package/dist/services/RewardSignalProcessor.d.ts +38 -1
- package/dist/services/RewardSignalProcessor.d.ts.map +1 -1
- package/dist/services/RewardSignalProcessor.js +109 -7
- package/dist/services/RewardSignalProcessor.js.map +1 -1
- package/dist/services/SharedStateService.d.ts +16 -2
- package/dist/services/SharedStateService.d.ts.map +1 -1
- package/dist/services/SharedStateService.js +44 -3
- package/dist/services/SharedStateService.js.map +1 -1
- package/dist/services/TaskEffectivenessService.d.ts +34 -18
- package/dist/services/TaskEffectivenessService.d.ts.map +1 -1
- package/dist/services/TaskEffectivenessService.js +91 -155
- package/dist/services/TaskEffectivenessService.js.map +1 -1
- package/dist/services/WebSearchService.d.ts.map +1 -1
- package/dist/services/WebSearchService.js.map +1 -1
- package/dist/services/dag/TaskDagService.d.ts.map +1 -1
- package/dist/services/dag/TaskDagService.js +2 -0
- package/dist/services/dag/TaskDagService.js.map +1 -1
- package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts +7 -0
- package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts.map +1 -1
- package/dist/services/orpar-memory/OrparMemoryCoordinator.js +71 -10
- package/dist/services/orpar-memory/OrparMemoryCoordinator.js.map +1 -1
- package/dist/types/MemoryUtilityTypes.d.ts +2 -0
- package/dist/types/MemoryUtilityTypes.d.ts.map +1 -1
- package/dist/types/MemoryUtilityTypes.js.map +1 -1
- package/dist/utils/Logger.d.ts +43 -3
- package/dist/utils/Logger.d.ts.map +1 -1
- package/dist/utils/Logger.js +78 -36
- package/dist/utils/Logger.js.map +1 -1
- package/dist/utils/MxpEncryption.d.ts +19 -6
- package/dist/utils/MxpEncryption.d.ts.map +1 -1
- package/dist/utils/MxpEncryption.js +48 -16
- package/dist/utils/MxpEncryption.js.map +1 -1
- package/package.json +19 -4
- package/src/adapters/FrameworkAdapter.ts +1 -1
- package/src/config/ConfigManager.ts +52 -19
- package/src/config/memory-strata.config.ts +136 -0
- package/src/constants/ToolNames.ts +4 -18
- package/src/database/DatabaseAdapterFactory.ts +23 -6
- package/src/database/adapters/mongodb/MongoBaseRepository.ts +16 -2
- package/src/events/ClientEventBus.ts +68 -403
- package/src/events/EventBus.ts +7 -8
- package/src/events/EventBusBase.ts +479 -116
- package/src/events/EventNames.ts +6 -0
- package/src/events/PublicEvents.ts +7 -4
- package/src/events/ServerEventBus.ts +78 -473
- package/src/events/event-definitions/LlmBudgetEvents.ts +74 -0
- package/src/interfaces/IMemoryPersistence.ts +51 -0
- package/src/middleware/MxpMiddleware.ts +51 -24
- package/src/models/channelKey.ts +18 -4
- package/src/models/memoryUtility.ts +64 -0
- package/src/models/user.ts +36 -4
- package/src/mxp/BinaryProtocolLayer.ts +108 -124
- package/src/mxp/ContextCompressionEngine.ts +219 -161
- package/src/mxp/MxpTokenOptimizer.ts +10 -10
- package/src/prompts/MxfAgentSystemPrompt.ts +45 -55
- package/src/protocols/mcp/ToolError.ts +201 -0
- package/src/protocols/mcp/defineTool.ts +379 -0
- package/src/protocols/mcp/security/HttpTargetGuard.ts +267 -0
- package/src/protocols/mcp/security/McpSecurityGuard.ts +10 -3
- package/src/protocols/mcp/security/McpToolPolicy.ts +221 -0
- package/src/protocols/mcp/services/ExternalMcpServerManager.ts +340 -322
- package/src/protocols/mcp/services/ExternalServerConfigs.ts +82 -19
- package/src/protocols/mcp/tools/AnalyticsTools.ts +1 -1
- package/src/protocols/mcp/tools/CodeAnalysisTools.ts +15 -0
- package/src/protocols/mcp/tools/EffectivenessTools.ts +3 -3
- package/src/protocols/mcp/tools/GitTools.ts +290 -569
- package/src/protocols/mcp/tools/InfrastructureTools.ts +189 -43
- package/src/protocols/mcp/tools/MemorySearchTools.ts +77 -1
- package/src/protocols/mcp/tools/OrparTools.ts +87 -0
- package/src/protocols/mcp/tools/SafetyTools.ts +501 -579
- package/src/protocols/mcp/tools/TaskPlanningTools.ts +81 -39
- package/src/protocols/mcp/tools/TestTools.ts +339 -419
- package/src/protocols/mcp/tools/TypeScriptTools.ts +316 -376
- package/src/protocols/mcp/tools/WebTools.ts +209 -307
- package/src/protocols/mcp/tools/shell/ShellExecuteHandler.ts +53 -14
- package/src/schemas/EventPayloadSchema.ts +37 -0
- package/src/services/BackgroundTaskManager.ts +12 -4
- package/src/services/BrowserManager.ts +41 -1
- package/src/services/MemoryCompressor.ts +20 -32
- package/src/services/MemoryService.ts +96 -58
- package/src/services/MxfMLService.ts +41 -7
- package/src/services/PredictiveAnalyticsService.ts +101 -51
- package/src/services/QValueManager.ts +10 -0
- package/src/services/RewardSignalProcessor.ts +138 -9
- package/src/services/SharedStateService.ts +56 -8
- package/src/services/TaskEffectivenessService.ts +114 -187
- package/src/services/WebSearchService.ts +2 -1
- package/src/services/dag/TaskDagService.ts +2 -0
- package/src/services/orpar-memory/OrparMemoryCoordinator.ts +86 -10
- package/src/types/MemoryUtilityTypes.ts +2 -0
- package/src/utils/Logger.ts +116 -40
- package/src/utils/MxpEncryption.ts +69 -34
- package/dist/mxp/MxpEventForwardingEnhancer.d.ts +0 -134
- package/dist/mxp/MxpEventForwardingEnhancer.d.ts.map +0 -1
- package/dist/mxp/MxpEventForwardingEnhancer.js +0 -361
- package/dist/mxp/MxpEventForwardingEnhancer.js.map +0 -1
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts +0 -56
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts.map +0 -1
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.js +0 -57
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.js.map +0 -1
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts +0 -270
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts.map +0 -1
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.js +0 -256
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.js.map +0 -1
- package/dist/protocols/mcp/tools/LspTools.d.ts +0 -495
- package/dist/protocols/mcp/tools/LspTools.d.ts.map +0 -1
- package/dist/protocols/mcp/tools/LspTools.js +0 -469
- package/dist/protocols/mcp/tools/LspTools.js.map +0 -1
- package/src/mxp/MxpEventForwardingEnhancer.ts +0 -471
- package/src/protocols/mcp/services/ToolSchemaEnhancements.ts +0 -92
- package/src/protocols/mcp/tools/ControlLoopLifecycle.ts +0 -302
- package/src/protocols/mcp/tools/LspTools.ts +0 -476
|
@@ -67,7 +67,13 @@ export enum ModelType {
|
|
|
67
67
|
GRADIENT_BOOSTING = 'gradient_boosting',
|
|
68
68
|
NEURAL_NETWORK = 'neural_network',
|
|
69
69
|
ISOLATION_FOREST = 'isolation_forest',
|
|
70
|
-
LSTM = 'lstm'
|
|
70
|
+
LSTM = 'lstm',
|
|
71
|
+
/**
|
|
72
|
+
* A rule-based heuristic, not a trained model. Exists so a heuristic result can say
|
|
73
|
+
* what it is: without it, heuristic predictions were reported to callers as
|
|
74
|
+
* GRADIENT_BOOSTING, alongside a hardcoded accuracy figure.
|
|
75
|
+
*/
|
|
76
|
+
HEURISTIC = 'heuristic'
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
/**
|
|
@@ -188,14 +194,24 @@ export interface RiskScore {
|
|
|
188
194
|
*/
|
|
189
195
|
export interface ModelMetadata {
|
|
190
196
|
modelId: string;
|
|
197
|
+
/**
|
|
198
|
+
* Which predictor this describes ('error_prediction', 'anomaly_detection',
|
|
199
|
+
* 'pattern_forecast'). Identity has to be separate from `type`, because `type` now
|
|
200
|
+
* changes with training state — a predictor is a HEURISTIC until it is actually
|
|
201
|
+
* trained — so callers can no longer identify a predictor by its algorithm label.
|
|
202
|
+
*/
|
|
203
|
+
name: string;
|
|
191
204
|
type: ModelType;
|
|
192
205
|
version: string;
|
|
193
|
-
|
|
206
|
+
/** Null until the predictor has actually been trained. */
|
|
207
|
+
trainedAt: number | null;
|
|
208
|
+
/** Null for heuristics — they have no measured validation accuracy. */
|
|
194
209
|
accuracy: number | null;
|
|
195
210
|
features: string[];
|
|
196
211
|
hyperparameters: Record<string, any>;
|
|
197
212
|
trainingDataSize: number;
|
|
198
|
-
|
|
213
|
+
/** Null until a real validation run has produced metrics. */
|
|
214
|
+
validationMetrics: Record<string, number> | null;
|
|
199
215
|
}
|
|
200
216
|
|
|
201
217
|
/** TF.js model ID for the error prediction Dense classifier */
|
|
@@ -234,8 +250,6 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
234
250
|
// Otherwise falls back to the heuristic distance-based isolation score.
|
|
235
251
|
private tfAnomalyAutoencoderReady: boolean = false;
|
|
236
252
|
|
|
237
|
-
// Models (simplified in-memory implementation)
|
|
238
|
-
private readonly models = new Map<string, any>();
|
|
239
253
|
private readonly modelMetadata = new Map<string, ModelMetadata>();
|
|
240
254
|
|
|
241
255
|
// Training data
|
|
@@ -446,13 +460,10 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
446
460
|
);
|
|
447
461
|
}
|
|
448
462
|
|
|
449
|
-
//
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
await this.trainErrorPredictionModel();
|
|
454
|
-
});
|
|
455
|
-
}
|
|
463
|
+
// Retraining is owned by this service's own interval (startRetrainSchedule),
|
|
464
|
+
// which trains both models sequentially. Do NOT also register an
|
|
465
|
+
// MxfMLService.scheduleRetrain() timer — a second schedule for the same
|
|
466
|
+
// model trains twice per interval and collides on concurrent fit() calls.
|
|
456
467
|
}
|
|
457
468
|
|
|
458
469
|
// =============================================================================
|
|
@@ -549,13 +560,8 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
549
560
|
);
|
|
550
561
|
}
|
|
551
562
|
|
|
552
|
-
//
|
|
553
|
-
|
|
554
|
-
if (config.autoTrainEnabled) {
|
|
555
|
-
mlService.scheduleRetrain(TF_ANOMALY_AUTOENCODER_MODEL_ID, async () => {
|
|
556
|
-
await this.trainAnomalyDetectionModel();
|
|
557
|
-
});
|
|
558
|
-
}
|
|
563
|
+
// Retraining is owned by this service's own interval (startRetrainSchedule) —
|
|
564
|
+
// see the note in initializeTfErrorPrediction().
|
|
559
565
|
}
|
|
560
566
|
|
|
561
567
|
// =============================================================================
|
|
@@ -601,7 +607,9 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
601
607
|
},
|
|
602
608
|
features,
|
|
603
609
|
model: {
|
|
604
|
-
|
|
610
|
+
// Say which it actually was. A heuristic reported as GRADIENT_BOOSTING is
|
|
611
|
+
// how an unvalidated rule of thumb ends up being trusted as a trained model.
|
|
612
|
+
type: usedTfModel ? ModelType.NEURAL_NETWORK : ModelType.HEURISTIC,
|
|
605
613
|
version: modelMeta?.version || '1.0',
|
|
606
614
|
accuracy: modelMeta?.accuracy ?? null
|
|
607
615
|
}
|
|
@@ -1662,37 +1670,36 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
1662
1670
|
/**
|
|
1663
1671
|
* Initialize models
|
|
1664
1672
|
*/
|
|
1673
|
+
/**
|
|
1674
|
+
* Register metadata for the predictors this service exposes.
|
|
1675
|
+
*
|
|
1676
|
+
* Until a model is actually trained (see trainErrorPredictionModel and friends), every
|
|
1677
|
+
* predictor here is a rule-based heuristic and is described as one. Nothing is
|
|
1678
|
+
* reported as trained, and no accuracy is claimed.
|
|
1679
|
+
*
|
|
1680
|
+
* This previously registered three constant functions — `predict: () => ({score: 0.5,
|
|
1681
|
+
* confidence: 0.7})` labelled GRADIENT_BOOSTING, and `sequence[last] * 1.1` labelled
|
|
1682
|
+
* LSTM — together with a hardcoded accuracy of 0.75, a training-set size of 1000, and
|
|
1683
|
+
* invented precision/recall figures, all of which were served to callers through the
|
|
1684
|
+
* predictive_* tools and the REST layer. The predict functions were dead (nothing ever
|
|
1685
|
+
* read this.models), but the fabricated metadata was not: it was read on every
|
|
1686
|
+
* prediction. Both are gone.
|
|
1687
|
+
*/
|
|
1665
1688
|
private initializeModels(): void {
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
this.models.set('error_prediction', {
|
|
1670
|
-
type: ModelType.GRADIENT_BOOSTING,
|
|
1671
|
-
predict: (features: number[]) => ({ score: 0.5, confidence: 0.7 })
|
|
1672
|
-
});
|
|
1673
|
-
|
|
1674
|
-
this.models.set('anomaly_detection', {
|
|
1675
|
-
type: ModelType.ISOLATION_FOREST,
|
|
1676
|
-
predict: (features: number[]) => ({ score: 0.5 })
|
|
1677
|
-
});
|
|
1678
|
-
|
|
1679
|
-
this.models.set('pattern_forecast', {
|
|
1680
|
-
type: ModelType.LSTM,
|
|
1681
|
-
predict: (sequence: number[]) => sequence[sequence.length - 1] * 1.1
|
|
1682
|
-
});
|
|
1683
|
-
|
|
1684
|
-
// Set metadata
|
|
1685
|
-
for (const [name, model] of this.models) {
|
|
1689
|
+
const heuristicPredictors = ['error_prediction', 'anomaly_detection', 'pattern_forecast'];
|
|
1690
|
+
|
|
1691
|
+
for (const name of heuristicPredictors) {
|
|
1686
1692
|
this.modelMetadata.set(name, {
|
|
1687
1693
|
modelId: uuidv4(),
|
|
1688
|
-
|
|
1694
|
+
name,
|
|
1695
|
+
type: ModelType.HEURISTIC,
|
|
1689
1696
|
version: '1.0.0',
|
|
1690
|
-
trainedAt:
|
|
1691
|
-
accuracy:
|
|
1697
|
+
trainedAt: null,
|
|
1698
|
+
accuracy: null, // heuristics have no measured validation accuracy
|
|
1692
1699
|
features: [],
|
|
1693
1700
|
hyperparameters: {},
|
|
1694
|
-
trainingDataSize:
|
|
1695
|
-
validationMetrics:
|
|
1701
|
+
trainingDataSize: 0,
|
|
1702
|
+
validationMetrics: null
|
|
1696
1703
|
});
|
|
1697
1704
|
}
|
|
1698
1705
|
}
|
|
@@ -1724,6 +1731,8 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
1724
1731
|
*
|
|
1725
1732
|
* When TF.js is enabled and sufficient training samples exist:
|
|
1726
1733
|
* - Vectorizes features and binary labels from collected training data
|
|
1734
|
+
* - Requires both label classes to be present (see the class-balance guard
|
|
1735
|
+
* below) — one-class data cannot train a discriminator
|
|
1727
1736
|
* - Calls MxfMLService.train() to run supervised training on the Dense classifier
|
|
1728
1737
|
* - Updates internal model metadata with real training metrics
|
|
1729
1738
|
* - Saves the trained model to persistent storage (GridFS)
|
|
@@ -1779,6 +1788,28 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
1779
1788
|
return;
|
|
1780
1789
|
}
|
|
1781
1790
|
|
|
1791
|
+
// One-class data cannot train a discriminator: binary crossentropy
|
|
1792
|
+
// drives the sigmoid to a constant, reporting perfect accuracy while
|
|
1793
|
+
// learning nothing. Require both classes, each meeting a minority
|
|
1794
|
+
// floor that scales with the sample count.
|
|
1795
|
+
const positives = labels.reduce((sum, label) => sum + label, 0);
|
|
1796
|
+
const negatives = labels.length - positives;
|
|
1797
|
+
const minorityFloor = Math.max(3, Math.ceil(labels.length * 0.02));
|
|
1798
|
+
if (positives < minorityFloor || negatives < minorityFloor) {
|
|
1799
|
+
this.logger.info(
|
|
1800
|
+
`[PredictiveAnalyticsService] Skipping TF.js error prediction training — ` +
|
|
1801
|
+
`class balance too skewed (positives=${positives}, negatives=${negatives}, ` +
|
|
1802
|
+
`minority floor=${minorityFloor}). Heuristic prediction remains active.`
|
|
1803
|
+
);
|
|
1804
|
+
if (!this.tfErrorPredictionReady) {
|
|
1805
|
+
// No usable TF model — keep heuristic bookkeeping current
|
|
1806
|
+
this.updateHeuristicMetadata(features.length);
|
|
1807
|
+
}
|
|
1808
|
+
// A previously trained model (this session or loaded from storage)
|
|
1809
|
+
// keeps serving; skipping retraining must not clobber its metadata.
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1782
1813
|
// Format labels as 2D array for MxfMLService.train()
|
|
1783
1814
|
const yData = labels.map(l => [l]);
|
|
1784
1815
|
|
|
@@ -1792,9 +1823,15 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
1792
1823
|
// Update internal metadata with real training metrics
|
|
1793
1824
|
const modelMeta = this.modelMetadata.get('error_prediction');
|
|
1794
1825
|
if (modelMeta) {
|
|
1826
|
+
// Backed by a trained classifier now, not the heuristic.
|
|
1827
|
+
modelMeta.type = ModelType.NEURAL_NETWORK;
|
|
1795
1828
|
modelMeta.trainedAt = Date.now();
|
|
1796
1829
|
modelMeta.trainingDataSize = features.length;
|
|
1797
|
-
|
|
1830
|
+
// Prefer validation accuracy — training accuracy overstates fit.
|
|
1831
|
+
// If training reported no accuracy, we do not know the accuracy. Say so.
|
|
1832
|
+
// This previously fell back to 0.75 — an invented figure indistinguishable
|
|
1833
|
+
// from a measured one.
|
|
1834
|
+
modelMeta.accuracy = metrics.valAccuracy ?? metrics.accuracy ?? null;
|
|
1798
1835
|
modelMeta.validationMetrics = {
|
|
1799
1836
|
accuracy: metrics.accuracy ?? 0,
|
|
1800
1837
|
val_accuracy: metrics.valAccuracy ?? 0,
|
|
@@ -1916,6 +1953,9 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
1916
1953
|
// Update internal metadata with real training metrics
|
|
1917
1954
|
const modelMeta = this.modelMetadata.get('anomaly_detection');
|
|
1918
1955
|
if (modelMeta) {
|
|
1956
|
+
// A real autoencoder is now backing this predictor, so it stops being a
|
|
1957
|
+
// heuristic and says so.
|
|
1958
|
+
modelMeta.type = ModelType.NEURAL_NETWORK;
|
|
1919
1959
|
modelMeta.trainedAt = Date.now();
|
|
1920
1960
|
modelMeta.trainingDataSize = features.length;
|
|
1921
1961
|
// For autoencoders, accuracy is not directly applicable —
|
|
@@ -2231,13 +2271,23 @@ export class PredictiveAnalyticsService extends EventEmitter {
|
|
|
2231
2271
|
/**
|
|
2232
2272
|
* Start retrain schedule.
|
|
2233
2273
|
*
|
|
2234
|
-
*
|
|
2235
|
-
*
|
|
2236
|
-
*
|
|
2237
|
-
*
|
|
2238
|
-
*
|
|
2274
|
+
* This interval is the single owner of scheduled retraining. trainModels()
|
|
2275
|
+
* trains the error predictor and the anomaly autoencoder sequentially —
|
|
2276
|
+
* TF.js rejects overlapping fit() calls on one model, and a single timer
|
|
2277
|
+
* avoids overlapping training cycles entirely. Registering per-model
|
|
2278
|
+
* MxfMLService.scheduleRetrain() timers alongside this interval caused
|
|
2279
|
+
* duplicate hourly training and fit() collisions.
|
|
2280
|
+
*
|
|
2281
|
+
* When TF.js is enabled, autoTrainEnabled gates scheduled retraining.
|
|
2282
|
+
* When TF.js is disabled, the interval refreshes heuristic metadata.
|
|
2239
2283
|
*/
|
|
2240
2284
|
private startRetrainSchedule(): void {
|
|
2285
|
+
if (isTensorFlowEnabled() && !getTensorFlowConfig().autoTrainEnabled) {
|
|
2286
|
+
this.logger.info(
|
|
2287
|
+
'[PredictiveAnalyticsService] TF.js auto-training disabled, not scheduling model retraining'
|
|
2288
|
+
);
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2241
2291
|
this.retrainInterval = setInterval(() => {
|
|
2242
2292
|
this.trainModels();
|
|
2243
2293
|
}, this.config.retrainInterval);
|
|
@@ -169,6 +169,16 @@ export class QValueManager {
|
|
|
169
169
|
return result;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Whether a Q-value for this memory is already in the cache.
|
|
174
|
+
*
|
|
175
|
+
* Used by MemoryService.hydrateQValues so a retrieval only reads persistence for
|
|
176
|
+
* memories this process has not scored yet.
|
|
177
|
+
*/
|
|
178
|
+
public isCached(memoryId: string): boolean {
|
|
179
|
+
return this.qValueCache.has(memoryId);
|
|
180
|
+
}
|
|
181
|
+
|
|
172
182
|
/**
|
|
173
183
|
* Set Q-value in cache (typically called when loading from persistence)
|
|
174
184
|
*/
|
|
@@ -91,6 +91,15 @@ export class RewardSignalProcessor {
|
|
|
91
91
|
// Memory usage tracking (task ID -> memories used)
|
|
92
92
|
private taskMemoryUsage: Map<string, MemoryUsageRecord[]> = new Map();
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Memories retrieved by an agent that are not yet attributed to a completed task,
|
|
96
|
+
* keyed by `${agentId}:${channelId}`. Drained on task completion.
|
|
97
|
+
*/
|
|
98
|
+
private agentMemoryUsage: Map<string, MemoryUsageRecord[]> = new Map();
|
|
99
|
+
|
|
100
|
+
/** Cap on buffered memories per agent+channel, so the buffer cannot grow unbounded. */
|
|
101
|
+
private static readonly MAX_BUFFERED_MEMORIES_PER_AGENT = 200;
|
|
102
|
+
|
|
94
103
|
// Event listener cleanup functions
|
|
95
104
|
private eventCleanupFns: (() => void)[] = [];
|
|
96
105
|
|
|
@@ -157,6 +166,15 @@ export class RewardSignalProcessor {
|
|
|
157
166
|
EventBus.server.on(Events.Task.COMPLETED, taskCompletedHandler);
|
|
158
167
|
this.eventCleanupFns.push(() => EventBus.server.off(Events.Task.COMPLETED, taskCompletedHandler));
|
|
159
168
|
|
|
169
|
+
// A failed task is the strongest signal MULS has that the memories it surfaced
|
|
170
|
+
// were unhelpful, and it was being thrown away: nothing listened for
|
|
171
|
+
// Events.Task.FAILED, so failure could never lower a Q-value.
|
|
172
|
+
const taskFailedHandler = (payload: any) => {
|
|
173
|
+
this.handleTaskCompleted(payload, 'failure');
|
|
174
|
+
};
|
|
175
|
+
EventBus.server.on(Events.Task.FAILED, taskFailedHandler);
|
|
176
|
+
this.eventCleanupFns.push(() => EventBus.server.off(Events.Task.FAILED, taskFailedHandler));
|
|
177
|
+
|
|
160
178
|
// Listen for analytics task completed events (may have evaluation scores)
|
|
161
179
|
const analyticsCompletedHandler = (payload: any) => {
|
|
162
180
|
this.handleAnalyticsTaskCompleted(payload);
|
|
@@ -183,17 +201,48 @@ export class RewardSignalProcessor {
|
|
|
183
201
|
/**
|
|
184
202
|
* Handle task completion event
|
|
185
203
|
*/
|
|
186
|
-
private async handleTaskCompleted(payload: any): Promise<void> {
|
|
204
|
+
private async handleTaskCompleted(payload: any, forcedStatus?: TaskOutcomeStatus): Promise<void> {
|
|
187
205
|
if (!this.enabled) return;
|
|
188
206
|
|
|
189
207
|
try {
|
|
190
208
|
const taskId = payload.data?.taskId ?? payload.taskId;
|
|
191
|
-
const status = this.mapStatusToOutcome(payload.data?.status ?? payload.status);
|
|
192
209
|
const agentId = payload.agentId ?? payload.data?.agentId;
|
|
193
210
|
const channelId = payload.channelId ?? payload.data?.channelId;
|
|
194
211
|
|
|
195
|
-
//
|
|
196
|
-
|
|
212
|
+
// Task events carry the task object in `data.task` (see TaskEventData), so the
|
|
213
|
+
// real status lives at data.task.status. Reading only data.status meant the
|
|
214
|
+
// status was always undefined, which the mapper then turned into 'partial' —
|
|
215
|
+
// giving every task, successful or not, the same small positive reward.
|
|
216
|
+
const rawStatus =
|
|
217
|
+
payload.data?.task?.status ??
|
|
218
|
+
payload.data?.status ??
|
|
219
|
+
payload.status;
|
|
220
|
+
|
|
221
|
+
const status = forcedStatus ?? this.mapStatusToOutcome(rawStatus);
|
|
222
|
+
|
|
223
|
+
// Never invent an outcome. Attributing a reward from a status we could not read
|
|
224
|
+
// would teach the system from a number nobody measured.
|
|
225
|
+
if (!status) {
|
|
226
|
+
this.logger.warn(
|
|
227
|
+
`[RewardSignalProcessor] Task ${taskId} completed with an unrecognised status ` +
|
|
228
|
+
`(${String(rawStatus)}); skipping reward attribution rather than guessing one.`
|
|
229
|
+
);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Memories used during this task: anything explicitly tracked against the task
|
|
234
|
+
// id, plus anything the agent retrieved in this channel since its last
|
|
235
|
+
// completion (retrieval has no task id to attribute to — see
|
|
236
|
+
// trackAgentMemoriesUsage).
|
|
237
|
+
const explicitlyTracked = this.taskMemoryUsage.get(taskId) ?? [];
|
|
238
|
+
const bufferedForAgent = this.drainAgentMemories(agentId, channelId);
|
|
239
|
+
|
|
240
|
+
const memoriesUsed: MemoryUsageRecord[] = [...explicitlyTracked];
|
|
241
|
+
for (const record of bufferedForAgent) {
|
|
242
|
+
if (!memoriesUsed.some(m => m.memoryId === record.memoryId)) {
|
|
243
|
+
memoriesUsed.push(record);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
197
246
|
|
|
198
247
|
if (memoriesUsed.length === 0) {
|
|
199
248
|
this.logger.debug(`[RewardSignalProcessor] No memories tracked for task ${taskId}`);
|
|
@@ -259,7 +308,14 @@ export class RewardSignalProcessor {
|
|
|
259
308
|
/**
|
|
260
309
|
* Map task status string to TaskOutcomeStatus
|
|
261
310
|
*/
|
|
262
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Map a task status to a reward outcome, or null when the status is unrecognised.
|
|
313
|
+
*
|
|
314
|
+
* Returns null rather than defaulting to 'partial'. The old default meant an
|
|
315
|
+
* unreadable status still earned a positive reward, so memories were reinforced by
|
|
316
|
+
* tasks whose outcome was never actually known.
|
|
317
|
+
*/
|
|
318
|
+
private mapStatusToOutcome(status: string): TaskOutcomeStatus | null {
|
|
263
319
|
switch (status?.toLowerCase()) {
|
|
264
320
|
case 'completed':
|
|
265
321
|
case 'success':
|
|
@@ -276,7 +332,7 @@ export class RewardSignalProcessor {
|
|
|
276
332
|
case 'timed_out':
|
|
277
333
|
return 'timeout';
|
|
278
334
|
default:
|
|
279
|
-
return
|
|
335
|
+
return null; // Unknown status — the caller must skip attribution, not guess.
|
|
280
336
|
}
|
|
281
337
|
}
|
|
282
338
|
|
|
@@ -494,6 +550,66 @@ export class RewardSignalProcessor {
|
|
|
494
550
|
}
|
|
495
551
|
}
|
|
496
552
|
|
|
553
|
+
/**
|
|
554
|
+
* Track memories retrieved by an agent, without needing a task id.
|
|
555
|
+
*
|
|
556
|
+
* Retrieval happens inside MCP tool handlers, and the tool context carries only
|
|
557
|
+
* agentId/channelId — never the task the agent is working on. Rather than plumb a
|
|
558
|
+
* task id through the whole MCP execution path, retrieval records what it returned
|
|
559
|
+
* against the agent+channel, and handleTaskCompleted drains that buffer when the
|
|
560
|
+
* agent's task finishes. A task is executed by one agent in one channel, so the
|
|
561
|
+
* memories buffered for that pair are exactly the ones the task used.
|
|
562
|
+
*
|
|
563
|
+
* This is the call that was missing: nothing in production ever recorded which
|
|
564
|
+
* memories were used, so every task completed with an empty usage list and no
|
|
565
|
+
* reward was ever attributed.
|
|
566
|
+
*/
|
|
567
|
+
public trackAgentMemoriesUsage(
|
|
568
|
+
agentId: string,
|
|
569
|
+
channelId: string,
|
|
570
|
+
memoryIds: string[],
|
|
571
|
+
phase: OrparPhase,
|
|
572
|
+
usageType?: MemoryUsageRecord['usageType']
|
|
573
|
+
): void {
|
|
574
|
+
if (!this.enabled || !this.config.trackMemoryUsage) return;
|
|
575
|
+
if (!agentId || !channelId || memoryIds.length === 0) return;
|
|
576
|
+
|
|
577
|
+
const key = `${agentId}:${channelId}`;
|
|
578
|
+
let records = this.agentMemoryUsage.get(key);
|
|
579
|
+
if (!records) {
|
|
580
|
+
records = [];
|
|
581
|
+
this.agentMemoryUsage.set(key, records);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
for (const memoryId of memoryIds) {
|
|
585
|
+
if (!records.some(r => r.memoryId === memoryId)) {
|
|
586
|
+
records.push({ memoryId, phase, retrievedAt: new Date(), usageType });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Bound the buffer. An agent that retrieves memories without ever completing a
|
|
591
|
+
// task would otherwise grow this map without limit.
|
|
592
|
+
if (records.length > RewardSignalProcessor.MAX_BUFFERED_MEMORIES_PER_AGENT) {
|
|
593
|
+
records.splice(0, records.length - RewardSignalProcessor.MAX_BUFFERED_MEMORIES_PER_AGENT);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
this.logger.debug(
|
|
597
|
+
`[RewardSignalProcessor] Buffered ${memoryIds.length} memories for ${key} (phase=${phase})`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Take and clear the memories buffered for an agent in a channel.
|
|
603
|
+
*/
|
|
604
|
+
private drainAgentMemories(agentId?: string, channelId?: string): MemoryUsageRecord[] {
|
|
605
|
+
if (!agentId || !channelId) return [];
|
|
606
|
+
|
|
607
|
+
const key = `${agentId}:${channelId}`;
|
|
608
|
+
const records = this.agentMemoryUsage.get(key) ?? [];
|
|
609
|
+
this.agentMemoryUsage.delete(key);
|
|
610
|
+
return records;
|
|
611
|
+
}
|
|
612
|
+
|
|
497
613
|
/**
|
|
498
614
|
* Track multiple memories for a task
|
|
499
615
|
*/
|
|
@@ -605,16 +721,29 @@ export class RewardSignalProcessor {
|
|
|
605
721
|
}
|
|
606
722
|
|
|
607
723
|
/**
|
|
608
|
-
* Get tracking statistics
|
|
724
|
+
* Get tracking statistics.
|
|
725
|
+
*
|
|
726
|
+
* `bufferedMemories` counts memories retrieved by agents that are not yet attributed
|
|
727
|
+
* to a completed task (see trackAgentMemoriesUsage).
|
|
609
728
|
*/
|
|
610
|
-
public getTrackingStats(): {
|
|
729
|
+
public getTrackingStats(): {
|
|
730
|
+
activeTasks: number;
|
|
731
|
+
totalMemoriesTracked: number;
|
|
732
|
+
bufferedMemories: number;
|
|
733
|
+
} {
|
|
611
734
|
let totalMemories = 0;
|
|
612
735
|
for (const [, records] of this.taskMemoryUsage) {
|
|
613
736
|
totalMemories += records.length;
|
|
614
737
|
}
|
|
738
|
+
|
|
739
|
+
let bufferedMemories = 0;
|
|
740
|
+
for (const [, records] of this.agentMemoryUsage) {
|
|
741
|
+
bufferedMemories += records.length;
|
|
742
|
+
}
|
|
615
743
|
return {
|
|
616
744
|
activeTasks: this.taskMemoryUsage.size,
|
|
617
|
-
totalMemoriesTracked: totalMemories
|
|
745
|
+
totalMemoriesTracked: totalMemories,
|
|
746
|
+
bufferedMemories
|
|
618
747
|
};
|
|
619
748
|
}
|
|
620
749
|
|
|
@@ -51,16 +51,45 @@ export class SharedStateService {
|
|
|
51
51
|
private static instance: SharedStateService;
|
|
52
52
|
private stateStore = new Map<string, SharedStateEntry>();
|
|
53
53
|
private channelStates = new Map<string, Set<string>>(); // channelId -> Set of stateIds
|
|
54
|
-
|
|
54
|
+
|
|
55
|
+
/** How long an untouched state entry is kept before cleanup (24 hours). */
|
|
56
|
+
private static readonly STATE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
57
|
+
|
|
58
|
+
/** How often the cleanup sweep runs (1 hour). */
|
|
59
|
+
private static readonly CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
|
60
|
+
|
|
61
|
+
private cleanupInterval: NodeJS.Timeout | null = null;
|
|
62
|
+
|
|
55
63
|
private constructor() {
|
|
64
|
+
// This is a process-lifetime singleton holding an unbounded Map, so the
|
|
65
|
+
// sweep has to be driven by something. cleanupOldStates() existed but had
|
|
66
|
+
// no callers anywhere, so stateStore/channelStates simply grew forever.
|
|
67
|
+
this.cleanupInterval = setInterval(() => {
|
|
68
|
+
this.cleanupOldStates();
|
|
69
|
+
}, SharedStateService.CLEANUP_INTERVAL_MS);
|
|
70
|
+
// Background sweep — must not keep the event loop alive on its own.
|
|
71
|
+
this.cleanupInterval.unref();
|
|
72
|
+
|
|
73
|
+
// Do not hold the event loop open on account of a garbage-collection timer.
|
|
74
|
+
this.cleanupInterval.unref?.();
|
|
56
75
|
}
|
|
57
|
-
|
|
76
|
+
|
|
58
77
|
public static getInstance(): SharedStateService {
|
|
59
78
|
if (!SharedStateService.instance) {
|
|
60
79
|
SharedStateService.instance = new SharedStateService();
|
|
61
80
|
}
|
|
62
81
|
return SharedStateService.instance;
|
|
63
82
|
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Stop the cleanup timer. For tests and shutdown.
|
|
86
|
+
*/
|
|
87
|
+
public stopCleanup(): void {
|
|
88
|
+
if (this.cleanupInterval) {
|
|
89
|
+
clearInterval(this.cleanupInterval);
|
|
90
|
+
this.cleanupInterval = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
64
93
|
|
|
65
94
|
/**
|
|
66
95
|
* Store collaborative state that can be referenced by ID
|
|
@@ -178,27 +207,46 @@ export class SharedStateService {
|
|
|
178
207
|
}
|
|
179
208
|
|
|
180
209
|
/**
|
|
181
|
-
*
|
|
210
|
+
* Drop states that have not been touched within the TTL.
|
|
211
|
+
*
|
|
212
|
+
* Runs on a timer from the constructor; also callable directly.
|
|
213
|
+
*
|
|
214
|
+
* @param maxAgeMs Age past which a state is dropped (default: 24 hours)
|
|
215
|
+
* @returns The number of states removed
|
|
182
216
|
*/
|
|
183
|
-
public cleanupOldStates(maxAgeMs: number =
|
|
217
|
+
public cleanupOldStates(maxAgeMs: number = SharedStateService.STATE_TTL_MS): number {
|
|
184
218
|
const now = Date.now();
|
|
185
219
|
const toDelete: string[] = [];
|
|
186
|
-
|
|
220
|
+
|
|
187
221
|
for (const [stateId, state] of this.stateStore.entries()) {
|
|
188
222
|
const age = now - (state.lastUpdatedAt || state.createdAt);
|
|
189
223
|
if (age > maxAgeMs) {
|
|
190
224
|
toDelete.push(stateId);
|
|
191
225
|
}
|
|
192
226
|
}
|
|
193
|
-
|
|
227
|
+
|
|
194
228
|
for (const stateId of toDelete) {
|
|
195
229
|
const state = this.stateStore.get(stateId);
|
|
196
230
|
if (state) {
|
|
197
231
|
this.stateStore.delete(stateId);
|
|
198
|
-
|
|
232
|
+
|
|
233
|
+
const channelStateIds = this.channelStates.get(state.channelId);
|
|
234
|
+
if (channelStateIds) {
|
|
235
|
+
channelStateIds.delete(stateId);
|
|
236
|
+
// Drop the channel entry too, or channelStates grows forever
|
|
237
|
+
// with empty Sets for channels that are long gone.
|
|
238
|
+
if (channelStateIds.size === 0) {
|
|
239
|
+
this.channelStates.delete(state.channelId);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
199
242
|
}
|
|
200
243
|
}
|
|
201
|
-
|
|
244
|
+
|
|
245
|
+
if (toDelete.length > 0) {
|
|
246
|
+
logger.debug(`Removed ${toDelete.length} shared state entries older than ${maxAgeMs}ms`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return toDelete.length;
|
|
202
250
|
}
|
|
203
251
|
|
|
204
252
|
/**
|