@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.
Files changed (96) hide show
  1. package/README.md +323 -0
  2. package/__tests__/hooks/bash-safety.test.ts +289 -0
  3. package/__tests__/hooks/file-organization.test.ts +335 -0
  4. package/__tests__/hooks/git-commit.test.ts +336 -0
  5. package/__tests__/hooks/index.ts +23 -0
  6. package/__tests__/hooks/session-hooks.test.ts +357 -0
  7. package/__tests__/hooks/task-hooks.test.ts +193 -0
  8. package/docs/EVENTS_IMPLEMENTATION_SUMMARY.md +388 -0
  9. package/docs/EVENTS_QUICK_REFERENCE.md +470 -0
  10. package/docs/EVENTS_README.md +352 -0
  11. package/package.json +39 -0
  12. package/src/core/config/defaults.ts +207 -0
  13. package/src/core/config/index.ts +15 -0
  14. package/src/core/config/loader.ts +271 -0
  15. package/src/core/config/schema.ts +188 -0
  16. package/src/core/config/validator.ts +209 -0
  17. package/src/core/event-bus.ts +236 -0
  18. package/src/core/index.ts +22 -0
  19. package/src/core/interfaces/agent.interface.ts +251 -0
  20. package/src/core/interfaces/coordinator.interface.ts +363 -0
  21. package/src/core/interfaces/event.interface.ts +267 -0
  22. package/src/core/interfaces/index.ts +19 -0
  23. package/src/core/interfaces/memory.interface.ts +332 -0
  24. package/src/core/interfaces/task.interface.ts +223 -0
  25. package/src/core/orchestrator/event-coordinator.ts +122 -0
  26. package/src/core/orchestrator/health-monitor.ts +214 -0
  27. package/src/core/orchestrator/index.ts +89 -0
  28. package/src/core/orchestrator/lifecycle-manager.ts +263 -0
  29. package/src/core/orchestrator/session-manager.ts +279 -0
  30. package/src/core/orchestrator/task-manager.ts +317 -0
  31. package/src/events/domain-events.ts +584 -0
  32. package/src/events/event-store.test.ts +387 -0
  33. package/src/events/event-store.ts +588 -0
  34. package/src/events/example-usage.ts +293 -0
  35. package/src/events/index.ts +90 -0
  36. package/src/events/projections.ts +561 -0
  37. package/src/events/state-reconstructor.ts +349 -0
  38. package/src/events.ts +367 -0
  39. package/src/hooks/INTEGRATION.md +658 -0
  40. package/src/hooks/README.md +532 -0
  41. package/src/hooks/example-usage.ts +499 -0
  42. package/src/hooks/executor.ts +379 -0
  43. package/src/hooks/hooks.test.ts +421 -0
  44. package/src/hooks/index.ts +131 -0
  45. package/src/hooks/registry.ts +333 -0
  46. package/src/hooks/safety/bash-safety.ts +604 -0
  47. package/src/hooks/safety/file-organization.ts +473 -0
  48. package/src/hooks/safety/git-commit.ts +623 -0
  49. package/src/hooks/safety/index.ts +46 -0
  50. package/src/hooks/session-hooks.ts +559 -0
  51. package/src/hooks/task-hooks.ts +513 -0
  52. package/src/hooks/types.ts +357 -0
  53. package/src/hooks/verify-exports.test.ts +125 -0
  54. package/src/index.ts +195 -0
  55. package/src/mcp/connection-pool.ts +438 -0
  56. package/src/mcp/index.ts +183 -0
  57. package/src/mcp/server.ts +774 -0
  58. package/src/mcp/session-manager.ts +428 -0
  59. package/src/mcp/tool-registry.ts +566 -0
  60. package/src/mcp/transport/http.ts +557 -0
  61. package/src/mcp/transport/index.ts +294 -0
  62. package/src/mcp/transport/stdio.ts +324 -0
  63. package/src/mcp/transport/websocket.ts +484 -0
  64. package/src/mcp/types.ts +565 -0
  65. package/src/plugin-interface.ts +663 -0
  66. package/src/plugin-loader.ts +638 -0
  67. package/src/plugin-registry.ts +604 -0
  68. package/src/plugins/index.ts +34 -0
  69. package/src/plugins/official/hive-mind-plugin.ts +330 -0
  70. package/src/plugins/official/index.ts +24 -0
  71. package/src/plugins/official/maestro-plugin.ts +508 -0
  72. package/src/plugins/types.ts +108 -0
  73. package/src/resilience/bulkhead.ts +277 -0
  74. package/src/resilience/circuit-breaker.ts +326 -0
  75. package/src/resilience/index.ts +26 -0
  76. package/src/resilience/rate-limiter.ts +420 -0
  77. package/src/resilience/retry.ts +224 -0
  78. package/src/security/index.ts +39 -0
  79. package/src/security/input-validation.ts +265 -0
  80. package/src/security/secure-random.ts +159 -0
  81. package/src/services/index.ts +16 -0
  82. package/src/services/v3-progress.service.ts +505 -0
  83. package/src/types/agent.types.ts +144 -0
  84. package/src/types/index.ts +22 -0
  85. package/src/types/mcp.types.ts +300 -0
  86. package/src/types/memory.types.ts +263 -0
  87. package/src/types/swarm.types.ts +255 -0
  88. package/src/types/task.types.ts +205 -0
  89. package/src/types.ts +367 -0
  90. package/src/utils/secure-logger.d.ts +69 -0
  91. package/src/utils/secure-logger.d.ts.map +1 -0
  92. package/src/utils/secure-logger.js +208 -0
  93. package/src/utils/secure-logger.js.map +1 -0
  94. package/src/utils/secure-logger.ts +257 -0
  95. package/tmp.json +0 -0
  96. package/tsconfig.json +9 -0
