@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,357 @@
1
+ /**
2
+ * V3 Session Hooks Tests
3
+ *
4
+ * Tests for session-end and session-restore hook functionality.
5
+ *
6
+ * @module v3/shared/hooks/__tests__/session-hooks.test
7
+ */
8
+
9
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
10
+ import {
11
+ createHookRegistry,
12
+ createSessionHooksManager,
13
+ SessionHooksManager,
14
+ HookRegistry,
15
+ HookEvent,
16
+ InMemorySessionStorage,
17
+ } from '../../src/hooks/index.js';
18
+
19
+ describe('SessionHooksManager', () => {
20
+ let registry: HookRegistry;
21
+ let sessionManager: SessionHooksManager;
22
+ let storage: InMemorySessionStorage;
23
+
24
+ beforeEach(() => {
25
+ registry = createHookRegistry();
26
+ storage = new InMemorySessionStorage();
27
+ sessionManager = createSessionHooksManager(registry, storage);
28
+ });
29
+
30
+ describe('session lifecycle hooks', () => {
31
+ it('should register session hooks on creation', () => {
32
+ const startHooks = registry.getHandlers(HookEvent.SessionStart);
33
+ const endHooks = registry.getHandlers(HookEvent.SessionEnd);
34
+ const resumeHooks = registry.getHandlers(HookEvent.SessionResume);
35
+
36
+ expect(startHooks.some(h => h.name === 'session-hooks:start')).toBe(true);
37
+ expect(endHooks.some(h => h.name === 'session-hooks:end')).toBe(true);
38
+ expect(resumeHooks.some(h => h.name === 'session-hooks:resume')).toBe(true);
39
+ });
40
+ });
41
+
42
+ describe('session-end hook', () => {
43
+ it('should end session and return summary', async () => {
44
+ // Simulate session start by triggering tracking
45
+ const context = {
46
+ event: HookEvent.SessionStart,
47
+ timestamp: new Date(),
48
+ session: { id: 'test-session', startTime: new Date() },
49
+ };
50
+ await sessionManager['handleSessionStart'](context);
51
+
52
+ // Wait a moment to ensure duration > 0
53
+ await new Promise(resolve => setTimeout(resolve, 10));
54
+
55
+ const result = await sessionManager.executeSessionEnd();
56
+
57
+ expect(result.success).toBe(true);
58
+ expect(result.duration).toBeGreaterThanOrEqual(0);
59
+ expect(result.summary).toBeDefined();
60
+ expect(result.summary!.tasksExecuted).toBe(0);
61
+ expect(result.summary!.commandsExecuted).toBe(0);
62
+ });
63
+
64
+ it('should persist session state', async () => {
65
+ // Start session
66
+ const startContext = {
67
+ event: HookEvent.SessionStart,
68
+ timestamp: new Date(),
69
+ session: { id: 'persist-session', startTime: new Date() },
70
+ };
71
+ await sessionManager['handleSessionStart'](startContext);
72
+
73
+ const result = await sessionManager.executeSessionEnd();
74
+
75
+ expect(result.persistedState).toBeDefined();
76
+ expect(result.statePath).toBeDefined();
77
+
78
+ // Verify state was saved
79
+ const sessions = await storage.list();
80
+ expect(sessions.length).toBeGreaterThan(0);
81
+ });
82
+
83
+ it('should handle ending session without active session', async () => {
84
+ const result = await sessionManager.executeSessionEnd();
85
+ expect(result.success).toBe(true);
86
+ });
87
+
88
+ it('should reset activity tracking after session end', async () => {
89
+ // Start session
90
+ const context = {
91
+ event: HookEvent.SessionStart,
92
+ timestamp: new Date(),
93
+ session: { id: 'reset-session', startTime: new Date() },
94
+ };
95
+ await sessionManager['handleSessionStart'](context);
96
+
97
+ await sessionManager.executeSessionEnd();
98
+
99
+ expect(sessionManager.getCurrentSessionId()).toBeNull();
100
+ });
101
+ });
102
+
103
+ describe('session-restore hook', () => {
104
+ it('should restore a previous session', async () => {
105
+ // Create and end a session first
106
+ const startContext = {
107
+ event: HookEvent.SessionStart,
108
+ timestamp: new Date(),
109
+ session: { id: 'restore-test', startTime: new Date() },
110
+ };
111
+ await sessionManager['handleSessionStart'](startContext);
112
+ await sessionManager.executeSessionEnd();
113
+
114
+ // Restore the session
115
+ const result = await sessionManager.executeSessionRestore('restore-test');
116
+
117
+ expect(result.success).toBe(true);
118
+ expect(result.restoredState).toBeDefined();
119
+ expect(result.restoredState!.sessionId).toBe('restore-test');
120
+ });
121
+
122
+ it('should restore latest session when no ID specified', async () => {
123
+ // Create and end multiple sessions
124
+ for (const id of ['session-1', 'session-2', 'session-3']) {
125
+ const context = {
126
+ event: HookEvent.SessionStart,
127
+ timestamp: new Date(),
128
+ session: { id, startTime: new Date() },
129
+ };
130
+ await sessionManager['handleSessionStart'](context);
131
+ await sessionManager.executeSessionEnd();
132
+ await new Promise(resolve => setTimeout(resolve, 5));
133
+ }
134
+
135
+ const result = await sessionManager.executeSessionRestore();
136
+
137
+ expect(result.success).toBe(true);
138
+ expect(result.restoredState).toBeDefined();
139
+ });
140
+
141
+ it('should fail gracefully when session not found', async () => {
142
+ const result = await sessionManager.executeSessionRestore('non-existent');
143
+
144
+ expect(result.success).toBe(false);
145
+ expect(result.error).toBeDefined();
146
+ expect(result.warnings).toBeDefined();
147
+ });
148
+
149
+ it('should return warnings for old sessions', async () => {
150
+ // Create a session with an old timestamp
151
+ const oldSession = {
152
+ sessionId: 'old-session',
153
+ startTime: new Date(Date.now() - 8 * 24 * 60 * 60 * 1000), // 8 days ago
154
+ endTime: new Date(Date.now() - 8 * 24 * 60 * 60 * 1000),
155
+ };
156
+ await storage.save('old-session', oldSession);
157
+
158
+ const result = await sessionManager.executeSessionRestore('old-session');
159
+
160
+ expect(result.success).toBe(true);
161
+ expect(result.warnings).toBeDefined();
162
+ expect(result.warnings!.some(w => w.includes('days old'))).toBe(true);
163
+ });
164
+
165
+ it('should start a new session after restoration', async () => {
166
+ // Create and end a session
167
+ const context = {
168
+ event: HookEvent.SessionStart,
169
+ timestamp: new Date(),
170
+ session: { id: 'new-after-restore', startTime: new Date() },
171
+ };
172
+ await sessionManager['handleSessionStart'](context);
173
+ await sessionManager.executeSessionEnd();
174
+
175
+ await sessionManager.executeSessionRestore('new-after-restore');
176
+
177
+ expect(sessionManager.getCurrentSessionId()).toBeDefined();
178
+ expect(sessionManager.getCurrentSessionId()).toContain('restored');
179
+ });
180
+
181
+ it('should count restored items', async () => {
182
+ // Create a session with tasks and agents
183
+ const sessionState = {
184
+ sessionId: 'count-test',
185
+ startTime: new Date(),
186
+ endTime: new Date(),
187
+ activeTasks: [
188
+ { id: 'task-1', description: 'Task 1', status: 'completed' as const },
189
+ { id: 'task-2', description: 'Task 2', status: 'in_progress' as const },
190
+ ],
191
+ spawnedAgents: [
192
+ { id: 'agent-1', type: 'coder', status: 'active' as const },
193
+ ],
194
+ memoryEntries: [
195
+ { key: 'key-1', namespace: 'default', type: 'string' },
196
+ ],
197
+ };
198
+ await storage.save('count-test', sessionState);
199
+
200
+ const result = await sessionManager.executeSessionRestore('count-test');
201
+
202
+ expect(result.tasksRestored).toBe(2);
203
+ expect(result.agentsRestored).toBe(1);
204
+ expect(result.memoryRestored).toBe(1);
205
+ });
206
+ });
207
+
208
+ describe('activity tracking', () => {
209
+ it('should track task executions', async () => {
210
+ // Start session
211
+ const context = {
212
+ event: HookEvent.SessionStart,
213
+ timestamp: new Date(),
214
+ session: { id: 'track-tasks', startTime: new Date() },
215
+ };
216
+ await sessionManager['handleSessionStart'](context);
217
+
218
+ // Track a successful task
219
+ await sessionManager['trackTaskExecution']({
220
+ event: HookEvent.PostTaskExecute,
221
+ timestamp: new Date(),
222
+ metadata: { success: true },
223
+ });
224
+
225
+ // Track a failed task
226
+ await sessionManager['trackTaskExecution']({
227
+ event: HookEvent.PostTaskExecute,
228
+ timestamp: new Date(),
229
+ metadata: { success: false },
230
+ });
231
+
232
+ const activity = sessionManager.getCurrentActivity();
233
+ expect(activity.tasksExecuted).toBe(2);
234
+ expect(activity.tasksSucceeded).toBe(1);
235
+ expect(activity.tasksFailed).toBe(1);
236
+ });
237
+
238
+ it('should track command executions', async () => {
239
+ // Start session
240
+ const context = {
241
+ event: HookEvent.SessionStart,
242
+ timestamp: new Date(),
243
+ session: { id: 'track-commands', startTime: new Date() },
244
+ };
245
+ await sessionManager['handleSessionStart'](context);
246
+
247
+ // Track commands
248
+ await sessionManager['trackCommandExecution']({
249
+ event: HookEvent.PostCommand,
250
+ timestamp: new Date(),
251
+ });
252
+ await sessionManager['trackCommandExecution']({
253
+ event: HookEvent.PostCommand,
254
+ timestamp: new Date(),
255
+ });
256
+
257
+ const activity = sessionManager.getCurrentActivity();
258
+ expect(activity.commandsExecuted).toBe(2);
259
+ });
260
+
261
+ it('should track file modifications', async () => {
262
+ // Start session
263
+ const context = {
264
+ event: HookEvent.SessionStart,
265
+ timestamp: new Date(),
266
+ session: { id: 'track-files', startTime: new Date() },
267
+ };
268
+ await sessionManager['handleSessionStart'](context);
269
+
270
+ // Track file modifications
271
+ await sessionManager['trackFileModification']({
272
+ event: HookEvent.PostEdit,
273
+ timestamp: new Date(),
274
+ file: { path: '/src/file1.ts', operation: 'edit' },
275
+ });
276
+ await sessionManager['trackFileModification']({
277
+ event: HookEvent.PostEdit,
278
+ timestamp: new Date(),
279
+ file: { path: '/src/file2.ts', operation: 'edit' },
280
+ });
281
+ // Same file again
282
+ await sessionManager['trackFileModification']({
283
+ event: HookEvent.PostEdit,
284
+ timestamp: new Date(),
285
+ file: { path: '/src/file1.ts', operation: 'edit' },
286
+ });
287
+
288
+ const activity = sessionManager.getCurrentActivity();
289
+ expect(activity.filesModified.size).toBe(2);
290
+ });
291
+
292
+ it('should track agent spawns', async () => {
293
+ // Start session
294
+ const context = {
295
+ event: HookEvent.SessionStart,
296
+ timestamp: new Date(),
297
+ session: { id: 'track-agents', startTime: new Date() },
298
+ };
299
+ await sessionManager['handleSessionStart'](context);
300
+
301
+ // Track agent spawns
302
+ await sessionManager['trackAgentSpawn']({
303
+ event: HookEvent.PostAgentSpawn,
304
+ timestamp: new Date(),
305
+ agent: { id: 'agent-1', type: 'coder' },
306
+ });
307
+ await sessionManager['trackAgentSpawn']({
308
+ event: HookEvent.PostAgentSpawn,
309
+ timestamp: new Date(),
310
+ agent: { id: 'agent-2', type: 'tester' },
311
+ });
312
+
313
+ const activity = sessionManager.getCurrentActivity();
314
+ expect(activity.agentsSpawned.size).toBe(2);
315
+ });
316
+ });
317
+
318
+ describe('session management', () => {
319
+ it('should list available sessions', async () => {
320
+ // Create multiple sessions
321
+ for (const id of ['list-1', 'list-2', 'list-3']) {
322
+ const context = {
323
+ event: HookEvent.SessionStart,
324
+ timestamp: new Date(),
325
+ session: { id, startTime: new Date() },
326
+ };
327
+ await sessionManager['handleSessionStart'](context);
328
+ await sessionManager.executeSessionEnd();
329
+ }
330
+
331
+ const sessions = await sessionManager.listSessions();
332
+ expect(sessions.length).toBe(3);
333
+ });
334
+
335
+ it('should delete a session', async () => {
336
+ // Create a session
337
+ const context = {
338
+ event: HookEvent.SessionStart,
339
+ timestamp: new Date(),
340
+ session: { id: 'delete-me', startTime: new Date() },
341
+ };
342
+ await sessionManager['handleSessionStart'](context);
343
+ await sessionManager.executeSessionEnd();
344
+
345
+ const deleted = await sessionManager.deleteSession('delete-me');
346
+ expect(deleted).toBe(true);
347
+
348
+ const sessions = await sessionManager.listSessions();
349
+ expect(sessions.find(s => s.id === 'delete-me')).toBeUndefined();
350
+ });
351
+
352
+ it('should return false when deleting non-existent session', async () => {
353
+ const deleted = await sessionManager.deleteSession('non-existent');
354
+ expect(deleted).toBe(false);
355
+ });
356
+ });
357
+ });
@@ -0,0 +1,193 @@
1
+ /**
2
+ * V3 Task Hooks Tests
3
+ *
4
+ * Tests for pre-task and post-task hook functionality.
5
+ *
6
+ * @module v3/shared/hooks/__tests__/task-hooks.test
7
+ */
8
+
9
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
10
+ import {
11
+ createHookRegistry,
12
+ createTaskHooksManager,
13
+ TaskHooksManager,
14
+ HookRegistry,
15
+ HookEvent,
16
+ } from '../../src/hooks/index.js';
17
+
18
+ describe('TaskHooksManager', () => {
19
+ let registry: HookRegistry;
20
+ let taskManager: TaskHooksManager;
21
+
22
+ beforeEach(() => {
23
+ registry = createHookRegistry();
24
+ taskManager = createTaskHooksManager(registry);
25
+ });
26
+
27
+ describe('pre-task hook', () => {
28
+ it('should register pre-task hook on creation', () => {
29
+ const hooks = registry.getHandlers(HookEvent.PreTaskExecute);
30
+ expect(hooks.length).toBeGreaterThan(0);
31
+ expect(hooks.some(h => h.name === 'task-hooks:pre-task')).toBe(true);
32
+ });
33
+
34
+ it('should analyze task and suggest agents for coding task', async () => {
35
+ const result = await taskManager.executePreTask(
36
+ 'task-123',
37
+ 'Implement user authentication feature'
38
+ );
39
+
40
+ expect(result.success).toBe(true);
41
+ expect(result.suggestedAgents).toBeDefined();
42
+ expect(result.suggestedAgents!.length).toBeGreaterThan(0);
43
+ expect(result.suggestedAgents![0].type).toBe('coder');
44
+ expect(result.suggestedAgents![0].confidence).toBeGreaterThan(0);
45
+ });
46
+
47
+ it('should suggest security-architect for security tasks', async () => {
48
+ const result = await taskManager.executePreTask(
49
+ 'task-456',
50
+ 'Fix security vulnerability in authentication'
51
+ );
52
+
53
+ expect(result.success).toBe(true);
54
+ expect(result.suggestedAgents).toBeDefined();
55
+ const securityAgent = result.suggestedAgents!.find(a => a.type === 'security-architect');
56
+ expect(securityAgent).toBeDefined();
57
+ });
58
+
59
+ it('should suggest tester for test-related tasks', async () => {
60
+ const result = await taskManager.executePreTask(
61
+ 'task-789',
62
+ 'Write unit tests for user service'
63
+ );
64
+
65
+ expect(result.success).toBe(true);
66
+ expect(result.suggestedAgents).toBeDefined();
67
+ const testerAgent = result.suggestedAgents!.find(a => a.type === 'tester');
68
+ expect(testerAgent).toBeDefined();
69
+ });
70
+
71
+ it('should estimate complexity based on task description', async () => {
72
+ // Simple task
73
+ const simpleResult = await taskManager.executePreTask(
74
+ 'task-simple',
75
+ 'Fix typo in readme'
76
+ );
77
+ expect(simpleResult.complexity).toBe('low');
78
+
79
+ // Complex task
80
+ const complexResult = await taskManager.executePreTask(
81
+ 'task-complex',
82
+ 'Refactor and redesign the entire authentication system with multiple OAuth providers'
83
+ );
84
+ expect(complexResult.complexity).toBe('high');
85
+ });
86
+
87
+ it('should detect risks in task description', async () => {
88
+ const result = await taskManager.executePreTask(
89
+ 'task-risky',
90
+ 'Delete old data from production database'
91
+ );
92
+
93
+ expect(result.risks).toBeDefined();
94
+ expect(result.risks!.length).toBeGreaterThan(0);
95
+ expect(result.risks!.some(r => r.includes('production'))).toBe(true);
96
+ });
97
+
98
+ it('should track active tasks', async () => {
99
+ await taskManager.executePreTask('task-1', 'Task 1');
100
+ await taskManager.executePreTask('task-2', 'Task 2');
101
+
102
+ const activeTasks = taskManager.getActiveTasks();
103
+ expect(activeTasks.size).toBe(2);
104
+ expect(activeTasks.has('task-1')).toBe(true);
105
+ expect(activeTasks.has('task-2')).toBe(true);
106
+ });
107
+
108
+ it('should provide recommendations for high complexity tasks', async () => {
109
+ const result = await taskManager.executePreTask(
110
+ 'task-high-complexity',
111
+ 'Implement complex distributed system with multiple services'
112
+ );
113
+
114
+ expect(result.recommendations).toBeDefined();
115
+ expect(result.recommendations!.length).toBeGreaterThan(0);
116
+ });
117
+ });
118
+
119
+ describe('post-task hook', () => {
120
+ it('should register post-task hook on creation', () => {
121
+ const hooks = registry.getHandlers(HookEvent.PostTaskExecute);
122
+ expect(hooks.length).toBeGreaterThan(0);
123
+ expect(hooks.some(h => h.name === 'task-hooks:post-task')).toBe(true);
124
+ });
125
+
126
+ it('should record successful task outcome', async () => {
127
+ // First start the task
128
+ await taskManager.executePreTask('task-success', 'Test task');
129
+
130
+ // Then complete it
131
+ const result = await taskManager.executePostTask('task-success', true);
132
+
133
+ expect(result.success).toBe(true);
134
+ expect(result.outcome).toBeDefined();
135
+ expect(result.outcome!.success).toBe(true);
136
+ expect(result.outcome!.duration).toBeGreaterThanOrEqual(0);
137
+ });
138
+
139
+ it('should record failed task outcome', async () => {
140
+ await taskManager.executePreTask('task-failed', 'Test task');
141
+
142
+ const result = await taskManager.executePostTask('task-failed', false, {
143
+ error: 'Test failed due to timeout',
144
+ });
145
+
146
+ expect(result.success).toBe(true);
147
+ expect(result.outcome).toBeDefined();
148
+ expect(result.outcome!.success).toBe(false);
149
+ });
150
+
151
+ it('should create learning trajectory', async () => {
152
+ await taskManager.executePreTask('task-learn', 'Test task');
153
+ const result = await taskManager.executePostTask('task-learn', true);
154
+
155
+ expect(result.trajectoryId).toBeDefined();
156
+ expect(result.trajectoryId).toContain('trajectory-');
157
+ });
158
+
159
+ it('should track learning updates', async () => {
160
+ await taskManager.executePreTask('task-updates', 'Test task');
161
+ const result = await taskManager.executePostTask('task-updates', true);
162
+
163
+ expect(result.learningUpdates).toBeDefined();
164
+ expect(result.learningUpdates!.trajectoriesRecorded).toBe(1);
165
+ });
166
+
167
+ it('should remove task from active tasks after completion', async () => {
168
+ await taskManager.executePreTask('task-cleanup', 'Test task');
169
+ expect(taskManager.getActiveTasks().has('task-cleanup')).toBe(true);
170
+
171
+ await taskManager.executePostTask('task-cleanup', true);
172
+ expect(taskManager.getActiveTasks().has('task-cleanup')).toBe(false);
173
+ });
174
+
175
+ it('should handle post-task without pre-task gracefully', async () => {
176
+ const result = await taskManager.executePostTask('task-no-pre', true);
177
+
178
+ expect(result.success).toBe(true);
179
+ expect(result.outcome).toBeDefined();
180
+ });
181
+ });
182
+
183
+ describe('clearActiveTasks', () => {
184
+ it('should clear all active tasks', async () => {
185
+ await taskManager.executePreTask('task-1', 'Task 1');
186
+ await taskManager.executePreTask('task-2', 'Task 2');
187
+ expect(taskManager.getActiveTasks().size).toBe(2);
188
+
189
+ taskManager.clearActiveTasks();
190
+ expect(taskManager.getActiveTasks().size).toBe(0);
191
+ });
192
+ });
193
+ });