@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,349 @@
1
+ /**
2
+ * State Reconstructor - ADR-007 Implementation
3
+ *
4
+ * Reconstructs aggregate state from event streams.
5
+ * Implements event sourcing patterns for V3.
6
+ *
7
+ * @module v3/shared/events/state-reconstructor
8
+ */
9
+
10
+ import { EventStore, type EventSnapshot } from './event-store.js';
11
+ import type { DomainEvent } from './domain-events.js';
12
+
13
+ /**
14
+ * Aggregate root interface
15
+ */
16
+ export interface AggregateRoot {
17
+ id: string;
18
+ version: number;
19
+ apply(event: DomainEvent): void;
20
+ getState(): Record<string, unknown>;
21
+ }
22
+
23
+ /**
24
+ * Reconstructor options
25
+ */
26
+ export interface ReconstructorOptions {
27
+ useSnapshots: boolean;
28
+ snapshotInterval: number; // Create snapshot every N events
29
+ maxEventsToReplay: number;
30
+ }
31
+
32
+ /**
33
+ * State Reconstructor
34
+ *
35
+ * Reconstructs aggregate state from event history.
36
+ * Supports snapshots for performance optimization.
37
+ */
38
+ export class StateReconstructor {
39
+ private readonly options: ReconstructorOptions;
40
+
41
+ constructor(
42
+ private readonly eventStore: EventStore,
43
+ options?: Partial<ReconstructorOptions>
44
+ ) {
45
+ this.options = {
46
+ useSnapshots: true,
47
+ snapshotInterval: 100,
48
+ maxEventsToReplay: 10000,
49
+ ...options,
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Reconstruct aggregate state from events
55
+ */
56
+ async reconstruct<T extends AggregateRoot>(
57
+ aggregateId: string,
58
+ factory: (id: string) => T
59
+ ): Promise<T> {
60
+ const aggregate = factory(aggregateId);
61
+
62
+ // Try to load from snapshot first
63
+ if (this.options.useSnapshots) {
64
+ const snapshot = await this.eventStore.getSnapshot(aggregateId);
65
+ if (snapshot) {
66
+ this.applySnapshot(aggregate, snapshot);
67
+ }
68
+ }
69
+
70
+ // Get events after snapshot version (or all if no snapshot)
71
+ const events = await this.eventStore.getEvents(aggregateId, aggregate.version + 1);
72
+
73
+ // Apply events
74
+ for (const event of events) {
75
+ if (events.length > this.options.maxEventsToReplay) {
76
+ throw new Error(`Too many events to replay (${events.length}). Consider creating a snapshot.`);
77
+ }
78
+
79
+ aggregate.apply(event);
80
+ }
81
+
82
+ // Create snapshot if interval reached
83
+ if (this.options.useSnapshots && aggregate.version % this.options.snapshotInterval === 0) {
84
+ await this.createSnapshot(aggregate);
85
+ }
86
+
87
+ return aggregate;
88
+ }
89
+
90
+ /**
91
+ * Reconstruct state at a specific point in time
92
+ */
93
+ async reconstructAtTime<T extends AggregateRoot>(
94
+ aggregateId: string,
95
+ factory: (id: string) => T,
96
+ timestamp: Date
97
+ ): Promise<T> {
98
+ const aggregate = factory(aggregateId);
99
+
100
+ // Get all events up to timestamp
101
+ const allEvents = await this.eventStore.getEvents(aggregateId);
102
+ const events = allEvents.filter((e) => e.timestamp <= timestamp.getTime());
103
+
104
+ // Apply events
105
+ for (const event of events) {
106
+ aggregate.apply(event);
107
+ }
108
+
109
+ return aggregate;
110
+ }
111
+
112
+ /**
113
+ * Reconstruct state at a specific version
114
+ */
115
+ async reconstructAtVersion<T extends AggregateRoot>(
116
+ aggregateId: string,
117
+ factory: (id: string) => T,
118
+ targetVersion: number
119
+ ): Promise<T> {
120
+ const aggregate = factory(aggregateId);
121
+
122
+ // Get events up to target version
123
+ const events = await this.eventStore.getEvents(aggregateId);
124
+ const limitedEvents = events.filter((e) => e.version <= targetVersion);
125
+
126
+ // Apply events
127
+ for (const event of limitedEvents) {
128
+ aggregate.apply(event);
129
+ }
130
+
131
+ return aggregate;
132
+ }
133
+
134
+ /**
135
+ * Apply snapshot to aggregate
136
+ */
137
+ private applySnapshot(aggregate: AggregateRoot, snapshot: EventSnapshot): void {
138
+ // Type assertion for aggregate that has restoreFromSnapshot
139
+ const restorable = aggregate as AggregateRoot & {
140
+ restoreFromSnapshot?(state: unknown): void;
141
+ };
142
+
143
+ if (typeof restorable.restoreFromSnapshot === 'function') {
144
+ restorable.restoreFromSnapshot(snapshot.state);
145
+ }
146
+
147
+ // Update version
148
+ (aggregate as any).version = snapshot.version;
149
+ }
150
+
151
+ /**
152
+ * Create snapshot for aggregate
153
+ */
154
+ private async createSnapshot(aggregate: AggregateRoot): Promise<void> {
155
+ const snapshot: EventSnapshot = {
156
+ aggregateId: aggregate.id,
157
+ aggregateType: this.getAggregateType(aggregate),
158
+ version: aggregate.version,
159
+ state: aggregate.getState(),
160
+ timestamp: Date.now(),
161
+ };
162
+
163
+ await this.eventStore.saveSnapshot(snapshot);
164
+ }
165
+
166
+ /**
167
+ * Get aggregate type from instance
168
+ */
169
+ private getAggregateType(aggregate: AggregateRoot): 'agent' | 'task' | 'memory' | 'swarm' {
170
+ const typeName = aggregate.constructor.name.toLowerCase().replace('aggregate', '');
171
+ // Map to valid aggregate types
172
+ if (typeName === 'agent' || typeName === 'task' || typeName === 'memory' || typeName === 'swarm') {
173
+ return typeName;
174
+ }
175
+ return 'agent'; // Default fallback
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Agent Aggregate - Example implementation
181
+ */
182
+ export class AgentAggregate implements AggregateRoot {
183
+ id: string;
184
+ version = 0;
185
+
186
+ private state = {
187
+ name: '',
188
+ role: '',
189
+ status: 'idle' as string,
190
+ currentTask: null as string | null,
191
+ completedTasks: [] as string[],
192
+ capabilities: [] as string[],
193
+ createdAt: null as Date | null,
194
+ lastActiveAt: null as Date | null,
195
+ };
196
+
197
+ constructor(id: string) {
198
+ this.id = id;
199
+ }
200
+
201
+ apply(event: DomainEvent): void {
202
+ this.version = event.version;
203
+
204
+ switch (event.type) {
205
+ case 'agent:spawned':
206
+ this.state.name = event.payload.name as string;
207
+ this.state.role = event.payload.role as string;
208
+ this.state.capabilities = (event.payload.capabilities as string[]) ?? [];
209
+ this.state.status = 'idle';
210
+ this.state.createdAt = new Date(event.timestamp);
211
+ break;
212
+
213
+ case 'agent:started':
214
+ this.state.status = 'active';
215
+ this.state.lastActiveAt = new Date(event.timestamp);
216
+ break;
217
+
218
+ case 'agent:task-assigned':
219
+ this.state.currentTask = event.payload.taskId as string;
220
+ this.state.status = 'busy';
221
+ this.state.lastActiveAt = new Date(event.timestamp);
222
+ break;
223
+
224
+ case 'agent:task-completed':
225
+ this.state.completedTasks.push(event.payload.taskId as string);
226
+ this.state.currentTask = null;
227
+ this.state.status = 'active';
228
+ this.state.lastActiveAt = new Date(event.timestamp);
229
+ break;
230
+
231
+ case 'agent:terminated':
232
+ this.state.status = 'terminated';
233
+ break;
234
+ }
235
+ }
236
+
237
+ getState(): Record<string, unknown> {
238
+ return { ...this.state };
239
+ }
240
+
241
+ restoreFromSnapshot(snapshotState: unknown): void {
242
+ const state = snapshotState as typeof this.state;
243
+ this.state = {
244
+ ...state,
245
+ createdAt: state.createdAt ? new Date(state.createdAt) : null,
246
+ lastActiveAt: state.lastActiveAt ? new Date(state.lastActiveAt) : null,
247
+ };
248
+ }
249
+
250
+ // Getters for type safety
251
+ get name(): string { return this.state.name; }
252
+ get role(): string { return this.state.role; }
253
+ get status(): string { return this.state.status; }
254
+ get currentTask(): string | null { return this.state.currentTask; }
255
+ get completedTasks(): string[] { return [...this.state.completedTasks]; }
256
+ get capabilities(): string[] { return [...this.state.capabilities]; }
257
+ }
258
+
259
+ /**
260
+ * Task Aggregate - Example implementation
261
+ */
262
+ export class TaskAggregate implements AggregateRoot {
263
+ id: string;
264
+ version = 0;
265
+
266
+ private state = {
267
+ title: '',
268
+ description: '',
269
+ type: '',
270
+ priority: 'normal' as string,
271
+ status: 'pending' as string,
272
+ assignedAgent: null as string | null,
273
+ result: null as unknown,
274
+ createdAt: null as Date | null,
275
+ startedAt: null as Date | null,
276
+ completedAt: null as Date | null,
277
+ };
278
+
279
+ constructor(id: string) {
280
+ this.id = id;
281
+ }
282
+
283
+ apply(event: DomainEvent): void {
284
+ this.version = event.version;
285
+
286
+ switch (event.type) {
287
+ case 'task:created':
288
+ this.state.title = event.payload.title as string;
289
+ this.state.description = event.payload.description as string;
290
+ this.state.type = event.payload.taskType as string;
291
+ this.state.priority = (event.payload.priority as string) ?? 'normal';
292
+ this.state.status = 'pending';
293
+ this.state.createdAt = new Date(event.timestamp);
294
+ break;
295
+
296
+ case 'task:started':
297
+ this.state.assignedAgent = event.payload.agentId as string;
298
+ this.state.status = 'running';
299
+ this.state.startedAt = new Date(event.timestamp);
300
+ break;
301
+
302
+ case 'task:completed':
303
+ this.state.result = event.payload.result;
304
+ this.state.status = 'completed';
305
+ this.state.completedAt = new Date(event.timestamp);
306
+ break;
307
+
308
+ case 'task:failed':
309
+ this.state.status = 'failed';
310
+ this.state.completedAt = new Date(event.timestamp);
311
+ break;
312
+
313
+ case 'task:cancelled':
314
+ this.state.status = 'cancelled';
315
+ this.state.completedAt = new Date(event.timestamp);
316
+ break;
317
+ }
318
+ }
319
+
320
+ getState(): Record<string, unknown> {
321
+ return { ...this.state };
322
+ }
323
+
324
+ restoreFromSnapshot(snapshotState: unknown): void {
325
+ const state = snapshotState as typeof this.state;
326
+ this.state = {
327
+ ...state,
328
+ createdAt: state.createdAt ? new Date(state.createdAt) : null,
329
+ startedAt: state.startedAt ? new Date(state.startedAt) : null,
330
+ completedAt: state.completedAt ? new Date(state.completedAt) : null,
331
+ };
332
+ }
333
+
334
+ // Getters
335
+ get title(): string { return this.state.title; }
336
+ get status(): string { return this.state.status; }
337
+ get assignedAgent(): string | null { return this.state.assignedAgent; }
338
+ get result(): unknown { return this.state.result; }
339
+ }
340
+
341
+ /**
342
+ * Factory function
343
+ */
344
+ export function createStateReconstructor(
345
+ eventStore: EventStore,
346
+ options?: Partial<ReconstructorOptions>
347
+ ): StateReconstructor {
348
+ return new StateReconstructor(eventStore, options);
349
+ }
package/src/events.ts ADDED
@@ -0,0 +1,367 @@
1
+ /**
2
+ * V3 Event Bus System
3
+ * Event-driven communication for the 15-agent swarm
4
+ *
5
+ * Based on ADR-007 (Event Sourcing for State Changes)
6
+ */
7
+
8
+ import {
9
+ EventType,
10
+ EventHandler,
11
+ SwarmEvent,
12
+ AgentId
13
+ } from './types.js';
14
+
15
+ // =============================================================================
16
+ // Event Bus Interface
17
+ // =============================================================================
18
+
19
+ export interface IEventBus {
20
+ subscribe<T>(eventType: EventType, handler: EventHandler<T>): () => void;
21
+ subscribeAll(handler: EventHandler): () => void;
22
+ emit<T>(event: SwarmEvent<T>): Promise<void>;
23
+ emitSync<T>(event: SwarmEvent<T>): void;
24
+ getHistory(filter?: EventFilter): SwarmEvent[];
25
+ clear(): void;
26
+ }
27
+
28
+ export interface EventFilter {
29
+ types?: EventType[];
30
+ sources?: (AgentId | 'swarm')[];
31
+ since?: number;
32
+ until?: number;
33
+ limit?: number;
34
+ }
35
+
36
+ // =============================================================================
37
+ // Event Store Interface (Event Sourcing)
38
+ // =============================================================================
39
+
40
+ export interface IEventStore {
41
+ append(event: SwarmEvent): Promise<void>;
42
+ getEvents(aggregateId: string, fromVersion?: number): Promise<SwarmEvent[]>;
43
+ getAllEvents(filter?: EventFilter): Promise<SwarmEvent[]>;
44
+ getSnapshot(aggregateId: string): Promise<EventStoreSnapshot | null>;
45
+ saveSnapshot(snapshot: EventStoreSnapshot): Promise<void>;
46
+ }
47
+
48
+ export interface EventStoreSnapshot {
49
+ aggregateId: string;
50
+ version: number;
51
+ state: unknown;
52
+ timestamp: number;
53
+ }
54
+
55
+ // =============================================================================
56
+ // Event Bus Implementation
57
+ // =============================================================================
58
+
59
+ export class EventBus implements IEventBus {
60
+ private handlers: Map<EventType | '*', Set<EventHandler>> = new Map();
61
+ private history: SwarmEvent[] = [];
62
+ private maxHistorySize: number;
63
+
64
+ constructor(options: { maxHistorySize?: number } = {}) {
65
+ this.maxHistorySize = options.maxHistorySize ?? 10000;
66
+ }
67
+
68
+ subscribe<T>(eventType: EventType, handler: EventHandler<T>): () => void {
69
+ if (!this.handlers.has(eventType)) {
70
+ this.handlers.set(eventType, new Set());
71
+ }
72
+
73
+ const handlers = this.handlers.get(eventType)!;
74
+ handlers.add(handler as EventHandler);
75
+
76
+ return () => {
77
+ handlers.delete(handler as EventHandler);
78
+ };
79
+ }
80
+
81
+ subscribeAll(handler: EventHandler): () => void {
82
+ if (!this.handlers.has('*')) {
83
+ this.handlers.set('*', new Set());
84
+ }
85
+
86
+ const handlers = this.handlers.get('*')!;
87
+ handlers.add(handler);
88
+
89
+ return () => {
90
+ handlers.delete(handler);
91
+ };
92
+ }
93
+
94
+ async emit<T>(event: SwarmEvent<T>): Promise<void> {
95
+ this.addToHistory(event);
96
+
97
+ const typeHandlers = this.handlers.get(event.type) ?? new Set();
98
+ const allHandlers = this.handlers.get('*') ?? new Set();
99
+
100
+ const allPromises: Promise<void>[] = [];
101
+
102
+ for (const handler of typeHandlers) {
103
+ allPromises.push(this.safeExecute(handler, event));
104
+ }
105
+
106
+ for (const handler of allHandlers) {
107
+ allPromises.push(this.safeExecute(handler, event));
108
+ }
109
+
110
+ await Promise.all(allPromises);
111
+ }
112
+
113
+ emitSync<T>(event: SwarmEvent<T>): void {
114
+ this.addToHistory(event);
115
+
116
+ const typeHandlers = this.handlers.get(event.type) ?? new Set();
117
+ const allHandlers = this.handlers.get('*') ?? new Set();
118
+
119
+ for (const handler of typeHandlers) {
120
+ try {
121
+ const result = handler(event);
122
+ if (result instanceof Promise) {
123
+ result.catch(err => console.error(`Event handler error: ${err}`));
124
+ }
125
+ } catch (err) {
126
+ console.error(`Event handler error: ${err}`);
127
+ }
128
+ }
129
+
130
+ for (const handler of allHandlers) {
131
+ try {
132
+ const result = handler(event);
133
+ if (result instanceof Promise) {
134
+ result.catch(err => console.error(`Event handler error: ${err}`));
135
+ }
136
+ } catch (err) {
137
+ console.error(`Event handler error: ${err}`);
138
+ }
139
+ }
140
+ }
141
+
142
+ getHistory(filter?: EventFilter): SwarmEvent[] {
143
+ let events = [...this.history];
144
+
145
+ if (filter?.types?.length) {
146
+ events = events.filter(e => filter.types!.includes(e.type));
147
+ }
148
+
149
+ if (filter?.sources?.length) {
150
+ events = events.filter(e => filter.sources!.includes(e.source));
151
+ }
152
+
153
+ if (filter?.since) {
154
+ events = events.filter(e => e.timestamp >= filter.since!);
155
+ }
156
+
157
+ if (filter?.until) {
158
+ events = events.filter(e => e.timestamp <= filter.until!);
159
+ }
160
+
161
+ if (filter?.limit) {
162
+ events = events.slice(-filter.limit);
163
+ }
164
+
165
+ return events;
166
+ }
167
+
168
+ clear(): void {
169
+ this.history = [];
170
+ }
171
+
172
+ private addToHistory(event: SwarmEvent): void {
173
+ this.history.push(event);
174
+
175
+ if (this.history.length > this.maxHistorySize) {
176
+ this.history = this.history.slice(-Math.floor(this.maxHistorySize / 2));
177
+ }
178
+ }
179
+
180
+ private async safeExecute(handler: EventHandler, event: SwarmEvent): Promise<void> {
181
+ try {
182
+ await handler(event);
183
+ } catch (err) {
184
+ console.error(`Event handler error for ${event.type}: ${err}`);
185
+ }
186
+ }
187
+ }
188
+
189
+ // =============================================================================
190
+ // In-Memory Event Store
191
+ // =============================================================================
192
+
193
+ export class InMemoryEventStore implements IEventStore {
194
+ private events: Map<string, SwarmEvent[]> = new Map();
195
+ private allEvents: SwarmEvent[] = [];
196
+ private snapshots: Map<string, EventStoreSnapshot> = new Map();
197
+
198
+ async append(event: SwarmEvent): Promise<void> {
199
+ const aggregateId = this.extractAggregateId(event);
200
+
201
+ if (!this.events.has(aggregateId)) {
202
+ this.events.set(aggregateId, []);
203
+ }
204
+
205
+ this.events.get(aggregateId)!.push(event);
206
+ this.allEvents.push(event);
207
+ }
208
+
209
+ async getEvents(aggregateId: string, fromVersion?: number): Promise<SwarmEvent[]> {
210
+ const events = this.events.get(aggregateId) ?? [];
211
+
212
+ if (fromVersion !== undefined) {
213
+ return events.slice(fromVersion);
214
+ }
215
+
216
+ return events;
217
+ }
218
+
219
+ async getAllEvents(filter?: EventFilter): Promise<SwarmEvent[]> {
220
+ let events = [...this.allEvents];
221
+
222
+ if (filter?.types?.length) {
223
+ events = events.filter(e => filter.types!.includes(e.type));
224
+ }
225
+
226
+ if (filter?.sources?.length) {
227
+ events = events.filter(e => filter.sources!.includes(e.source));
228
+ }
229
+
230
+ if (filter?.since) {
231
+ events = events.filter(e => e.timestamp >= filter.since!);
232
+ }
233
+
234
+ if (filter?.until) {
235
+ events = events.filter(e => e.timestamp <= filter.until!);
236
+ }
237
+
238
+ if (filter?.limit) {
239
+ events = events.slice(-filter.limit);
240
+ }
241
+
242
+ return events;
243
+ }
244
+
245
+ async getSnapshot(aggregateId: string): Promise<EventStoreSnapshot | null> {
246
+ return this.snapshots.get(aggregateId) ?? null;
247
+ }
248
+
249
+ async saveSnapshot(snapshot: EventStoreSnapshot): Promise<void> {
250
+ this.snapshots.set(snapshot.aggregateId, snapshot);
251
+ }
252
+
253
+ private extractAggregateId(event: SwarmEvent): string {
254
+ if (event.source !== 'swarm') {
255
+ return event.source;
256
+ }
257
+
258
+ if (typeof event.payload === 'object' && event.payload !== null) {
259
+ const payload = event.payload as Record<string, unknown>;
260
+ if ('agentId' in payload) return payload.agentId as string;
261
+ if ('taskId' in payload) return payload.taskId as string;
262
+ }
263
+
264
+ return 'swarm';
265
+ }
266
+ }
267
+
268
+ // =============================================================================
269
+ // Event Factory Functions
270
+ // =============================================================================
271
+
272
+ let eventCounter = 0;
273
+
274
+ export function createEvent<T>(
275
+ type: EventType,
276
+ source: AgentId | 'swarm',
277
+ payload: T
278
+ ): SwarmEvent<T> {
279
+ return {
280
+ id: `evt-${Date.now()}-${++eventCounter}`,
281
+ type,
282
+ timestamp: Date.now(),
283
+ source,
284
+ payload
285
+ };
286
+ }
287
+
288
+ // Agent Events
289
+ export function agentSpawnedEvent(agentId: AgentId, role: string): SwarmEvent {
290
+ return createEvent('agent:spawned', 'swarm', { agentId, role });
291
+ }
292
+
293
+ export function agentStatusChangedEvent(
294
+ agentId: AgentId,
295
+ previousStatus: string,
296
+ newStatus: string
297
+ ): SwarmEvent {
298
+ return createEvent('agent:status-changed', agentId, { previousStatus, newStatus });
299
+ }
300
+
301
+ export function agentTaskAssignedEvent(agentId: AgentId, taskId: string): SwarmEvent {
302
+ return createEvent('agent:task-assigned', 'swarm', { agentId, taskId });
303
+ }
304
+
305
+ export function agentTaskCompletedEvent(agentId: AgentId, taskId: string, result: unknown): SwarmEvent {
306
+ return createEvent('agent:task-completed', agentId, { taskId, result });
307
+ }
308
+
309
+ export function agentErrorEvent(agentId: AgentId, error: Error): SwarmEvent {
310
+ return createEvent('agent:error', agentId, {
311
+ message: error.message,
312
+ stack: error.stack
313
+ });
314
+ }
315
+
316
+ // Task Events
317
+ export function taskCreatedEvent(taskId: string, type: string, title: string): SwarmEvent {
318
+ return createEvent('task:created', 'swarm', { taskId, type, title });
319
+ }
320
+
321
+ export function taskQueuedEvent(taskId: string, priority: string): SwarmEvent {
322
+ return createEvent('task:queued', 'swarm', { taskId, priority });
323
+ }
324
+
325
+ export function taskAssignedEvent(taskId: string, agentId: AgentId): SwarmEvent {
326
+ return createEvent('task:assigned', 'swarm', { taskId, agentId });
327
+ }
328
+
329
+ export function taskStartedEvent(taskId: string, agentId: AgentId): SwarmEvent {
330
+ return createEvent('task:started', agentId, { taskId });
331
+ }
332
+
333
+ export function taskCompletedEvent(taskId: string, result: unknown): SwarmEvent {
334
+ return createEvent('task:completed', 'swarm', { taskId, result });
335
+ }
336
+
337
+ export function taskFailedEvent(taskId: string, error: Error): SwarmEvent {
338
+ return createEvent('task:failed', 'swarm', {
339
+ taskId,
340
+ error: error.message,
341
+ stack: error.stack
342
+ });
343
+ }
344
+
345
+ export function taskBlockedEvent(taskId: string, blockedBy: string[]): SwarmEvent {
346
+ return createEvent('task:blocked', 'swarm', { taskId, blockedBy });
347
+ }
348
+
349
+ // Swarm Events
350
+ export function swarmInitializedEvent(config: unknown): SwarmEvent {
351
+ return createEvent('swarm:initialized', 'swarm', { config });
352
+ }
353
+
354
+ export function swarmPhaseChangedEvent(previousPhase: string, newPhase: string): SwarmEvent {
355
+ return createEvent('swarm:phase-changed', 'swarm', { previousPhase, newPhase });
356
+ }
357
+
358
+ export function swarmMilestoneReachedEvent(milestoneId: string, name: string): SwarmEvent {
359
+ return createEvent('swarm:milestone-reached', 'swarm', { milestoneId, name });
360
+ }
361
+
362
+ export function swarmErrorEvent(error: Error): SwarmEvent {
363
+ return createEvent('swarm:error', 'swarm', {
364
+ message: error.message,
365
+ stack: error.stack
366
+ });
367
+ }