@@ -0,0 +1,559 @@
1
+ /**
2
+ * V3 Session Management Hooks
3
+ *
4
+ * Provides session-end and session-restore hooks for state persistence.
5
+ * Enables cross-session memory and state recovery.
6
+ *
7
+ * @module v3/shared/hooks/session-hooks
8
+ */
9
+
10
+ import {
11
+ HookEvent,
12
+ HookContext,
13
+ HookResult,
14
+ HookPriority,
15
+ SessionInfo,
16
+ } from './types.js';
17
+ import { HookRegistry } from './registry.js';
18
+
19
+ /**
20
+ * Session state to persist
21
+ */
22
+ export interface SessionState {
23
+ /** Session ID */
24
+ sessionId: string;
25
+ /** Session start time */
26
+ startTime: Date;
27
+ /** Session end time */
28
+ endTime?: Date;
29
+ /** Working directory */
30
+ workingDirectory?: string;
31
+ /** Active tasks */
32
+ activeTasks?: Array<{
33
+ id: string;
34
+ description: string;
35
+ status: 'pending' | 'in_progress' | 'completed' | 'failed';
36
+ }>;
37
+ /** Spawned agents */
38
+ spawnedAgents?: Array<{
39
+ id: string;
40
+ type: string;
41
+ status: 'active' | 'idle' | 'terminated';
42
+ }>;
43
+ /** Memory entries */
44
+ memoryEntries?: Array<{
45
+ key: string;
46
+ namespace: string;
47
+ type: string;
48
+ }>;
49
+ /** Git state */
50
+ gitState?: {
51
+ branch: string;
52
+ uncommittedChanges: number;
53
+ lastCommit?: string;
54
+ };
55
+ /** Learning metrics */
56
+ learningMetrics?: {
57
+ patternsLearned: number;
58
+ trajectoryCount: number;
59
+ avgConfidence: number;
60
+ };
61
+ /** Custom metadata */
62
+ metadata?: Record<string, unknown>;
63
+ }
64
+
65
+ /**
66
+ * Session-end hook result
67
+ */
68
+ export interface SessionEndHookResult extends HookResult {
69
+ /** Session state that was persisted */
70
+ persistedState?: SessionState;
71
+ /** File path where state was saved */
72
+ statePath?: string;
73
+ /** Duration of the session in ms */
74
+ duration?: number;
75
+ /** Summary of session activity */
76
+ summary?: SessionSummary;
77
+ }
78
+
79
+ /**
80
+ * Session-restore hook result
81
+ */
82
+ export interface SessionRestoreHookResult extends HookResult {
83
+ /** Restored session state */
84
+ restoredState?: SessionState;
85
+ /** Number of tasks restored */
86
+ tasksRestored?: number;
87
+ /** Number of agents restored */
88
+ agentsRestored?: number;
89
+ /** Memory entries restored */
90
+ memoryRestored?: number;
91
+ /** Warnings during restoration */
92
+ warnings?: string[];
93
+ }
94
+
95
+ /**
96
+ * Session summary
97
+ */
98
+ export interface SessionSummary {
99
+ /** Total tasks executed */
100
+ tasksExecuted: number;
101
+ /** Successful tasks */
102
+ tasksSucceeded: number;
103
+ /** Failed tasks */
104
+ tasksFailed: number;
105
+ /** Commands executed */
106
+ commandsExecuted: number;
107
+ /** Files modified */
108
+ filesModified: number;
109
+ /** Agents spawned */
110
+ agentsSpawned: number;
111
+ /** Duration in ms */
112
+ duration: number;
113
+ }
114
+
115
+ /**
116
+ * Session storage interface
117
+ */
118
+ export interface SessionStorage {
119
+ /** Save session state */
120
+ save(sessionId: string, state: SessionState): Promise<void>;
121
+ /** Load session state */
122
+ load(sessionId: string): Promise<SessionState | null>;
123
+ /** List available sessions */
124
+ list(): Promise<Array<{ id: string; startTime: Date; summary?: SessionSummary }>>;
125
+ /** Delete session */
126
+ delete(sessionId: string): Promise<boolean>;
127
+ /** Get latest session ID */
128
+ getLatest(): Promise<string | null>;
129
+ }
130
+
131
+ /**
132
+ * In-memory session storage (for testing and fallback)
133
+ */
134
+ export class InMemorySessionStorage implements SessionStorage {
135
+ private sessions: Map<string, SessionState> = new Map();
136
+
137
+ async save(sessionId: string, state: SessionState): Promise<void> {
138
+ this.sessions.set(sessionId, state);
139
+ }
140
+
141
+ async load(sessionId: string): Promise<SessionState | null> {
142
+ return this.sessions.get(sessionId) || null;
143
+ }
144
+
145
+ async list(): Promise<Array<{ id: string; startTime: Date; summary?: SessionSummary }>> {
146
+ return Array.from(this.sessions.entries()).map(([id, state]) => ({
147
+ id,
148
+ startTime: state.startTime,
149
+ }));
150
+ }
151
+
152
+ async delete(sessionId: string): Promise<boolean> {
153
+ return this.sessions.delete(sessionId);
154
+ }
155
+
156
+ async getLatest(): Promise<string | null> {
157
+ let latest: { id: string; time: number } | null = null;
158
+
159
+ for (const [id, state] of this.sessions) {
160
+ const time = state.startTime.getTime();
161
+ if (!latest || time > latest.time) {
162
+ latest = { id, time };
163
+ }
164
+ }
165
+
166
+ return latest?.id || null;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Session activity tracker
172
+ */
173
+ interface SessionActivity {
174
+ tasksExecuted: number;
175
+ tasksSucceeded: number;
176
+ tasksFailed: number;
177
+ commandsExecuted: number;
178
+ filesModified: Set<string>;
179
+ agentsSpawned: Set<string>;
180
+ }
181
+
182
+ /**
183
+ * Session Hooks Manager
184
+ *
185
+ * Manages session lifecycle hooks with state persistence.
186
+ */
187
+ export class SessionHooksManager {
188
+ private registry: HookRegistry;
189
+ private storage: SessionStorage;
190
+ private currentSessionId: string | null = null;
191
+ private sessionStartTime: Date | null = null;
192
+ private activity: SessionActivity = {
193
+ tasksExecuted: 0,
194
+ tasksSucceeded: 0,
195
+ tasksFailed: 0,
196
+ commandsExecuted: 0,
197
+ filesModified: new Set(),
198
+ agentsSpawned: new Set(),
199
+ };
200
+
201
+ constructor(registry: HookRegistry, storage?: SessionStorage) {
202
+ this.registry = registry;
203
+ this.storage = storage || new InMemorySessionStorage();
204
+ this.registerDefaultHooks();
205
+ }
206
+
207
+ /**
208
+ * Register default session hooks
209
+ */
210
+ private registerDefaultHooks(): void {
211
+ // Session start hook
212
+ this.registry.register(
213
+ HookEvent.SessionStart,
214
+ this.handleSessionStart.bind(this),
215
+ HookPriority.High,
216
+ { name: 'session-hooks:start' }
217
+ );
218
+
219
+ // Session end hook
220
+ this.registry.register(
221
+ HookEvent.SessionEnd,
222
+ this.handleSessionEnd.bind(this),
223
+ HookPriority.High,
224
+ { name: 'session-hooks:end' }
225
+ );
226
+
227
+ // Session resume hook (for restoration)
228
+ this.registry.register(
229
+ HookEvent.SessionResume,
230
+ this.handleSessionResume.bind(this),
231
+ HookPriority.High,
232
+ { name: 'session-hooks:resume' }
233
+ );
234
+
235
+ // Track tasks
236
+ this.registry.register(
237
+ HookEvent.PostTaskExecute,
238
+ this.trackTaskExecution.bind(this),
239
+ HookPriority.Low,
240
+ { name: 'session-hooks:track-task' }
241
+ );
242
+
243
+ // Track commands
244
+ this.registry.register(
245
+ HookEvent.PostCommand,
246
+ this.trackCommandExecution.bind(this),
247
+ HookPriority.Low,
248
+ { name: 'session-hooks:track-command' }
249
+ );
250
+
251
+ // Track file modifications
252
+ this.registry.register(
253
+ HookEvent.PostEdit,
254
+ this.trackFileModification.bind(this),
255
+ HookPriority.Low,
256
+ { name: 'session-hooks:track-file' }
257
+ );
258
+
259
+ // Track agent spawns
260
+ this.registry.register(
261
+ HookEvent.PostAgentSpawn,
262
+ this.trackAgentSpawn.bind(this),
263
+ HookPriority.Low,
264
+ { name: 'session-hooks:track-agent' }
265
+ );
266
+ }
267
+
268
+ /**
269
+ * Handle session start
270
+ */
271
+ async handleSessionStart(context: HookContext): Promise<HookResult> {
272
+ this.currentSessionId = context.session?.id || `session-${Date.now()}`;
273
+ this.sessionStartTime = new Date();
274
+ this.resetActivity();
275
+
276
+ return {
277
+ success: true,
278
+ data: {
279
+ session: {
280
+ id: this.currentSessionId,
281
+ startTime: this.sessionStartTime,
282
+ },
283
+ },
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Handle session end
289
+ */
290
+ async handleSessionEnd(context: HookContext): Promise<SessionEndHookResult> {
291
+ if (!this.currentSessionId || !this.sessionStartTime) {
292
+ return { success: true }; // No active session to end
293
+ }
294
+
295
+ const endTime = new Date();
296
+ const duration = endTime.getTime() - this.sessionStartTime.getTime();
297
+
298
+ // Build session summary
299
+ const summary: SessionSummary = {
300
+ tasksExecuted: this.activity.tasksExecuted,
301
+ tasksSucceeded: this.activity.tasksSucceeded,
302
+ tasksFailed: this.activity.tasksFailed,
303
+ commandsExecuted: this.activity.commandsExecuted,
304
+ filesModified: this.activity.filesModified.size,
305
+ agentsSpawned: this.activity.agentsSpawned.size,
306
+ duration,
307
+ };
308
+
309
+ // Build session state
310
+ const state: SessionState = {
311
+ sessionId: this.currentSessionId,
312
+ startTime: this.sessionStartTime,
313
+ endTime,
314
+ workingDirectory: context.metadata?.workingDirectory as string | undefined,
315
+ activeTasks: context.metadata?.activeTasks as SessionState['activeTasks'],
316
+ spawnedAgents: context.metadata?.spawnedAgents as SessionState['spawnedAgents'],
317
+ memoryEntries: context.metadata?.memoryEntries as SessionState['memoryEntries'],
318
+ gitState: context.metadata?.gitState as SessionState['gitState'],
319
+ learningMetrics: context.metadata?.learningMetrics as SessionState['learningMetrics'],
320
+ metadata: {
321
+ summary,
322
+ ...(context.metadata || {}),
323
+ },
324
+ };
325
+
326
+ // Persist state
327
+ await this.storage.save(this.currentSessionId, state);
328
+
329
+ // Reset session tracking
330
+ const sessionId = this.currentSessionId;
331
+ this.currentSessionId = null;
332
+ this.sessionStartTime = null;
333
+ this.resetActivity();
334
+
335
+ return {
336
+ success: true,
337
+ persistedState: state,
338
+ statePath: `sessions/${sessionId}.json`,
339
+ duration,
340
+ summary,
341
+ };
342
+ }
343
+
344
+ /**
345
+ * Handle session resume (restoration)
346
+ */
347
+ async handleSessionResume(context: HookContext): Promise<SessionRestoreHookResult> {
348
+ let sessionId = context.session?.id;
349
+
350
+ // If 'latest' is requested, get the most recent session
351
+ if (sessionId === 'latest' || !sessionId) {
352
+ sessionId = await this.storage.getLatest() || undefined;
353
+ }
354
+
355
+ if (!sessionId) {
356
+ return {
357
+ success: false,
358
+ error: new Error('No session ID provided and no previous sessions found'),
359
+ warnings: ['No sessions available for restoration'],
360
+ };
361
+ }
362
+
363
+ // Load session state
364
+ const state = await this.storage.load(sessionId);
365
+
366
+ if (!state) {
367
+ return {
368
+ success: false,
369
+ error: new Error(`Session ${sessionId} not found`),
370
+ warnings: [`Session ${sessionId} does not exist or has been deleted`],
371
+ };
372
+ }
373
+
374
+ const warnings: string[] = [];
375
+
376
+ // Validate state age
377
+ const stateAge = Date.now() - state.startTime.getTime();
378
+ const maxAge = 7 * 24 * 60 * 60 * 1000; // 7 days
379
+ if (stateAge > maxAge) {
380
+ warnings.push(`Session is ${Math.floor(stateAge / (24 * 60 * 60 * 1000))} days old, some state may be stale`);
381
+ }
382
+
383
+ // Count restorable items
384
+ const tasksRestored = state.activeTasks?.length || 0;
385
+ const agentsRestored = state.spawnedAgents?.length || 0;
386
+ const memoryRestored = state.memoryEntries?.length || 0;
387
+
388
+ // Check for incomplete tasks
389
+ const incompleteTasks = state.activeTasks?.filter(
390
+ t => t.status === 'pending' || t.status === 'in_progress'
391
+ );
392
+ if (incompleteTasks && incompleteTasks.length > 0) {
393
+ warnings.push(`${incompleteTasks.length} tasks were incomplete when session ended`);
394
+ }
395
+
396
+ // Update current session tracking
397
+ this.currentSessionId = `session-${Date.now()}-restored`;
398
+ this.sessionStartTime = new Date();
399
+ this.resetActivity();
400
+
401
+ return {
402
+ success: true,
403
+ restoredState: state,
404
+ tasksRestored,
405
+ agentsRestored,
406
+ memoryRestored,
407
+ warnings: warnings.length > 0 ? warnings : undefined,
408
+ data: {
409
+ session: {
410
+ id: this.currentSessionId,
411
+ startTime: this.sessionStartTime,
412
+ metadata: {
413
+ restoredFrom: sessionId,
414
+ originalStartTime: state.startTime,
415
+ },
416
+ },
417
+ },
418
+ };
419
+ }
420
+
421
+ /**
422
+ * Track task execution
423
+ */
424
+ private async trackTaskExecution(context: HookContext): Promise<HookResult> {
425
+ this.activity.tasksExecuted++;
426
+ if (context.metadata?.success !== false) {
427
+ this.activity.tasksSucceeded++;
428
+ } else {
429
+ this.activity.tasksFailed++;
430
+ }
431
+ return { success: true };
432
+ }
433
+
434
+ /**
435
+ * Track command execution
436
+ */
437
+ private async trackCommandExecution(context: HookContext): Promise<HookResult> {
438
+ this.activity.commandsExecuted++;
439
+ return { success: true };
440
+ }
441
+
442
+ /**
443
+ * Track file modification
444
+ */
445
+ private async trackFileModification(context: HookContext): Promise<HookResult> {
446
+ if (context.file?.path) {
447
+ this.activity.filesModified.add(context.file.path);
448
+ }
449
+ return { success: true };
450
+ }
451
+
452
+ /**
453
+ * Track agent spawn
454
+ */
455
+ private async trackAgentSpawn(context: HookContext): Promise<HookResult> {
456
+ if (context.agent?.id) {
457
+ this.activity.agentsSpawned.add(context.agent.id);
458
+ }
459
+ return { success: true };
460
+ }
461
+
462
+ /**
463
+ * Reset activity tracking
464
+ */
465
+ private resetActivity(): void {
466
+ this.activity = {
467
+ tasksExecuted: 0,
468
+ tasksSucceeded: 0,
469
+ tasksFailed: 0,
470
+ commandsExecuted: 0,
471
+ filesModified: new Set(),
472
+ agentsSpawned: new Set(),
473
+ };
474
+ }
475
+
476
+ /**
477
+ * Execute session-end hook manually
478
+ */
479
+ async executeSessionEnd(metadata?: Record<string, unknown>): Promise<SessionEndHookResult> {
480
+ const context: HookContext = {
481
+ event: HookEvent.SessionEnd,
482
+ timestamp: new Date(),
483
+ session: this.currentSessionId
484
+ ? {
485
+ id: this.currentSessionId,
486
+ startTime: this.sessionStartTime!,
487
+ }
488
+ : undefined,
489
+ metadata,
490
+ };
491
+
492
+ return this.handleSessionEnd(context);
493
+ }
494
+
495
+ /**
496
+ * Execute session-restore hook manually
497
+ */
498
+ async executeSessionRestore(
499
+ sessionId?: string,
500
+ metadata?: Record<string, unknown>
501
+ ): Promise<SessionRestoreHookResult> {
502
+ const context: HookContext = {
503
+ event: HookEvent.SessionResume,
504
+ timestamp: new Date(),
505
+ session: {
506
+ id: sessionId || 'latest',
507
+ startTime: new Date(),
508
+ },
509
+ metadata,
510
+ };
511
+
512
+ return this.handleSessionResume(context);
513
+ }
514
+
515
+ /**
516
+ * List available sessions
517
+ */
518
+ async listSessions(): Promise<Array<{ id: string; startTime: Date; summary?: SessionSummary }>> {
519
+ return this.storage.list();
520
+ }
521
+
522
+ /**
523
+ * Delete a session
524
+ */
525
+ async deleteSession(sessionId: string): Promise<boolean> {
526
+ return this.storage.delete(sessionId);
527
+ }
528
+
529
+ /**
530
+ * Get current session ID
531
+ */
532
+ getCurrentSessionId(): string | null {
533
+ return this.currentSessionId;
534
+ }
535
+
536
+ /**
537
+ * Get current session activity
538
+ */
539
+ getCurrentActivity(): SessionActivity {
540
+ return { ...this.activity };
541
+ }
542
+
543
+ /**
544
+ * Set storage backend
545
+ */
546
+ setStorage(storage: SessionStorage): void {
547
+ this.storage = storage;
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Create session hooks manager
553
+ */
554
+ export function createSessionHooksManager(
555
+ registry: HookRegistry,
556
+ storage?: SessionStorage
557
+ ): SessionHooksManager {
558
+ return new SessionHooksManager(registry, storage);
559
+ }