0xray 4.0.36 → 4.0.38
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/.grok-plugin/plugin.json +1 -1
- package/.xray/features.json +1 -1
- package/dist/CHANGELOG.md +28 -0
- package/dist/analytics/emerging-pattern-detector.d.ts +12 -1
- package/dist/analytics/emerging-pattern-detector.js +37 -12
- package/dist/analytics/pattern-learning-engine.js +1 -9
- package/dist/analytics/predictive-analytics.js +51 -33
- package/dist/analytics/prompt-pattern-analyzer.d.ts +1 -0
- package/dist/analytics/prompt-pattern-analyzer.js +39 -20
- package/dist/analytics/routing-performance-analyzer.d.ts +2 -0
- package/dist/analytics/routing-performance-analyzer.js +89 -37
- package/dist/analytics/simple-pattern-analyzer.d.ts +4 -0
- package/dist/analytics/simple-pattern-analyzer.js +66 -28
- package/dist/architect/architect-tools.d.ts +3 -0
- package/dist/architect/architect-tools.js +22 -0
- package/dist/cli/commands/grok-install.js +1 -1
- package/dist/cli/commands/hermes-install.js +11 -5
- package/dist/cli/commands/openclaw-install.js +13 -7
- package/dist/cli/commands/opencode-install.js +7 -4
- package/dist/cli/commands/suit-monitor.d.ts +30 -0
- package/dist/cli/commands/suit-monitor.js +128 -0
- package/dist/cli/index.js +52 -15
- package/dist/cli/suit-pulse.d.ts +29 -0
- package/dist/cli/suit-pulse.js +270 -0
- package/dist/delegation/agent-delegator.d.ts +4 -2
- package/dist/delegation/agent-delegator.js +46 -27
- package/dist/delegation/agent-expertise.js +2 -1
- package/dist/delegation/analytics/outcome-tracker.d.ts +6 -0
- package/dist/delegation/analytics/outcome-tracker.js +17 -3
- package/dist/delegation/ast-code-parser.d.ts +5 -0
- package/dist/delegation/ast-code-parser.js +53 -48
- package/dist/delegation/codebase-context-analyzer.d.ts +3 -7
- package/dist/delegation/codebase-context-analyzer.js +188 -159
- package/dist/delegation/session-coordinator.js +3 -2
- package/dist/enforcement/loaders/codex-loader.js +18 -14
- package/dist/enforcement/validators/architecture-validators.js +115 -9
- package/dist/governance/codex-policy.service.d.ts +12 -2
- package/dist/governance/codex-policy.service.js +91 -16
- package/dist/integrations/grok/hooks/session-start.js +6 -6
- package/dist/integrations/hooks/goggles-pipeline.mjs +169 -28
- package/dist/integrations/hooks/goggles-planes.json +172 -8
- package/dist/integrations/hooks/plates.cjs +138 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +63 -5
- package/dist/integrations/hooks/write-home.cjs +59 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/architect-tools.server.d.ts +6 -0
- package/dist/mcps/architect-tools.server.js +19 -18
- package/dist/mcps/orchestrator/server.d.ts +4 -3
- package/dist/mcps/orchestrator/server.js +29 -17
- package/dist/memory-routing/lookup-cache.d.ts +8 -0
- package/dist/memory-routing/lookup-cache.js +72 -0
- package/dist/memory-routing/plates.d.ts +1 -1
- package/dist/memory-routing/plates.js +26 -0
- package/dist/memory-routing/provider-loader.js +3 -0
- package/dist/metrics/agent-metrics.d.ts +14 -5
- package/dist/metrics/agent-metrics.js +362 -224
- package/dist/nucleus/synthesis-consult-receipt.js +38 -16
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +5 -0
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +9 -0
- package/dist/orchestrator/multi-agent-orchestration-coordinator.js +1 -2
- package/dist/orchestrator/orchestrator.js +1 -2
- package/dist/processors/implementations/agents-md-validation-processor.d.ts +0 -1
- package/dist/processors/implementations/agents-md-validation-processor.js +0 -11
- package/dist/processors/implementations/console-log-guard-processor.js +39 -50
- package/dist/processors/implementations/performance-budget-processor.d.ts +0 -5
- package/dist/processors/implementations/performance-budget-processor.js +29 -38
- package/dist/processors/implementations/storytelling-trigger-processor.d.ts +5 -0
- package/dist/processors/implementations/storytelling-trigger-processor.js +69 -16
- package/dist/processors/implementations/test-auto-creation-processor.js +39 -36
- package/dist/reporting/framework-reporting-system.d.ts +3 -0
- package/dist/reporting/framework-reporting-system.js +61 -8
- package/dist/reporting/log-parser.js +188 -105
- package/dist/reporting/metrics.js +42 -18
- package/dist/security/prompt-security-validator.js +8 -7
- package/dist/security/security-auditor.d.ts +4 -0
- package/dist/security/security-auditor.js +132 -57
- package/dist/security/security-hardener.d.ts +2 -0
- package/dist/security/security-hardener.js +31 -6
- package/dist/security/security-headers.d.ts +2 -0
- package/dist/security/security-headers.js +23 -21
- package/dist/security/security-scanner.js +18 -4
- package/dist/session/session-cleanup-manager.d.ts +2 -0
- package/dist/session/session-cleanup-manager.js +29 -8
- package/dist/session/session-monitor.d.ts +14 -4
- package/dist/session/session-monitor.js +208 -106
- package/dist/session/session-state-manager.d.ts +11 -0
- package/dist/session/session-state-manager.js +90 -8
- package/docs-site/docs/plates/activity-log.md +38 -0
- package/docs-site/docs/plates/cursor-compact.md +35 -0
- package/docs-site/docs/plates/grok-compact.md +35 -0
- package/docs-site/docs/plates/index.md +25 -3
- package/docs-site/docs/plates/inference-files.md +35 -0
- package/docs-site/docs/plates/kept-line.md +35 -0
- package/docs-site/docs/plates/lens-gate.md +35 -0
- package/docs-site/docs/plates/lens-page.md +35 -0
- package/docs-site/docs/plates/notes-page.md +35 -0
- package/docs-site/docs/plates/package-face.md +35 -0
- package/docs-site/docs/plates/payload-heat.md +35 -0
- package/docs-site/docs/plates/pickup-stamp.md +35 -0
- package/docs-site/docs/plates/pre-tool.md +35 -0
- package/docs-site/docs/plates/record-map.md +41 -0
- package/docs-site/docs/plates/reflection-page.md +35 -0
- package/docs-site/docs/plates/review.md +57 -0
- package/docs-site/docs/plates/session-capture.md +38 -0
- package/docs-site/docs/plates/site-manual.md +35 -0
- package/docs-site/docs/plates/stamp-plate.md +35 -0
- package/docs-site/docs/plates/station-card.md +35 -0
- package/docs-site/docs/plates/station-heat.md +35 -0
- package/docs-site/docs/plates/suit-organs.md +38 -0
- package/docs-site/docs/plates/suit-settings.md +35 -0
- package/docs-site/docs/plates/suit-wear.md +37 -0
- package/docs-site/docs/plates/trail-state.md +35 -0
- package/docs-site/docs/plates/work-fresh.md +35 -0
- package/docs-site/docs/plates/write-home.md +38 -0
- package/package.json +3 -2
- package/scripts/node/install-bridges.cjs +302 -78
- package/scripts/node/postinstall.cjs +14 -3
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/architect-tools.server.ts +25 -20
- package/src/mcps/orchestrator/server.ts +33 -19
- package/xray/features.json +1 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.js.map +0 -1
|
@@ -13,6 +13,8 @@ export class SessionCleanupManager {
|
|
|
13
13
|
cleanupInterval;
|
|
14
14
|
_sessionMetadata;
|
|
15
15
|
_metadataLoaded = false;
|
|
16
|
+
/** Same object the store holds, so a one-field write does not clone every session. */
|
|
17
|
+
metadataRecord = {};
|
|
16
18
|
sessionMonitor;
|
|
17
19
|
constructor(stateManager, config = {}, sessionMonitor) {
|
|
18
20
|
this.stateManager = stateManager;
|
|
@@ -40,6 +42,7 @@ export class SessionCleanupManager {
|
|
|
40
42
|
set sessionMetadata(value) {
|
|
41
43
|
this._sessionMetadata = value;
|
|
42
44
|
this._metadataLoaded = true;
|
|
45
|
+
this.metadataRecord = Object.fromEntries(value);
|
|
43
46
|
}
|
|
44
47
|
/**
|
|
45
48
|
* Initialize cleanup manager and start auto-cleanup if enabled
|
|
@@ -228,19 +231,21 @@ export class SessionCleanupManager {
|
|
|
228
231
|
const now = Date.now();
|
|
229
232
|
let expiredCount = 0;
|
|
230
233
|
let idleCount = 0;
|
|
234
|
+
let activeSessions = 0;
|
|
231
235
|
for (const metadata of this.sessionMetadata.values()) {
|
|
232
236
|
if (!metadata.isActive)
|
|
233
237
|
continue;
|
|
238
|
+
activeSessions += 1;
|
|
234
239
|
if (now - metadata.createdAt > metadata.ttlMs) {
|
|
235
|
-
expiredCount
|
|
240
|
+
expiredCount += 1;
|
|
236
241
|
}
|
|
237
242
|
else if (now - metadata.lastActivity > this.config.idleTimeoutMs) {
|
|
238
|
-
idleCount
|
|
243
|
+
idleCount += 1;
|
|
239
244
|
}
|
|
240
245
|
}
|
|
241
246
|
return {
|
|
242
247
|
totalSessions: this.sessionMetadata.size,
|
|
243
|
-
activeSessions
|
|
248
|
+
activeSessions,
|
|
244
249
|
expiredSessions: expiredCount,
|
|
245
250
|
idleSessions: idleCount,
|
|
246
251
|
nextCleanup: this.cleanupInterval
|
|
@@ -292,19 +297,29 @@ export class SessionCleanupManager {
|
|
|
292
297
|
* Load session metadata from state manager (lazy loading)
|
|
293
298
|
*/
|
|
294
299
|
loadSessionMetadata() {
|
|
300
|
+
this._sessionMetadata = new Map();
|
|
301
|
+
this.metadataRecord = {};
|
|
295
302
|
try {
|
|
296
303
|
const storedMetadata = this.stateManager.get("cleanup:session_metadata");
|
|
297
|
-
this._sessionMetadata = new Map();
|
|
298
304
|
if (storedMetadata &&
|
|
299
305
|
typeof storedMetadata === "object" &&
|
|
300
306
|
!Array.isArray(storedMetadata)) {
|
|
307
|
+
const loaded = new Map();
|
|
308
|
+
this._sessionMetadata = loaded;
|
|
309
|
+
let dropped = false;
|
|
301
310
|
for (const [sessionId, metadata] of Object.entries(storedMetadata)) {
|
|
302
311
|
if (metadata && typeof metadata === "object" && metadata.sessionId) {
|
|
303
|
-
|
|
312
|
+
loaded.set(sessionId, metadata);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
dropped = true;
|
|
304
316
|
}
|
|
305
317
|
}
|
|
318
|
+
this.metadataRecord = dropped
|
|
319
|
+
? Object.fromEntries(loaded)
|
|
320
|
+
: storedMetadata;
|
|
306
321
|
frameworkLogger.log("session-cleanup", "load-metadata", "info", {
|
|
307
|
-
sessionsLoaded:
|
|
322
|
+
sessionsLoaded: loaded.size,
|
|
308
323
|
});
|
|
309
324
|
}
|
|
310
325
|
else if (storedMetadata) {
|
|
@@ -316,13 +331,19 @@ export class SessionCleanupManager {
|
|
|
316
331
|
error,
|
|
317
332
|
});
|
|
318
333
|
}
|
|
334
|
+
this._metadataLoaded = true;
|
|
319
335
|
}
|
|
320
336
|
/**
|
|
321
337
|
* Persist session metadata to state manager
|
|
322
338
|
*/
|
|
323
339
|
persistSessionMetadata(sessionId, metadata) {
|
|
324
|
-
|
|
325
|
-
|
|
340
|
+
if (this._sessionMetadata?.has(sessionId)) {
|
|
341
|
+
this.metadataRecord[sessionId] = metadata;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
delete this.metadataRecord[sessionId];
|
|
345
|
+
}
|
|
346
|
+
this.stateManager.set("cleanup:session_metadata", this.metadataRecord);
|
|
326
347
|
}
|
|
327
348
|
/**
|
|
328
349
|
* Cleanup a specific session
|
|
@@ -74,6 +74,13 @@ export declare class SessionMonitor {
|
|
|
74
74
|
private interactionHistory;
|
|
75
75
|
private sessionResponseTimes;
|
|
76
76
|
private sessionErrors;
|
|
77
|
+
/** Stable store records. A field write patches one session instead of cloning the map. */
|
|
78
|
+
private healthRecord;
|
|
79
|
+
private metricsRecord;
|
|
80
|
+
private alertRecord;
|
|
81
|
+
private interactionRecord;
|
|
82
|
+
private responseTotals;
|
|
83
|
+
private outcomeTotals;
|
|
77
84
|
constructor(stateManager: XrayStateManager, sessionCoordinator: SessionCoordinator, cleanupManager: SessionCleanupManager, config?: Partial<MonitorConfig>);
|
|
78
85
|
start(): void;
|
|
79
86
|
registerSession(sessionId: string): void;
|
|
@@ -96,14 +103,15 @@ export declare class SessionMonitor {
|
|
|
96
103
|
private startMetricsCollection;
|
|
97
104
|
private generateAlerts;
|
|
98
105
|
private loadPersistedData;
|
|
99
|
-
private
|
|
100
|
-
private persistMetricsData;
|
|
101
|
-
private persistAlertData;
|
|
106
|
+
private persistHealth;
|
|
102
107
|
shutdown(): void;
|
|
103
108
|
/**
|
|
104
109
|
* Record an interaction for tracking
|
|
105
110
|
*/
|
|
106
111
|
recordInteraction(sessionId: string, interaction: InteractionRecord): void;
|
|
112
|
+
private persistInteractions;
|
|
113
|
+
private seedInteractionTotals;
|
|
114
|
+
private adoptStoreRecord;
|
|
107
115
|
/**
|
|
108
116
|
* Get interaction history for a session
|
|
109
117
|
*/
|
|
@@ -115,6 +123,9 @@ export declare class SessionMonitor {
|
|
|
115
123
|
/**
|
|
116
124
|
* Calculate failure ratio from interactions
|
|
117
125
|
*/
|
|
126
|
+
private outcomeOf;
|
|
127
|
+
private responseAverage;
|
|
128
|
+
private hasDiverseRecentAgents;
|
|
118
129
|
private calculateFailureRatio;
|
|
119
130
|
/**
|
|
120
131
|
* Calculate conflict resolution rate from interactions
|
|
@@ -124,7 +135,6 @@ export declare class SessionMonitor {
|
|
|
124
135
|
* Calculate coordination efficiency from interactions
|
|
125
136
|
*/
|
|
126
137
|
private calculateCoordinationEfficiency;
|
|
127
|
-
private persistInteractionData;
|
|
128
138
|
private calculateSessionMemoryUsage;
|
|
129
139
|
private countSessionConflicts;
|
|
130
140
|
private calculateAverageResponseTime;
|
|
@@ -20,6 +20,13 @@ export class SessionMonitor {
|
|
|
20
20
|
interactionHistory = new Map();
|
|
21
21
|
sessionResponseTimes = new Map();
|
|
22
22
|
sessionErrors = new Map();
|
|
23
|
+
/** Stable store records. A field write patches one session instead of cloning the map. */
|
|
24
|
+
healthRecord = {};
|
|
25
|
+
metricsRecord = {};
|
|
26
|
+
alertRecord = {};
|
|
27
|
+
interactionRecord = {};
|
|
28
|
+
responseTotals = new Map();
|
|
29
|
+
outcomeTotals = new Map();
|
|
23
30
|
constructor(stateManager, sessionCoordinator, cleanupManager, config = {}) {
|
|
24
31
|
this.stateManager = stateManager;
|
|
25
32
|
this.sessionCoordinator = sessionCoordinator;
|
|
@@ -61,7 +68,7 @@ export class SessionMonitor {
|
|
|
61
68
|
};
|
|
62
69
|
this.healthChecks.set(sessionId, health);
|
|
63
70
|
this.metricsHistory.set(sessionId, []);
|
|
64
|
-
this.
|
|
71
|
+
this.persistHealth(sessionId);
|
|
65
72
|
frameworkLogger.log("session-monitor", "session-registered", "info", {
|
|
66
73
|
sessionId,
|
|
67
74
|
});
|
|
@@ -75,7 +82,7 @@ export class SessionMonitor {
|
|
|
75
82
|
alert.resolvedAt = Date.now();
|
|
76
83
|
}
|
|
77
84
|
}
|
|
78
|
-
this.
|
|
85
|
+
this.persistHealth(sessionId);
|
|
79
86
|
frameworkLogger.log("session-monitor", "session-unregistered", "info", {
|
|
80
87
|
sessionId,
|
|
81
88
|
});
|
|
@@ -83,10 +90,15 @@ export class SessionMonitor {
|
|
|
83
90
|
async performHealthCheck(sessionId) {
|
|
84
91
|
const jobId = `session-health-check-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
85
92
|
const startTime = Date.now();
|
|
86
|
-
const
|
|
87
|
-
if (!
|
|
93
|
+
const current = this.healthChecks.get(sessionId);
|
|
94
|
+
if (!current) {
|
|
88
95
|
throw new Error(`Session ${sessionId} not registered for monitoring`);
|
|
89
96
|
}
|
|
97
|
+
// Readers keep the last published session until every field is ready.
|
|
98
|
+
const health = {
|
|
99
|
+
...current,
|
|
100
|
+
issues: [...current.issues],
|
|
101
|
+
};
|
|
90
102
|
const issues = [];
|
|
91
103
|
let status = "healthy";
|
|
92
104
|
try {
|
|
@@ -109,7 +121,9 @@ export class SessionMonitor {
|
|
|
109
121
|
}
|
|
110
122
|
else {
|
|
111
123
|
health.activeAgents = sessionStatus.agentCount;
|
|
112
|
-
this.performComprehensiveHealthChecks(sessionId,
|
|
124
|
+
const checked = this.performComprehensiveHealthChecks(sessionId, issues, sessionStatus);
|
|
125
|
+
if (status === "healthy")
|
|
126
|
+
status = checked;
|
|
113
127
|
if (health.errorCount > 10) {
|
|
114
128
|
issues.push(`High error count: ${health.errorCount} errors detected`);
|
|
115
129
|
status = "degraded";
|
|
@@ -118,8 +132,7 @@ export class SessionMonitor {
|
|
|
118
132
|
issues.push(`Critical response time: ${health.responseTime}ms (exceeded 2x threshold)`);
|
|
119
133
|
status = "critical";
|
|
120
134
|
}
|
|
121
|
-
const
|
|
122
|
-
const failedRatio = this.calculateFailureRatio(recentInteractions);
|
|
135
|
+
const failedRatio = this.calculateFailureRatio(sessionId);
|
|
123
136
|
if (failedRatio > this.config.alertThresholds.maxErrorRate) {
|
|
124
137
|
issues.push(`High failure rate: ${(failedRatio * 100).toFixed(1)}% failed interactions`);
|
|
125
138
|
status = status === "healthy" ? "degraded" : status;
|
|
@@ -166,11 +179,12 @@ export class SessionMonitor {
|
|
|
166
179
|
health.lastCheck = Date.now();
|
|
167
180
|
health.responseTime = responseTime;
|
|
168
181
|
health.issues = issues;
|
|
169
|
-
this.
|
|
182
|
+
this.healthChecks.set(sessionId, health);
|
|
183
|
+
this.persistHealth(sessionId);
|
|
170
184
|
if (issues.length > 0 && this.config.enableAlerts) {
|
|
171
185
|
this.generateAlerts(sessionId, issues, status);
|
|
172
186
|
}
|
|
173
|
-
return
|
|
187
|
+
return health;
|
|
174
188
|
}
|
|
175
189
|
collectMetrics(sessionId) {
|
|
176
190
|
const sessionStatus = this.sessionCoordinator.getSessionStatus(sessionId);
|
|
@@ -178,16 +192,10 @@ export class SessionMonitor {
|
|
|
178
192
|
return null;
|
|
179
193
|
const metadata = this.cleanupManager?.getSessionMetadata(sessionId);
|
|
180
194
|
const interactions = this.interactionHistory.get(sessionId) || [];
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const totalInteractions = interactions.length;
|
|
186
|
-
const avgResponseTime = responseTimes.length > 0
|
|
187
|
-
? responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length
|
|
188
|
-
: 0;
|
|
189
|
-
const conflictResolutionRate = this.calculateConflictResolutionRate(sessionId, interactions);
|
|
190
|
-
const coordinationEfficiency = this.calculateCoordinationEfficiency(sessionId, interactions);
|
|
195
|
+
const { success: successfulInteractions, failed: failedInteractions, total: totalInteractions } = this.outcomeOf(sessionId);
|
|
196
|
+
const avgResponseTime = this.responseAverage(sessionId);
|
|
197
|
+
const conflictResolutionRate = this.calculateConflictResolutionRate(sessionId);
|
|
198
|
+
const coordinationEfficiency = this.calculateCoordinationEfficiency(sessionId, interactions ?? []);
|
|
191
199
|
const metrics = {
|
|
192
200
|
timestamp: Date.now(),
|
|
193
201
|
sessionId,
|
|
@@ -200,13 +208,21 @@ export class SessionMonitor {
|
|
|
200
208
|
memoryUsage: metadata?.memoryUsage || this.calculateSessionMemoryUsage(sessionId),
|
|
201
209
|
agentCount: sessionStatus.agentCount,
|
|
202
210
|
};
|
|
203
|
-
|
|
211
|
+
let history = this.metricsHistory.get(sessionId);
|
|
212
|
+
if (!history) {
|
|
213
|
+
history = [];
|
|
214
|
+
this.metricsHistory.set(sessionId, history);
|
|
215
|
+
}
|
|
204
216
|
history.push(metrics);
|
|
205
217
|
if (history.length > 100) {
|
|
206
218
|
history.shift();
|
|
207
219
|
}
|
|
208
|
-
this.
|
|
209
|
-
this.
|
|
220
|
+
this.metricsRecord[sessionId] = history;
|
|
221
|
+
for (const key of Object.keys(this.metricsRecord)) {
|
|
222
|
+
if (!this.metricsHistory.has(key))
|
|
223
|
+
delete this.metricsRecord[key];
|
|
224
|
+
}
|
|
225
|
+
this.stateManager.set("monitor:metrics", this.metricsRecord);
|
|
210
226
|
return metrics;
|
|
211
227
|
}
|
|
212
228
|
getHealthStatus(sessionId) {
|
|
@@ -217,11 +233,14 @@ export class SessionMonitor {
|
|
|
217
233
|
return history.slice(-limit);
|
|
218
234
|
}
|
|
219
235
|
getActiveAlerts(sessionId) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
236
|
+
if (!sessionId)
|
|
237
|
+
return Array.from(this.activeAlerts.values());
|
|
238
|
+
const matched = [];
|
|
239
|
+
for (const alert of this.activeAlerts.values()) {
|
|
240
|
+
if (alert.sessionId === sessionId)
|
|
241
|
+
matched.push(alert);
|
|
223
242
|
}
|
|
224
|
-
return
|
|
243
|
+
return matched;
|
|
225
244
|
}
|
|
226
245
|
resolveAlert(alertId) {
|
|
227
246
|
const alert = this.activeAlerts.get(alertId);
|
|
@@ -229,7 +248,8 @@ export class SessionMonitor {
|
|
|
229
248
|
alert.resolved = true;
|
|
230
249
|
alert.resolvedAt = Date.now();
|
|
231
250
|
this.activeAlerts.delete(alertId);
|
|
232
|
-
this.
|
|
251
|
+
delete this.alertRecord[alertId];
|
|
252
|
+
this.stateManager.set("monitor:alerts", this.alertRecord);
|
|
233
253
|
frameworkLogger.log("session-monitor", "alert-resolved", "info", {
|
|
234
254
|
alertId,
|
|
235
255
|
});
|
|
@@ -314,44 +334,56 @@ export class SessionMonitor {
|
|
|
314
334
|
resolved: false,
|
|
315
335
|
};
|
|
316
336
|
this.activeAlerts.set(alert.id, alert);
|
|
337
|
+
this.alertRecord[alert.id] = alert;
|
|
317
338
|
frameworkLogger.log("session-monitor", "alert-generated", "info", {
|
|
318
339
|
sessionId,
|
|
319
340
|
issue,
|
|
320
341
|
});
|
|
321
342
|
}
|
|
322
|
-
this.
|
|
343
|
+
this.stateManager.set("monitor:alerts", this.alertRecord);
|
|
323
344
|
}
|
|
324
345
|
loadPersistedData() {
|
|
325
346
|
const healthData = this.stateManager.get("monitor:health");
|
|
326
|
-
if (healthData) {
|
|
347
|
+
if (healthData && typeof healthData === "object" && !Array.isArray(healthData)) {
|
|
348
|
+
this.healthRecord = healthData;
|
|
327
349
|
for (const [sessionId, health] of Object.entries(healthData)) {
|
|
328
350
|
this.healthChecks.set(sessionId, health);
|
|
329
351
|
}
|
|
330
352
|
}
|
|
331
353
|
const metricsData = this.stateManager.get("monitor:metrics");
|
|
332
|
-
if (metricsData) {
|
|
354
|
+
if (metricsData && typeof metricsData === "object" && !Array.isArray(metricsData)) {
|
|
355
|
+
this.metricsRecord = metricsData;
|
|
333
356
|
for (const [sessionId, history] of Object.entries(metricsData)) {
|
|
334
357
|
this.metricsHistory.set(sessionId, history);
|
|
335
358
|
}
|
|
336
359
|
}
|
|
337
360
|
const alertData = this.stateManager.get("monitor:alerts");
|
|
338
|
-
if (alertData) {
|
|
361
|
+
if (alertData && typeof alertData === "object" && !Array.isArray(alertData)) {
|
|
362
|
+
this.alertRecord = alertData;
|
|
339
363
|
for (const [alertId, alert] of Object.entries(alertData)) {
|
|
340
364
|
this.activeAlerts.set(alertId, alert);
|
|
341
365
|
}
|
|
342
366
|
}
|
|
367
|
+
const interactionData = this.stateManager.get("monitor:interactions");
|
|
368
|
+
if (interactionData &&
|
|
369
|
+
typeof interactionData === "object" &&
|
|
370
|
+
!Array.isArray(interactionData)) {
|
|
371
|
+
this.interactionRecord = interactionData;
|
|
372
|
+
for (const [sessionId, history] of Object.entries(interactionData)) {
|
|
373
|
+
if (!Array.isArray(history))
|
|
374
|
+
continue;
|
|
375
|
+
this.interactionHistory.set(sessionId, history);
|
|
376
|
+
this.seedInteractionTotals(sessionId, history);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
343
379
|
}
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
this.stateManager.set("monitor:
|
|
351
|
-
}
|
|
352
|
-
persistAlertData() {
|
|
353
|
-
const alertData = Object.fromEntries(this.activeAlerts);
|
|
354
|
-
this.stateManager.set("monitor:alerts", alertData);
|
|
380
|
+
persistHealth(sessionId) {
|
|
381
|
+
const health = this.healthChecks.get(sessionId);
|
|
382
|
+
if (health)
|
|
383
|
+
this.healthRecord[sessionId] = health;
|
|
384
|
+
else
|
|
385
|
+
delete this.healthRecord[sessionId];
|
|
386
|
+
this.stateManager.set("monitor:health", this.healthRecord);
|
|
355
387
|
}
|
|
356
388
|
shutdown() {
|
|
357
389
|
if (this.healthCheckInterval) {
|
|
@@ -368,23 +400,77 @@ export class SessionMonitor {
|
|
|
368
400
|
* Record an interaction for tracking
|
|
369
401
|
*/
|
|
370
402
|
recordInteraction(sessionId, interaction) {
|
|
371
|
-
|
|
372
|
-
|
|
403
|
+
let interactions = this.interactionHistory.get(sessionId);
|
|
404
|
+
if (!interactions) {
|
|
405
|
+
interactions = [];
|
|
406
|
+
this.interactionHistory.set(sessionId, interactions);
|
|
373
407
|
}
|
|
374
|
-
|
|
408
|
+
this.interactionRecord[sessionId] = interactions;
|
|
375
409
|
interactions.push(interaction);
|
|
410
|
+
let outcomes = this.outcomeTotals.get(sessionId);
|
|
411
|
+
if (!outcomes) {
|
|
412
|
+
outcomes = { success: 0, failed: 0 };
|
|
413
|
+
this.outcomeTotals.set(sessionId, outcomes);
|
|
414
|
+
}
|
|
415
|
+
if (interaction.success)
|
|
416
|
+
outcomes.success += 1;
|
|
417
|
+
else
|
|
418
|
+
outcomes.failed += 1;
|
|
376
419
|
if (interactions.length > 100) {
|
|
377
|
-
interactions.shift();
|
|
420
|
+
const removed = interactions.shift();
|
|
421
|
+
if (removed) {
|
|
422
|
+
if (removed.success)
|
|
423
|
+
outcomes.success -= 1;
|
|
424
|
+
else
|
|
425
|
+
outcomes.failed -= 1;
|
|
426
|
+
}
|
|
378
427
|
}
|
|
379
|
-
|
|
380
|
-
|
|
428
|
+
let responseTimes = this.sessionResponseTimes.get(sessionId);
|
|
429
|
+
if (!responseTimes) {
|
|
430
|
+
responseTimes = [];
|
|
431
|
+
this.sessionResponseTimes.set(sessionId, responseTimes);
|
|
381
432
|
}
|
|
382
|
-
|
|
433
|
+
responseTimes.push(interaction.duration);
|
|
434
|
+
const totals = this.responseTotals.get(sessionId) ?? { sum: 0, count: 0 };
|
|
435
|
+
totals.sum += interaction.duration;
|
|
436
|
+
totals.count += 1;
|
|
437
|
+
this.responseTotals.set(sessionId, totals);
|
|
383
438
|
if (!interaction.success) {
|
|
384
439
|
const currentErrors = this.sessionErrors.get(sessionId) || 0;
|
|
385
440
|
this.sessionErrors.set(sessionId, currentErrors + 1);
|
|
386
441
|
}
|
|
387
|
-
this.
|
|
442
|
+
this.persistInteractions();
|
|
443
|
+
}
|
|
444
|
+
persistInteractions() {
|
|
445
|
+
this.interactionRecord = this.adoptStoreRecord("monitor:interactions", this.interactionRecord);
|
|
446
|
+
this.stateManager.set("monitor:interactions", this.interactionRecord);
|
|
447
|
+
}
|
|
448
|
+
seedInteractionTotals(sessionId, history) {
|
|
449
|
+
let success = 0;
|
|
450
|
+
let failed = 0;
|
|
451
|
+
let sum = 0;
|
|
452
|
+
for (const item of history) {
|
|
453
|
+
if (item.success)
|
|
454
|
+
success += 1;
|
|
455
|
+
else
|
|
456
|
+
failed += 1;
|
|
457
|
+
sum += item.duration;
|
|
458
|
+
}
|
|
459
|
+
this.outcomeTotals.set(sessionId, { success, failed });
|
|
460
|
+
this.responseTotals.set(sessionId, { sum, count: history.length });
|
|
461
|
+
}
|
|
462
|
+
adoptStoreRecord(key, record) {
|
|
463
|
+
const existing = this.stateManager.get(key);
|
|
464
|
+
if (!existing ||
|
|
465
|
+
typeof existing !== "object" ||
|
|
466
|
+
Array.isArray(existing) ||
|
|
467
|
+
existing === record) {
|
|
468
|
+
return record;
|
|
469
|
+
}
|
|
470
|
+
for (const [id, value] of Object.entries(record)) {
|
|
471
|
+
existing[id] = value;
|
|
472
|
+
}
|
|
473
|
+
return existing;
|
|
388
474
|
}
|
|
389
475
|
/**
|
|
390
476
|
* Get interaction history for a session
|
|
@@ -396,75 +482,90 @@ export class SessionMonitor {
|
|
|
396
482
|
/**
|
|
397
483
|
* Perform comprehensive health checks on a session
|
|
398
484
|
*/
|
|
399
|
-
performComprehensiveHealthChecks(sessionId,
|
|
485
|
+
performComprehensiveHealthChecks(sessionId, issues, sessionStatus) {
|
|
486
|
+
let status = "healthy";
|
|
400
487
|
if (!sessionStatus.active) {
|
|
401
488
|
issues.push("Session is not active");
|
|
402
|
-
|
|
489
|
+
status = "degraded";
|
|
403
490
|
}
|
|
404
|
-
const interactions = this.interactionHistory.get(sessionId)
|
|
405
|
-
const
|
|
491
|
+
const interactions = this.interactionHistory.get(sessionId);
|
|
492
|
+
const lastInteraction = interactions && interactions.length > 0
|
|
493
|
+
? interactions[interactions.length - 1]
|
|
494
|
+
: undefined;
|
|
406
495
|
const staleThreshold = 5 * 60 * 1000;
|
|
407
|
-
if (
|
|
408
|
-
const
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
issues.push(`Stale session: no activity for ${Math.round(timeSinceLastInteraction / 1000)}s`);
|
|
413
|
-
health.status = "degraded";
|
|
414
|
-
}
|
|
496
|
+
if (lastInteraction) {
|
|
497
|
+
const timeSinceLastInteraction = Date.now() - lastInteraction.timestamp;
|
|
498
|
+
if (timeSinceLastInteraction > staleThreshold) {
|
|
499
|
+
issues.push(`Stale session: no activity for ${Math.round(timeSinceLastInteraction / 1000)}s`);
|
|
500
|
+
status = "degraded";
|
|
415
501
|
}
|
|
416
502
|
}
|
|
417
|
-
const
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
if (avgResponseTime > this.config.alertThresholds.maxResponseTime * 1.5) {
|
|
421
|
-
issues.push(`Elevated response time: ${avgResponseTime.toFixed(0)}ms average`);
|
|
422
|
-
}
|
|
503
|
+
const avgResponseTime = this.responseAverage(sessionId);
|
|
504
|
+
if (avgResponseTime > this.config.alertThresholds.maxResponseTime * 1.5) {
|
|
505
|
+
issues.push(`Elevated response time: ${avgResponseTime.toFixed(0)}ms average`);
|
|
423
506
|
}
|
|
424
|
-
const coordinationEfficiency = this.calculateCoordinationEfficiency(sessionId, interactions);
|
|
507
|
+
const coordinationEfficiency = this.calculateCoordinationEfficiency(sessionId, interactions ?? []);
|
|
425
508
|
if (coordinationEfficiency < this.config.alertThresholds.minCoordinationEfficiency) {
|
|
426
509
|
issues.push(`Low coordination efficiency: ${(coordinationEfficiency * 100).toFixed(0)}%`);
|
|
427
|
-
|
|
510
|
+
status = "degraded";
|
|
428
511
|
}
|
|
512
|
+
return status;
|
|
429
513
|
}
|
|
430
514
|
/**
|
|
431
515
|
* Calculate failure ratio from interactions
|
|
432
516
|
*/
|
|
433
|
-
|
|
434
|
-
|
|
517
|
+
outcomeOf(sessionId) {
|
|
518
|
+
const outcomes = this.outcomeTotals.get(sessionId);
|
|
519
|
+
const success = outcomes?.success ?? 0;
|
|
520
|
+
const failed = outcomes?.failed ?? 0;
|
|
521
|
+
return { success, failed, total: success + failed };
|
|
522
|
+
}
|
|
523
|
+
responseAverage(sessionId) {
|
|
524
|
+
const totals = this.responseTotals.get(sessionId);
|
|
525
|
+
if (!totals || totals.count === 0)
|
|
526
|
+
return 0;
|
|
527
|
+
return totals.sum / totals.count;
|
|
528
|
+
}
|
|
529
|
+
hasDiverseRecentAgents(interactions) {
|
|
530
|
+
const start = Math.max(0, interactions.length - 10);
|
|
531
|
+
let first;
|
|
532
|
+
for (let index = start; index < interactions.length; index += 1) {
|
|
533
|
+
const agentId = interactions[index]?.agentId;
|
|
534
|
+
if (!agentId)
|
|
535
|
+
continue;
|
|
536
|
+
if (first === undefined)
|
|
537
|
+
first = agentId;
|
|
538
|
+
else if (agentId !== first)
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
calculateFailureRatio(sessionId) {
|
|
544
|
+
const { failed, total } = this.outcomeOf(sessionId);
|
|
545
|
+
if (total === 0)
|
|
435
546
|
return 0;
|
|
436
|
-
|
|
437
|
-
return failures / interactions.length;
|
|
547
|
+
return failed / total;
|
|
438
548
|
}
|
|
439
549
|
/**
|
|
440
550
|
* Calculate conflict resolution rate from interactions
|
|
441
551
|
*/
|
|
442
|
-
calculateConflictResolutionRate(sessionId
|
|
443
|
-
|
|
552
|
+
calculateConflictResolutionRate(sessionId) {
|
|
553
|
+
const { success, total } = this.outcomeOf(sessionId);
|
|
554
|
+
if (total === 0)
|
|
444
555
|
return 1.0;
|
|
445
|
-
|
|
446
|
-
return successfulInteractions / interactions.length;
|
|
556
|
+
return success / total;
|
|
447
557
|
}
|
|
448
558
|
/**
|
|
449
559
|
* Calculate coordination efficiency from interactions
|
|
450
560
|
*/
|
|
451
561
|
calculateCoordinationEfficiency(sessionId, interactions) {
|
|
452
|
-
|
|
562
|
+
const { success, total } = this.outcomeOf(sessionId);
|
|
563
|
+
if (total === 0)
|
|
453
564
|
return 1.0;
|
|
454
|
-
const
|
|
455
|
-
const
|
|
456
|
-
const recentInteractions = interactions.slice(-10);
|
|
457
|
-
const hasMultipleAgents = new Set(recentInteractions.map(i => i.agentId).filter(Boolean)).size > 1;
|
|
458
|
-
const agentDiversityBonus = hasMultipleAgents ? 0.1 : 0;
|
|
565
|
+
const baseEfficiency = success / total;
|
|
566
|
+
const agentDiversityBonus = this.hasDiverseRecentAgents(interactions) ? 0.1 : 0;
|
|
459
567
|
return Math.min(1.0, baseEfficiency + agentDiversityBonus);
|
|
460
568
|
}
|
|
461
|
-
persistInteractionData() {
|
|
462
|
-
const interactionData = {};
|
|
463
|
-
for (const [sessionId, history] of this.interactionHistory) {
|
|
464
|
-
interactionData[sessionId] = history;
|
|
465
|
-
}
|
|
466
|
-
this.stateManager.set("monitor:interactions", interactionData);
|
|
467
|
-
}
|
|
468
569
|
calculateSessionMemoryUsage(sessionId) {
|
|
469
570
|
// Estimate memory usage based on session activity
|
|
470
571
|
const interactions = this.interactionHistory.get(sessionId) || [];
|
|
@@ -475,26 +576,27 @@ export class SessionMonitor {
|
|
|
475
576
|
return baseMemory + totalInteractions * perInteractionMemory;
|
|
476
577
|
}
|
|
477
578
|
countSessionConflicts(sessionId) {
|
|
478
|
-
// Estimate conflicts based on failed interactions
|
|
479
|
-
const interactions = this.interactionHistory.get(sessionId) || [];
|
|
480
|
-
const failedInteractions = interactions.filter(i => !i.success).length;
|
|
481
579
|
// Conflicts are estimated as failed interactions that might indicate coordination issues
|
|
482
|
-
return Math.floor(
|
|
580
|
+
return Math.floor(this.outcomeOf(sessionId).failed * 0.1);
|
|
483
581
|
}
|
|
484
582
|
calculateAverageResponseTime(sessionId) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (history.length === 0)
|
|
583
|
+
const history = this.metricsHistory.get(sessionId);
|
|
584
|
+
if (!history || history.length === 0)
|
|
488
585
|
return 0;
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
586
|
+
const start = Math.max(0, history.length - 10);
|
|
587
|
+
let totalResponseTime = 0;
|
|
588
|
+
let counted = 0;
|
|
589
|
+
for (let index = start; index < history.length; index += 1) {
|
|
590
|
+
const metric = history[index];
|
|
591
|
+
if (!metric)
|
|
592
|
+
continue;
|
|
593
|
+
totalResponseTime +=
|
|
594
|
+
metric.successfulInteractions > 0
|
|
494
595
|
? 1000 / metric.successfulInteractions
|
|
495
|
-
: 1000
|
|
496
|
-
|
|
497
|
-
|
|
596
|
+
: 1000;
|
|
597
|
+
counted += 1;
|
|
598
|
+
}
|
|
599
|
+
return counted === 0 ? 0 : totalResponseTime / counted;
|
|
498
600
|
}
|
|
499
601
|
}
|
|
500
602
|
export const createSessionMonitor = (stateManager, sessionCoordinator, cleanupManager, config) => {
|
|
@@ -48,6 +48,10 @@ export declare class SessionStateManager {
|
|
|
48
48
|
private dependencies;
|
|
49
49
|
private sessionGroups;
|
|
50
50
|
private failoverConfigs;
|
|
51
|
+
/** Stable store records. A field write patches one key instead of cloning the map. */
|
|
52
|
+
private dependencyRecord;
|
|
53
|
+
private groupRecord;
|
|
54
|
+
private failoverRecord;
|
|
51
55
|
constructor(stateManager: XrayStateManager, sessionCoordinator: SessionCoordinator);
|
|
52
56
|
/**
|
|
53
57
|
* Share state between sessions
|
|
@@ -136,9 +140,16 @@ export declare class SessionStateManager {
|
|
|
136
140
|
};
|
|
137
141
|
private propagateDependencyUpdate;
|
|
138
142
|
private rollbackMigration;
|
|
143
|
+
private trackDependency;
|
|
144
|
+
private trackGroup;
|
|
145
|
+
private trackFailover;
|
|
139
146
|
private persistDependencies;
|
|
140
147
|
private persistSessionGroups;
|
|
141
148
|
private persistFailoverConfigs;
|
|
149
|
+
private loadPersistedRecords;
|
|
150
|
+
private readStoreRecord;
|
|
151
|
+
/** Patch the stored object so a pre-seeded key survives the first write. */
|
|
152
|
+
private adoptStoreRecord;
|
|
142
153
|
shutdown(): void;
|
|
143
154
|
}
|
|
144
155
|
export declare const createSessionStateManager: (stateManager: XrayStateManager, sessionCoordinator: SessionCoordinator) => SessionStateManager;
|