@sparkleideas/shared 3.0.0-alpha.7
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 +323 -0
- package/__tests__/hooks/bash-safety.test.ts +289 -0
- package/__tests__/hooks/file-organization.test.ts +335 -0
- package/__tests__/hooks/git-commit.test.ts +336 -0
- package/__tests__/hooks/index.ts +23 -0
- package/__tests__/hooks/session-hooks.test.ts +357 -0
- package/__tests__/hooks/task-hooks.test.ts +193 -0
- package/docs/EVENTS_IMPLEMENTATION_SUMMARY.md +388 -0
- package/docs/EVENTS_QUICK_REFERENCE.md +470 -0
- package/docs/EVENTS_README.md +352 -0
- package/package.json +39 -0
- package/src/core/config/defaults.ts +207 -0
- package/src/core/config/index.ts +15 -0
- package/src/core/config/loader.ts +271 -0
- package/src/core/config/schema.ts +188 -0
- package/src/core/config/validator.ts +209 -0
- package/src/core/event-bus.ts +236 -0
- package/src/core/index.ts +22 -0
- package/src/core/interfaces/agent.interface.ts +251 -0
- package/src/core/interfaces/coordinator.interface.ts +363 -0
- package/src/core/interfaces/event.interface.ts +267 -0
- package/src/core/interfaces/index.ts +19 -0
- package/src/core/interfaces/memory.interface.ts +332 -0
- package/src/core/interfaces/task.interface.ts +223 -0
- package/src/core/orchestrator/event-coordinator.ts +122 -0
- package/src/core/orchestrator/health-monitor.ts +214 -0
- package/src/core/orchestrator/index.ts +89 -0
- package/src/core/orchestrator/lifecycle-manager.ts +263 -0
- package/src/core/orchestrator/session-manager.ts +279 -0
- package/src/core/orchestrator/task-manager.ts +317 -0
- package/src/events/domain-events.ts +584 -0
- package/src/events/event-store.test.ts +387 -0
- package/src/events/event-store.ts +588 -0
- package/src/events/example-usage.ts +293 -0
- package/src/events/index.ts +90 -0
- package/src/events/projections.ts +561 -0
- package/src/events/state-reconstructor.ts +349 -0
- package/src/events.ts +367 -0
- package/src/hooks/INTEGRATION.md +658 -0
- package/src/hooks/README.md +532 -0
- package/src/hooks/example-usage.ts +499 -0
- package/src/hooks/executor.ts +379 -0
- package/src/hooks/hooks.test.ts +421 -0
- package/src/hooks/index.ts +131 -0
- package/src/hooks/registry.ts +333 -0
- package/src/hooks/safety/bash-safety.ts +604 -0
- package/src/hooks/safety/file-organization.ts +473 -0
- package/src/hooks/safety/git-commit.ts +623 -0
- package/src/hooks/safety/index.ts +46 -0
- package/src/hooks/session-hooks.ts +559 -0
- package/src/hooks/task-hooks.ts +513 -0
- package/src/hooks/types.ts +357 -0
- package/src/hooks/verify-exports.test.ts +125 -0
- package/src/index.ts +195 -0
- package/src/mcp/connection-pool.ts +438 -0
- package/src/mcp/index.ts +183 -0
- package/src/mcp/server.ts +774 -0
- package/src/mcp/session-manager.ts +428 -0
- package/src/mcp/tool-registry.ts +566 -0
- package/src/mcp/transport/http.ts +557 -0
- package/src/mcp/transport/index.ts +294 -0
- package/src/mcp/transport/stdio.ts +324 -0
- package/src/mcp/transport/websocket.ts +484 -0
- package/src/mcp/types.ts +565 -0
- package/src/plugin-interface.ts +663 -0
- package/src/plugin-loader.ts +638 -0
- package/src/plugin-registry.ts +604 -0
- package/src/plugins/index.ts +34 -0
- package/src/plugins/official/hive-mind-plugin.ts +330 -0
- package/src/plugins/official/index.ts +24 -0
- package/src/plugins/official/maestro-plugin.ts +508 -0
- package/src/plugins/types.ts +108 -0
- package/src/resilience/bulkhead.ts +277 -0
- package/src/resilience/circuit-breaker.ts +326 -0
- package/src/resilience/index.ts +26 -0
- package/src/resilience/rate-limiter.ts +420 -0
- package/src/resilience/retry.ts +224 -0
- package/src/security/index.ts +39 -0
- package/src/security/input-validation.ts +265 -0
- package/src/security/secure-random.ts +159 -0
- package/src/services/index.ts +16 -0
- package/src/services/v3-progress.service.ts +505 -0
- package/src/types/agent.types.ts +144 -0
- package/src/types/index.ts +22 -0
- package/src/types/mcp.types.ts +300 -0
- package/src/types/memory.types.ts +263 -0
- package/src/types/swarm.types.ts +255 -0
- package/src/types/task.types.ts +205 -0
- package/src/types.ts +367 -0
- package/src/utils/secure-logger.d.ts +69 -0
- package/src/utils/secure-logger.d.ts.map +1 -0
- package/src/utils/secure-logger.js +208 -0
- package/src/utils/secure-logger.js.map +1 -0
- package/src/utils/secure-logger.ts +257 -0
- package/tmp.json +0 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 MCP Session Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages MCP sessions with:
|
|
5
|
+
* - Session lifecycle management
|
|
6
|
+
* - Authentication integration
|
|
7
|
+
* - Session timeout handling
|
|
8
|
+
* - Concurrent session support
|
|
9
|
+
* - Session metrics and monitoring
|
|
10
|
+
*
|
|
11
|
+
* Performance Targets:
|
|
12
|
+
* - Session creation: <5ms
|
|
13
|
+
* - Session lookup: <1ms
|
|
14
|
+
* - Session cleanup: <10ms
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { EventEmitter } from 'events';
|
|
18
|
+
import {
|
|
19
|
+
MCPSession,
|
|
20
|
+
SessionState,
|
|
21
|
+
SessionMetrics,
|
|
22
|
+
MCPInitializeParams,
|
|
23
|
+
MCPCapabilities,
|
|
24
|
+
MCPClientInfo,
|
|
25
|
+
MCPProtocolVersion,
|
|
26
|
+
TransportType,
|
|
27
|
+
ILogger,
|
|
28
|
+
} from './types.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Session configuration
|
|
32
|
+
*/
|
|
33
|
+
export interface SessionConfig {
|
|
34
|
+
maxSessions?: number;
|
|
35
|
+
sessionTimeout?: number;
|
|
36
|
+
cleanupInterval?: number;
|
|
37
|
+
enableMetrics?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Default session configuration
|
|
42
|
+
*/
|
|
43
|
+
const DEFAULT_SESSION_CONFIG: Required<SessionConfig> = {
|
|
44
|
+
maxSessions: 100,
|
|
45
|
+
sessionTimeout: 30 * 60 * 1000, // 30 minutes
|
|
46
|
+
cleanupInterval: 60 * 1000, // 1 minute
|
|
47
|
+
enableMetrics: true,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Session Manager
|
|
52
|
+
*
|
|
53
|
+
* Handles creation, tracking, and cleanup of MCP sessions
|
|
54
|
+
*/
|
|
55
|
+
export class SessionManager extends EventEmitter {
|
|
56
|
+
private readonly sessions: Map<string, MCPSession> = new Map();
|
|
57
|
+
private readonly config: Required<SessionConfig>;
|
|
58
|
+
private cleanupTimer?: NodeJS.Timeout;
|
|
59
|
+
private sessionCounter = 0;
|
|
60
|
+
|
|
61
|
+
// Statistics
|
|
62
|
+
private totalCreated = 0;
|
|
63
|
+
private totalClosed = 0;
|
|
64
|
+
private totalExpired = 0;
|
|
65
|
+
|
|
66
|
+
constructor(
|
|
67
|
+
private readonly logger: ILogger,
|
|
68
|
+
config: SessionConfig = {}
|
|
69
|
+
) {
|
|
70
|
+
super();
|
|
71
|
+
this.config = { ...DEFAULT_SESSION_CONFIG, ...config };
|
|
72
|
+
this.startCleanupTimer();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create a new session
|
|
77
|
+
*/
|
|
78
|
+
createSession(transport: TransportType): MCPSession {
|
|
79
|
+
// Check max sessions
|
|
80
|
+
if (this.sessions.size >= this.config.maxSessions) {
|
|
81
|
+
throw new Error(`Maximum sessions (${this.config.maxSessions}) reached`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const id = this.generateSessionId();
|
|
85
|
+
const now = new Date();
|
|
86
|
+
|
|
87
|
+
const session: MCPSession = {
|
|
88
|
+
id,
|
|
89
|
+
state: 'created',
|
|
90
|
+
transport,
|
|
91
|
+
createdAt: now,
|
|
92
|
+
lastActivityAt: now,
|
|
93
|
+
isInitialized: false,
|
|
94
|
+
isAuthenticated: false,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
this.sessions.set(id, session);
|
|
98
|
+
this.totalCreated++;
|
|
99
|
+
|
|
100
|
+
this.logger.debug('Session created', { id, transport });
|
|
101
|
+
this.emit('session:created', session);
|
|
102
|
+
|
|
103
|
+
return session;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Initialize a session with client information
|
|
108
|
+
*/
|
|
109
|
+
initializeSession(
|
|
110
|
+
sessionId: string,
|
|
111
|
+
params: MCPInitializeParams
|
|
112
|
+
): MCPSession | undefined {
|
|
113
|
+
const session = this.sessions.get(sessionId);
|
|
114
|
+
if (!session) {
|
|
115
|
+
this.logger.warn('Session not found for initialization', { sessionId });
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
session.state = 'ready';
|
|
120
|
+
session.isInitialized = true;
|
|
121
|
+
session.clientInfo = params.clientInfo;
|
|
122
|
+
session.protocolVersion = params.protocolVersion;
|
|
123
|
+
session.capabilities = params.capabilities;
|
|
124
|
+
session.lastActivityAt = new Date();
|
|
125
|
+
|
|
126
|
+
this.logger.info('Session initialized', {
|
|
127
|
+
sessionId,
|
|
128
|
+
clientInfo: params.clientInfo,
|
|
129
|
+
protocolVersion: params.protocolVersion,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
this.emit('session:initialized', session);
|
|
133
|
+
return session;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Authenticate a session
|
|
138
|
+
*/
|
|
139
|
+
authenticateSession(sessionId: string): boolean {
|
|
140
|
+
const session = this.sessions.get(sessionId);
|
|
141
|
+
if (!session) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
session.isAuthenticated = true;
|
|
146
|
+
session.lastActivityAt = new Date();
|
|
147
|
+
|
|
148
|
+
this.logger.debug('Session authenticated', { sessionId });
|
|
149
|
+
this.emit('session:authenticated', session);
|
|
150
|
+
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get a session by ID
|
|
156
|
+
*/
|
|
157
|
+
getSession(sessionId: string): MCPSession | undefined {
|
|
158
|
+
return this.sessions.get(sessionId);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if session exists
|
|
163
|
+
*/
|
|
164
|
+
hasSession(sessionId: string): boolean {
|
|
165
|
+
return this.sessions.has(sessionId);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get all active sessions
|
|
170
|
+
*/
|
|
171
|
+
getActiveSessions(): MCPSession[] {
|
|
172
|
+
return Array.from(this.sessions.values()).filter(
|
|
173
|
+
(s) => s.state === 'ready' || s.state === 'created' || s.state === 'initializing'
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Update session activity timestamp
|
|
179
|
+
*/
|
|
180
|
+
updateActivity(sessionId: string): boolean {
|
|
181
|
+
const session = this.sessions.get(sessionId);
|
|
182
|
+
if (!session) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
session.lastActivityAt = new Date();
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Set session state
|
|
192
|
+
*/
|
|
193
|
+
setState(sessionId: string, state: SessionState): boolean {
|
|
194
|
+
const session = this.sessions.get(sessionId);
|
|
195
|
+
if (!session) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const oldState = session.state;
|
|
200
|
+
session.state = state;
|
|
201
|
+
session.lastActivityAt = new Date();
|
|
202
|
+
|
|
203
|
+
this.logger.debug('Session state changed', {
|
|
204
|
+
sessionId,
|
|
205
|
+
oldState,
|
|
206
|
+
newState: state,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
this.emit('session:stateChanged', { session, oldState, newState: state });
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Set session metadata
|
|
215
|
+
*/
|
|
216
|
+
setMetadata(sessionId: string, key: string, value: unknown): boolean {
|
|
217
|
+
const session = this.sessions.get(sessionId);
|
|
218
|
+
if (!session) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!session.metadata) {
|
|
223
|
+
session.metadata = {};
|
|
224
|
+
}
|
|
225
|
+
session.metadata[key] = value;
|
|
226
|
+
session.lastActivityAt = new Date();
|
|
227
|
+
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get session metadata
|
|
233
|
+
*/
|
|
234
|
+
getMetadata(sessionId: string, key: string): unknown {
|
|
235
|
+
const session = this.sessions.get(sessionId);
|
|
236
|
+
return session?.metadata?.[key];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Close a session
|
|
241
|
+
*/
|
|
242
|
+
closeSession(sessionId: string, reason?: string): boolean {
|
|
243
|
+
const session = this.sessions.get(sessionId);
|
|
244
|
+
if (!session) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
session.state = 'closed';
|
|
249
|
+
this.sessions.delete(sessionId);
|
|
250
|
+
this.totalClosed++;
|
|
251
|
+
|
|
252
|
+
this.logger.info('Session closed', { sessionId, reason });
|
|
253
|
+
this.emit('session:closed', { session, reason });
|
|
254
|
+
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Remove a session (alias for closeSession)
|
|
260
|
+
*/
|
|
261
|
+
removeSession(sessionId: string): boolean {
|
|
262
|
+
return this.closeSession(sessionId);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get session metrics
|
|
267
|
+
*/
|
|
268
|
+
getSessionMetrics(): SessionMetrics {
|
|
269
|
+
let authenticated = 0;
|
|
270
|
+
let active = 0;
|
|
271
|
+
|
|
272
|
+
for (const session of this.sessions.values()) {
|
|
273
|
+
if (session.isAuthenticated) authenticated++;
|
|
274
|
+
if (session.state === 'ready') active++;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
total: this.sessions.size,
|
|
279
|
+
active,
|
|
280
|
+
authenticated,
|
|
281
|
+
expired: this.totalExpired,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get detailed statistics
|
|
287
|
+
*/
|
|
288
|
+
getStats(): {
|
|
289
|
+
total: number;
|
|
290
|
+
byState: Record<SessionState, number>;
|
|
291
|
+
byTransport: Record<TransportType, number>;
|
|
292
|
+
totalCreated: number;
|
|
293
|
+
totalClosed: number;
|
|
294
|
+
totalExpired: number;
|
|
295
|
+
oldestSession?: Date;
|
|
296
|
+
newestSession?: Date;
|
|
297
|
+
} {
|
|
298
|
+
const byState: Record<string, number> = {
|
|
299
|
+
created: 0,
|
|
300
|
+
initializing: 0,
|
|
301
|
+
ready: 0,
|
|
302
|
+
closing: 0,
|
|
303
|
+
closed: 0,
|
|
304
|
+
error: 0,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const byTransport: Record<string, number> = {
|
|
308
|
+
stdio: 0,
|
|
309
|
+
http: 0,
|
|
310
|
+
websocket: 0,
|
|
311
|
+
'in-process': 0,
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
let oldest: Date | undefined;
|
|
315
|
+
let newest: Date | undefined;
|
|
316
|
+
|
|
317
|
+
for (const session of this.sessions.values()) {
|
|
318
|
+
byState[session.state] = (byState[session.state] || 0) + 1;
|
|
319
|
+
byTransport[session.transport] = (byTransport[session.transport] || 0) + 1;
|
|
320
|
+
|
|
321
|
+
if (!oldest || session.createdAt < oldest) {
|
|
322
|
+
oldest = session.createdAt;
|
|
323
|
+
}
|
|
324
|
+
if (!newest || session.createdAt > newest) {
|
|
325
|
+
newest = session.createdAt;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
total: this.sessions.size,
|
|
331
|
+
byState: byState as Record<SessionState, number>,
|
|
332
|
+
byTransport: byTransport as Record<TransportType, number>,
|
|
333
|
+
totalCreated: this.totalCreated,
|
|
334
|
+
totalClosed: this.totalClosed,
|
|
335
|
+
totalExpired: this.totalExpired,
|
|
336
|
+
oldestSession: oldest,
|
|
337
|
+
newestSession: newest,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Clean up expired sessions
|
|
343
|
+
*/
|
|
344
|
+
cleanupExpiredSessions(): number {
|
|
345
|
+
const now = Date.now();
|
|
346
|
+
const expired: string[] = [];
|
|
347
|
+
|
|
348
|
+
for (const [id, session] of this.sessions) {
|
|
349
|
+
const inactiveTime = now - session.lastActivityAt.getTime();
|
|
350
|
+
if (inactiveTime > this.config.sessionTimeout) {
|
|
351
|
+
expired.push(id);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
for (const id of expired) {
|
|
356
|
+
const session = this.sessions.get(id);
|
|
357
|
+
if (session) {
|
|
358
|
+
session.state = 'closed';
|
|
359
|
+
this.sessions.delete(id);
|
|
360
|
+
this.totalExpired++;
|
|
361
|
+
this.logger.info('Session expired', { sessionId: id });
|
|
362
|
+
this.emit('session:expired', session);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (expired.length > 0) {
|
|
367
|
+
this.logger.info('Cleaned up expired sessions', { count: expired.length });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return expired.length;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Start cleanup timer
|
|
375
|
+
*/
|
|
376
|
+
private startCleanupTimer(): void {
|
|
377
|
+
this.cleanupTimer = setInterval(() => {
|
|
378
|
+
this.cleanupExpiredSessions();
|
|
379
|
+
}, this.config.cleanupInterval);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Stop cleanup timer
|
|
384
|
+
*/
|
|
385
|
+
private stopCleanupTimer(): void {
|
|
386
|
+
if (this.cleanupTimer) {
|
|
387
|
+
clearInterval(this.cleanupTimer);
|
|
388
|
+
this.cleanupTimer = undefined;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Generate a unique session ID
|
|
394
|
+
*/
|
|
395
|
+
private generateSessionId(): string {
|
|
396
|
+
return `session-${++this.sessionCounter}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Clear all sessions
|
|
401
|
+
*/
|
|
402
|
+
clearAll(): void {
|
|
403
|
+
for (const id of this.sessions.keys()) {
|
|
404
|
+
this.closeSession(id, 'Session manager cleared');
|
|
405
|
+
}
|
|
406
|
+
this.logger.info('All sessions cleared');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Destroy the session manager
|
|
411
|
+
*/
|
|
412
|
+
destroy(): void {
|
|
413
|
+
this.stopCleanupTimer();
|
|
414
|
+
this.clearAll();
|
|
415
|
+
this.removeAllListeners();
|
|
416
|
+
this.logger.info('Session manager destroyed');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Create a session manager
|
|
422
|
+
*/
|
|
423
|
+
export function createSessionManager(
|
|
424
|
+
logger: ILogger,
|
|
425
|
+
config?: SessionConfig
|
|
426
|
+
): SessionManager {
|
|
427
|
+
return new SessionManager(logger, config);
|
|
428
|
+
}
|