@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,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
|
+
}
|