@fleettools/squawk 0.1.0 → 0.1.1
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/package.json +1 -1
- package/AGENTS.md +0 -28
- package/dist/src/db/checkpoint-storage.d.ts +0 -19
- package/dist/src/db/checkpoint-storage.d.ts.map +0 -1
- package/dist/src/db/checkpoint-storage.js +0 -355
- package/dist/src/db/checkpoint-storage.js.map +0 -1
- package/dist/src/db/index.d.ts +0 -30
- package/dist/src/db/index.d.ts.map +0 -1
- package/dist/src/db/index.js +0 -329
- package/dist/src/db/index.js.map +0 -1
- package/dist/src/db/sqlite.d.ts +0 -31
- package/dist/src/db/sqlite.d.ts.map +0 -1
- package/dist/src/db/sqlite.js +0 -558
- package/dist/src/db/sqlite.js.map +0 -1
- package/dist/src/db/types.d.ts +0 -611
- package/dist/src/db/types.d.ts.map +0 -1
- package/dist/src/db/types.js +0 -4
- package/dist/src/db/types.js.map +0 -1
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -285
- package/dist/src/index.js.map +0 -1
- package/dist/src/recovery/checkpointing.d.ts +0 -244
- package/dist/src/recovery/checkpointing.d.ts.map +0 -1
- package/dist/src/recovery/checkpointing.js +0 -511
- package/dist/src/recovery/checkpointing.js.map +0 -1
- package/dist/src/recovery/detection.d.ts +0 -137
- package/dist/src/recovery/detection.d.ts.map +0 -1
- package/dist/src/recovery/detection.js +0 -240
- package/dist/src/recovery/detection.js.map +0 -1
- package/dist/src/recovery/detector.d.ts +0 -34
- package/dist/src/recovery/detector.d.ts.map +0 -1
- package/dist/src/recovery/detector.js +0 -42
- package/dist/src/recovery/detector.js.map +0 -1
- package/dist/src/recovery/index.d.ts +0 -3
- package/dist/src/recovery/index.d.ts.map +0 -1
- package/dist/src/recovery/index.js +0 -3
- package/dist/src/recovery/index.js.map +0 -1
- package/dist/src/recovery/restorer.d.ts +0 -51
- package/dist/src/recovery/restorer.d.ts.map +0 -1
- package/dist/src/recovery/restorer.js +0 -266
- package/dist/src/recovery/restorer.js.map +0 -1
- package/dist/src/schemas.d.ts +0 -142
- package/dist/src/schemas.d.ts.map +0 -1
- package/dist/src/schemas.js +0 -110
- package/dist/src/schemas.js.map +0 -1
- package/src/db/checkpoint-storage.ts +0 -443
- package/src/db/index.d.ts +0 -30
- package/src/db/index.d.ts.map +0 -1
- package/src/db/index.js.map +0 -1
- package/src/db/index.ts +0 -417
- package/src/db/schema.sql +0 -112
- package/src/db/sqlite.d.ts +0 -31
- package/src/db/sqlite.d.ts.map +0 -1
- package/src/db/sqlite.js +0 -667
- package/src/db/sqlite.js.map +0 -1
- package/src/db/sqlite.ts +0 -677
- package/src/db/types.d.ts +0 -612
- package/src/db/types.d.ts.map +0 -1
- package/src/db/types.js +0 -4
- package/src/db/types.js.map +0 -1
- package/src/db/types.ts +0 -771
- package/src/index.ts +0 -332
- package/src/recovery/detector.ts +0 -82
- package/src/recovery/index.ts +0 -3
- package/src/recovery/restorer.ts +0 -377
|
@@ -1,511 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 3: Checkpoint Triggering System
|
|
3
|
-
*
|
|
4
|
-
* Implements automatic checkpoint creation based on:
|
|
5
|
-
* - Progress milestones (25%, 50%, 75%)
|
|
6
|
-
* - Error conditions (exceptions, API errors, lock timeouts, message failures)
|
|
7
|
-
* - Manual triggers (user-initiated)
|
|
8
|
-
*
|
|
9
|
-
* All checkpoint operations are non-blocking async to avoid delaying mission progress.
|
|
10
|
-
*
|
|
11
|
-
* @since 1.0.0
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* Monitors mission progress and creates checkpoints at milestones
|
|
15
|
-
*
|
|
16
|
-
* Triggers checkpoints at 25%, 50%, 75% completion to enable recovery
|
|
17
|
-
* from context window compaction at natural breakpoints.
|
|
18
|
-
*/
|
|
19
|
-
export class ProgressCheckpointTrigger {
|
|
20
|
-
lastCheckpointProgress = 0;
|
|
21
|
-
lastCheckpointTime = 0;
|
|
22
|
-
milestones;
|
|
23
|
-
minIntervalMs;
|
|
24
|
-
constructor(options = {}) {
|
|
25
|
-
this.milestones = options.milestones ?? [25, 50, 75];
|
|
26
|
-
this.minIntervalMs = options.minIntervalMs ?? 60000; // 1 minute
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Check if progress milestone has been reached
|
|
30
|
-
*
|
|
31
|
-
* Returns true if:
|
|
32
|
-
* 1. Current progress crosses a milestone threshold
|
|
33
|
-
* 2. Minimum interval since last checkpoint has passed
|
|
34
|
-
*/
|
|
35
|
-
shouldCheckpoint(currentProgress) {
|
|
36
|
-
const now = Date.now();
|
|
37
|
-
const timeSinceLastCheckpoint = now - this.lastCheckpointTime;
|
|
38
|
-
// Check if minimum interval has passed
|
|
39
|
-
if (timeSinceLastCheckpoint < this.minIntervalMs) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
// Check if we've crossed a milestone
|
|
43
|
-
for (const milestone of this.milestones) {
|
|
44
|
-
if (this.lastCheckpointProgress < milestone &&
|
|
45
|
-
currentProgress >= milestone) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Record that a checkpoint was created at this progress level
|
|
53
|
-
*/
|
|
54
|
-
recordCheckpoint(progress) {
|
|
55
|
-
this.lastCheckpointProgress = progress;
|
|
56
|
-
this.lastCheckpointTime = Date.now();
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Get the next milestone after current progress
|
|
60
|
-
*/
|
|
61
|
-
getNextMilestone(currentProgress) {
|
|
62
|
-
for (const milestone of this.milestones) {
|
|
63
|
-
if (currentProgress < milestone) {
|
|
64
|
-
return milestone;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Detects error conditions and creates checkpoints to preserve state
|
|
72
|
-
*
|
|
73
|
-
* Monitors for:
|
|
74
|
-
* - Unhandled exceptions in sorties
|
|
75
|
-
* - API errors (4xx/5xx responses)
|
|
76
|
-
* - Lock acquisition timeouts
|
|
77
|
-
* - Message delivery failures
|
|
78
|
-
*
|
|
79
|
-
* Captures error details in recovery context for debugging and recovery.
|
|
80
|
-
*/
|
|
81
|
-
export class ErrorCheckpointTrigger {
|
|
82
|
-
lastErrorCheckpointTime = 0;
|
|
83
|
-
minIntervalMs;
|
|
84
|
-
enabled;
|
|
85
|
-
recentErrors = new Map();
|
|
86
|
-
constructor(options = {}) {
|
|
87
|
-
this.enabled = options.enabled ?? true;
|
|
88
|
-
this.minIntervalMs = options.minIntervalMs ?? 30000; // 30 seconds
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Check if error checkpoint should be created
|
|
92
|
-
*
|
|
93
|
-
* Returns true if:
|
|
94
|
-
* 1. Error detection is enabled
|
|
95
|
-
* 2. Minimum interval since last error checkpoint has passed
|
|
96
|
-
*/
|
|
97
|
-
shouldCheckpoint() {
|
|
98
|
-
if (!this.enabled) {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
const now = Date.now();
|
|
102
|
-
const timeSinceLastCheckpoint = now - this.lastErrorCheckpointTime;
|
|
103
|
-
return timeSinceLastCheckpoint >= this.minIntervalMs;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Record an unhandled exception
|
|
107
|
-
*/
|
|
108
|
-
recordException(sortieId, error, context) {
|
|
109
|
-
const blocker = {
|
|
110
|
-
type: 'exception',
|
|
111
|
-
description: `Unhandled exception in sortie ${sortieId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
112
|
-
affected_sortie: sortieId,
|
|
113
|
-
resolution_hint: 'Review error details and retry sortie',
|
|
114
|
-
timestamp: new Date().toISOString(),
|
|
115
|
-
error_details: context,
|
|
116
|
-
};
|
|
117
|
-
this.addBlocker(sortieId, blocker);
|
|
118
|
-
return blocker;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Record an API error
|
|
122
|
-
*/
|
|
123
|
-
recordApiError(sortieId, statusCode, message, endpoint) {
|
|
124
|
-
const blocker = {
|
|
125
|
-
type: 'api_error',
|
|
126
|
-
description: `API error ${statusCode} from ${endpoint || 'unknown endpoint'}: ${message}`,
|
|
127
|
-
affected_sortie: sortieId,
|
|
128
|
-
resolution_hint: statusCode >= 500
|
|
129
|
-
? 'Server error - retry after delay'
|
|
130
|
-
: 'Client error - check request and retry',
|
|
131
|
-
timestamp: new Date().toISOString(),
|
|
132
|
-
error_code: String(statusCode),
|
|
133
|
-
};
|
|
134
|
-
this.addBlocker(sortieId, blocker);
|
|
135
|
-
return blocker;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Record a lock acquisition timeout
|
|
139
|
-
*/
|
|
140
|
-
recordLockTimeout(sortieId, filePath, heldBy) {
|
|
141
|
-
const blocker = {
|
|
142
|
-
type: 'lock_timeout',
|
|
143
|
-
description: `Lock timeout on file ${filePath}${heldBy ? ` (held by ${heldBy})` : ''}`,
|
|
144
|
-
affected_sortie: sortieId,
|
|
145
|
-
resolution_hint: `Release lock on ${filePath} or wait for holder to complete`,
|
|
146
|
-
timestamp: new Date().toISOString(),
|
|
147
|
-
};
|
|
148
|
-
this.addBlocker(sortieId, blocker);
|
|
149
|
-
return blocker;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Record a message delivery failure
|
|
153
|
-
*/
|
|
154
|
-
recordMessageFailure(sortieId, messageId, reason) {
|
|
155
|
-
const blocker = {
|
|
156
|
-
type: 'message_failure',
|
|
157
|
-
description: `Message delivery failed for ${messageId}: ${reason}`,
|
|
158
|
-
affected_sortie: sortieId,
|
|
159
|
-
resolution_hint: 'Verify recipient is available and retry message',
|
|
160
|
-
timestamp: new Date().toISOString(),
|
|
161
|
-
};
|
|
162
|
-
this.addBlocker(sortieId, blocker);
|
|
163
|
-
return blocker;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Record a dependency blocker
|
|
167
|
-
*/
|
|
168
|
-
recordDependencyBlocker(sortieId, dependsOn, reason) {
|
|
169
|
-
const blocker = {
|
|
170
|
-
type: 'dependency',
|
|
171
|
-
description: `Blocked waiting for sortie ${dependsOn}${reason ? `: ${reason}` : ''}`,
|
|
172
|
-
affected_sortie: sortieId,
|
|
173
|
-
resolution_hint: `Wait for sortie ${dependsOn} to complete`,
|
|
174
|
-
timestamp: new Date().toISOString(),
|
|
175
|
-
};
|
|
176
|
-
this.addBlocker(sortieId, blocker);
|
|
177
|
-
return blocker;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Record a generic blocker
|
|
181
|
-
*/
|
|
182
|
-
recordBlocker(sortieId, blocker) {
|
|
183
|
-
if (!blocker.timestamp) {
|
|
184
|
-
blocker.timestamp = new Date().toISOString();
|
|
185
|
-
}
|
|
186
|
-
this.addBlocker(sortieId, blocker);
|
|
187
|
-
return blocker;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Get all blockers for a sortie
|
|
191
|
-
*/
|
|
192
|
-
getBlockers(sortieId) {
|
|
193
|
-
return this.recentErrors.get(sortieId) ?? [];
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Get all blockers across all sorties
|
|
197
|
-
*/
|
|
198
|
-
getAllBlockers() {
|
|
199
|
-
const all = [];
|
|
200
|
-
for (const blockers of this.recentErrors.values()) {
|
|
201
|
-
all.push(...blockers);
|
|
202
|
-
}
|
|
203
|
-
return all;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Clear blockers for a sortie (after recovery)
|
|
207
|
-
*/
|
|
208
|
-
clearBlockers(sortieId) {
|
|
209
|
-
this.recentErrors.delete(sortieId);
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Record that error checkpoint was created
|
|
213
|
-
*/
|
|
214
|
-
recordCheckpoint() {
|
|
215
|
-
this.lastErrorCheckpointTime = Date.now();
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Internal: Add blocker to tracking
|
|
219
|
-
*/
|
|
220
|
-
addBlocker(sortieId, blocker) {
|
|
221
|
-
if (!this.recentErrors.has(sortieId)) {
|
|
222
|
-
this.recentErrors.set(sortieId, []);
|
|
223
|
-
}
|
|
224
|
-
this.recentErrors.get(sortieId).push(blocker);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Creates checkpoints with full state snapshots
|
|
229
|
-
*
|
|
230
|
-
* Captures:
|
|
231
|
-
* - Mission and sortie state
|
|
232
|
-
* - Active file locks
|
|
233
|
-
* - Pending messages
|
|
234
|
-
* - Recovery context for LLM prompt injection
|
|
235
|
-
*/
|
|
236
|
-
export class CheckpointCreator {
|
|
237
|
-
db;
|
|
238
|
-
agentId;
|
|
239
|
-
constructor(options) {
|
|
240
|
-
this.db = options.db;
|
|
241
|
-
this.agentId = options.agentId;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Create a checkpoint with full state snapshot
|
|
245
|
-
*
|
|
246
|
-
* Non-blocking: returns immediately, checkpoint creation happens in background
|
|
247
|
-
*/
|
|
248
|
-
async createCheckpoint(missionId, trigger, options) {
|
|
249
|
-
// Get mission
|
|
250
|
-
const mission = await this.db.missions.getById(missionId);
|
|
251
|
-
if (!mission) {
|
|
252
|
-
throw new Error(`Mission not found: ${missionId}`);
|
|
253
|
-
}
|
|
254
|
-
// Calculate progress
|
|
255
|
-
const progressPercent = options?.progressPercent ??
|
|
256
|
-
(mission.total_sorties > 0
|
|
257
|
-
? (mission.completed_sorties / mission.total_sorties) * 100
|
|
258
|
-
: 0);
|
|
259
|
-
// Get sorties
|
|
260
|
-
const sorties = await this.db.sorties.getByMission(missionId);
|
|
261
|
-
const sortieSnapshots = sorties.map((s) => ({
|
|
262
|
-
id: s.id,
|
|
263
|
-
status: s.status,
|
|
264
|
-
assigned_to: s.assigned_to,
|
|
265
|
-
files: s.files,
|
|
266
|
-
started_at: s.started_at,
|
|
267
|
-
progress: s.progress,
|
|
268
|
-
progress_notes: s.progress_notes,
|
|
269
|
-
}));
|
|
270
|
-
// Get active locks
|
|
271
|
-
const locks = await this.db.locks.getActiveLocks(missionId);
|
|
272
|
-
const lockSnapshots = locks.map((l) => ({
|
|
273
|
-
id: l.id,
|
|
274
|
-
file: l.file,
|
|
275
|
-
held_by: l.reserved_by,
|
|
276
|
-
acquired_at: l.reserved_at,
|
|
277
|
-
purpose: l.purpose,
|
|
278
|
-
timeout_ms: new Date(l.expires_at).getTime() - Date.now(),
|
|
279
|
-
}));
|
|
280
|
-
// Get pending messages
|
|
281
|
-
const messages = await this.db.messages.getPendingMessages(missionId);
|
|
282
|
-
const messageSnapshots = messages.map((m) => ({
|
|
283
|
-
id: m.id,
|
|
284
|
-
from: m.sender_id ?? 'system',
|
|
285
|
-
to: [m.mailbox_id],
|
|
286
|
-
subject: m.message_type,
|
|
287
|
-
sent_at: m.sent_at,
|
|
288
|
-
delivered: m.status === 'acked',
|
|
289
|
-
}));
|
|
290
|
-
// Build recovery context
|
|
291
|
-
const recoveryContext = this.buildRecoveryContext(mission, sorties, options?.blockers ?? []);
|
|
292
|
-
// Create checkpoint
|
|
293
|
-
const checkpoint = await this.db.checkpoints.create({
|
|
294
|
-
mission_id: missionId,
|
|
295
|
-
trigger,
|
|
296
|
-
trigger_details: options?.triggerDetails,
|
|
297
|
-
progress_percent: progressPercent,
|
|
298
|
-
created_by: this.agentId,
|
|
299
|
-
sorties: sortieSnapshots,
|
|
300
|
-
active_locks: lockSnapshots,
|
|
301
|
-
pending_messages: messageSnapshots,
|
|
302
|
-
recovery_context: recoveryContext,
|
|
303
|
-
ttl_hours: options?.ttlHours ?? 168, // 7 days default
|
|
304
|
-
});
|
|
305
|
-
// Emit checkpoint_created event (non-blocking)
|
|
306
|
-
if (checkpoint) {
|
|
307
|
-
this.emitCheckpointEvent(checkpoint).catch((err) => {
|
|
308
|
-
console.error('Failed to emit checkpoint event:', err);
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
return checkpoint;
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Build recovery context for LLM prompt injection
|
|
315
|
-
*/
|
|
316
|
-
buildRecoveryContext(mission, sorties, blockers) {
|
|
317
|
-
const completedSorties = sorties.filter((s) => s.status === 'completed');
|
|
318
|
-
const inProgressSorties = sorties.filter((s) => s.status === 'in_progress');
|
|
319
|
-
const blockedSorties = sorties.filter((s) => s.status === 'blocked');
|
|
320
|
-
const pendingSorties = sorties.filter((s) => s.status === 'pending');
|
|
321
|
-
// Build next steps
|
|
322
|
-
const nextSteps = [];
|
|
323
|
-
if (inProgressSorties.length > 0) {
|
|
324
|
-
nextSteps.push(`Continue work on ${inProgressSorties.length} in-progress sortie(s)`);
|
|
325
|
-
}
|
|
326
|
-
if (blockedSorties.length > 0) {
|
|
327
|
-
nextSteps.push(`Resolve blockers for ${blockedSorties.length} sortie(s)`);
|
|
328
|
-
}
|
|
329
|
-
if (pendingSorties.length > 0) {
|
|
330
|
-
nextSteps.push(`Start ${pendingSorties.length} pending sortie(s)`);
|
|
331
|
-
}
|
|
332
|
-
if (nextSteps.length === 0) {
|
|
333
|
-
nextSteps.push('Review mission status and continue');
|
|
334
|
-
}
|
|
335
|
-
// Build blocker descriptions
|
|
336
|
-
const blockerDescriptions = blockers.map((b) => `${b.type}: ${b.description}`);
|
|
337
|
-
// Build files modified
|
|
338
|
-
const filesModified = new Set();
|
|
339
|
-
for (const sortie of sorties) {
|
|
340
|
-
if (sortie.files) {
|
|
341
|
-
sortie.files.forEach((f) => filesModified.add(f));
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
// Calculate elapsed time
|
|
345
|
-
const startTime = mission.started_at
|
|
346
|
-
? new Date(mission.started_at).getTime()
|
|
347
|
-
: new Date(mission.created_at).getTime();
|
|
348
|
-
const elapsedMs = Date.now() - startTime;
|
|
349
|
-
return {
|
|
350
|
-
last_action: `Checkpoint created at ${progressPercent(mission)}% progress`,
|
|
351
|
-
next_steps: nextSteps,
|
|
352
|
-
blockers: blockerDescriptions,
|
|
353
|
-
files_modified: Array.from(filesModified),
|
|
354
|
-
mission_summary: `${mission.title}: ${completedSorties.length}/${mission.total_sorties} sorties completed`,
|
|
355
|
-
elapsed_time_ms: elapsedMs,
|
|
356
|
-
last_activity_at: new Date().toISOString(),
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Emit checkpoint_created event (non-blocking)
|
|
361
|
-
*/
|
|
362
|
-
async emitCheckpointEvent(checkpoint) {
|
|
363
|
-
try {
|
|
364
|
-
await this.db.events.append({
|
|
365
|
-
event_type: 'checkpoint_created',
|
|
366
|
-
stream_type: 'checkpoint',
|
|
367
|
-
stream_id: checkpoint.id,
|
|
368
|
-
data: {
|
|
369
|
-
checkpoint_id: checkpoint.id,
|
|
370
|
-
mission_id: checkpoint.mission_id,
|
|
371
|
-
trigger: checkpoint.trigger,
|
|
372
|
-
progress_percent: checkpoint.progress_percent,
|
|
373
|
-
created_by: checkpoint.created_by,
|
|
374
|
-
},
|
|
375
|
-
occurred_at: new Date().toISOString(),
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
catch (error) {
|
|
379
|
-
console.error('Failed to emit checkpoint_created event:', error);
|
|
380
|
-
// Don't throw - checkpoint was already created
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Manages checkpoint lifecycle and cleanup
|
|
386
|
-
*
|
|
387
|
-
* Implements:
|
|
388
|
-
* - TTL-based expiration (default 7 days)
|
|
389
|
-
* - Per-mission retention (keep at least N checkpoints)
|
|
390
|
-
* - Automatic cleanup job (runs daily)
|
|
391
|
-
* - Archive support for old checkpoints
|
|
392
|
-
*/
|
|
393
|
-
export class CheckpointCleanupService {
|
|
394
|
-
db;
|
|
395
|
-
ttlDays;
|
|
396
|
-
keepPerMission;
|
|
397
|
-
includeCompletedMissions;
|
|
398
|
-
cleanupIntervalMs;
|
|
399
|
-
cleanupTimer;
|
|
400
|
-
constructor(db, options = {}) {
|
|
401
|
-
this.db = db;
|
|
402
|
-
this.ttlDays = options.ttlDays ?? 7;
|
|
403
|
-
this.keepPerMission = options.keepPerMission ?? 3;
|
|
404
|
-
this.includeCompletedMissions = options.includeCompletedMissions ?? false;
|
|
405
|
-
this.cleanupIntervalMs = options.cleanupIntervalMs ?? 86400000; // 24 hours
|
|
406
|
-
if (options.autoCleanup ?? true) {
|
|
407
|
-
this.startAutoCleanup();
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Start automatic cleanup job
|
|
412
|
-
*/
|
|
413
|
-
startAutoCleanup() {
|
|
414
|
-
if (this.cleanupTimer) {
|
|
415
|
-
return; // Already running
|
|
416
|
-
}
|
|
417
|
-
this.cleanupTimer = setInterval(() => {
|
|
418
|
-
this.cleanup().catch((err) => {
|
|
419
|
-
console.error('Checkpoint cleanup failed:', err);
|
|
420
|
-
});
|
|
421
|
-
}, this.cleanupIntervalMs);
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Stop automatic cleanup job
|
|
425
|
-
*/
|
|
426
|
-
stopAutoCleanup() {
|
|
427
|
-
if (this.cleanupTimer) {
|
|
428
|
-
clearInterval(this.cleanupTimer);
|
|
429
|
-
this.cleanupTimer = undefined;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Run cleanup immediately
|
|
434
|
-
*
|
|
435
|
-
* Removes checkpoints older than TTL, keeping minimum per mission
|
|
436
|
-
*/
|
|
437
|
-
async cleanup() {
|
|
438
|
-
let deletedCount = 0;
|
|
439
|
-
const now = Date.now();
|
|
440
|
-
const ttlMs = this.ttlDays * 24 * 60 * 60 * 1000;
|
|
441
|
-
// Get all checkpoints
|
|
442
|
-
const allCheckpoints = await this.db.checkpoints.list();
|
|
443
|
-
// Group by mission
|
|
444
|
-
const byMission = new Map();
|
|
445
|
-
for (const checkpoint of allCheckpoints) {
|
|
446
|
-
if (!byMission.has(checkpoint.mission_id)) {
|
|
447
|
-
byMission.set(checkpoint.mission_id, []);
|
|
448
|
-
}
|
|
449
|
-
byMission.get(checkpoint.mission_id).push(checkpoint);
|
|
450
|
-
}
|
|
451
|
-
// Process each mission
|
|
452
|
-
for (const [missionId, checkpoints] of byMission) {
|
|
453
|
-
// Check if mission is completed
|
|
454
|
-
const mission = await this.db.missions.getById(missionId);
|
|
455
|
-
if (mission?.status === 'completed' && !this.includeCompletedMissions) {
|
|
456
|
-
continue;
|
|
457
|
-
}
|
|
458
|
-
// Sort by timestamp (newest first)
|
|
459
|
-
checkpoints.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
460
|
-
// Keep minimum checkpoints, delete old ones
|
|
461
|
-
for (let i = this.keepPerMission; i < checkpoints.length; i++) {
|
|
462
|
-
const cp = checkpoints[i];
|
|
463
|
-
if (!cp)
|
|
464
|
-
continue;
|
|
465
|
-
const age = now - new Date(cp.timestamp).getTime();
|
|
466
|
-
if (age > ttlMs) {
|
|
467
|
-
const deleted = await this.db.checkpoints.delete(cp.id);
|
|
468
|
-
if (deleted) {
|
|
469
|
-
deletedCount++;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return deletedCount;
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Get cleanup statistics
|
|
478
|
-
*/
|
|
479
|
-
async getStats() {
|
|
480
|
-
const now = Date.now();
|
|
481
|
-
const ttlMs = this.ttlDays * 24 * 60 * 60 * 1000;
|
|
482
|
-
const allCheckpoints = await this.db.checkpoints.list();
|
|
483
|
-
let expiredCount = 0;
|
|
484
|
-
const byMission = {};
|
|
485
|
-
for (const checkpoint of allCheckpoints) {
|
|
486
|
-
const age = now - new Date(checkpoint.timestamp).getTime();
|
|
487
|
-
if (age > ttlMs) {
|
|
488
|
-
expiredCount++;
|
|
489
|
-
}
|
|
490
|
-
const missionId = checkpoint.mission_id;
|
|
491
|
-
if (!byMission[missionId]) {
|
|
492
|
-
byMission[missionId] = 0;
|
|
493
|
-
}
|
|
494
|
-
byMission[missionId]++;
|
|
495
|
-
}
|
|
496
|
-
return {
|
|
497
|
-
total_checkpoints: allCheckpoints.length,
|
|
498
|
-
expired_checkpoints: expiredCount,
|
|
499
|
-
by_mission: byMission,
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
// ============================================================================
|
|
504
|
-
// HELPER FUNCTION
|
|
505
|
-
// ============================================================================
|
|
506
|
-
function progressPercent(mission) {
|
|
507
|
-
if (mission.total_sorties === 0)
|
|
508
|
-
return 0;
|
|
509
|
-
return Math.round((mission.completed_sorties / mission.total_sorties) * 100);
|
|
510
|
-
}
|
|
511
|
-
//# sourceMappingURL=checkpointing.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"checkpointing.js","sourceRoot":"","sources":["../../../src/recovery/checkpointing.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 3: Inactivity Detection
|
|
3
|
-
*
|
|
4
|
-
* Monitors mission activity and detects when a mission has been inactive
|
|
5
|
-
* for longer than a threshold. Triggers recovery prompts to help users
|
|
6
|
-
* resume work after context window compaction.
|
|
7
|
-
*
|
|
8
|
-
* @since 1.0.0
|
|
9
|
-
*/
|
|
10
|
-
import type { Mission, Event } from '../db/types.js';
|
|
11
|
-
export interface InactivityDetectorOptions {
|
|
12
|
-
/** Inactivity threshold in milliseconds (default: 5 minutes) */
|
|
13
|
-
inactivityThresholdMs?: number;
|
|
14
|
-
/** Enable auto-resume flag support (default: true) */
|
|
15
|
-
autoResumeEnabled?: boolean;
|
|
16
|
-
}
|
|
17
|
-
export interface InactivityStatus {
|
|
18
|
-
mission_id: string;
|
|
19
|
-
mission_title: string;
|
|
20
|
-
is_inactive: boolean;
|
|
21
|
-
inactivity_duration_ms: number;
|
|
22
|
-
last_activity_at: string;
|
|
23
|
-
last_activity_type?: string;
|
|
24
|
-
should_prompt_recovery: boolean;
|
|
25
|
-
recovery_checkpoint_available: boolean;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Detects mission inactivity and triggers recovery prompts
|
|
29
|
-
*
|
|
30
|
-
* Monitors:
|
|
31
|
-
* - Time since last event for a mission
|
|
32
|
-
* - Availability of recovery checkpoints
|
|
33
|
-
* - User preference for auto-resume
|
|
34
|
-
*
|
|
35
|
-
* Helps users recover from context window compaction by detecting
|
|
36
|
-
* when a mission has been idle and offering to resume from checkpoint.
|
|
37
|
-
*/
|
|
38
|
-
export declare class InactivityDetector {
|
|
39
|
-
private inactivityThresholdMs;
|
|
40
|
-
private autoResumeEnabled;
|
|
41
|
-
constructor(options?: InactivityDetectorOptions);
|
|
42
|
-
/**
|
|
43
|
-
* Check if a mission is inactive
|
|
44
|
-
*
|
|
45
|
-
* Returns true if no events have been recorded for the mission
|
|
46
|
-
* within the inactivity threshold.
|
|
47
|
-
*/
|
|
48
|
-
isInactive(lastActivityTime: number): boolean;
|
|
49
|
-
/**
|
|
50
|
-
* Get inactivity duration in milliseconds
|
|
51
|
-
*/
|
|
52
|
-
getInactivityDuration(lastActivityTime: number): number;
|
|
53
|
-
/**
|
|
54
|
-
* Format inactivity duration as human-readable string
|
|
55
|
-
*/
|
|
56
|
-
formatDuration(durationMs: number): string;
|
|
57
|
-
/**
|
|
58
|
-
* Check inactivity status for a mission
|
|
59
|
-
*/
|
|
60
|
-
checkStatus(mission: Mission, lastEventTime: number, hasCheckpoint: boolean): InactivityStatus;
|
|
61
|
-
/**
|
|
62
|
-
* Generate recovery prompt for user
|
|
63
|
-
*/
|
|
64
|
-
generateRecoveryPrompt(status: InactivityStatus): string;
|
|
65
|
-
/**
|
|
66
|
-
* Check if auto-resume should be triggered
|
|
67
|
-
*
|
|
68
|
-
* Returns true if:
|
|
69
|
-
* 1. Mission is inactive
|
|
70
|
-
* 2. Checkpoint is available
|
|
71
|
-
* 3. Auto-resume is enabled
|
|
72
|
-
*/
|
|
73
|
-
shouldAutoResume(status: InactivityStatus): boolean;
|
|
74
|
-
/**
|
|
75
|
-
* Set inactivity threshold
|
|
76
|
-
*/
|
|
77
|
-
setInactivityThreshold(thresholdMs: number): void;
|
|
78
|
-
/**
|
|
79
|
-
* Enable/disable auto-resume
|
|
80
|
-
*/
|
|
81
|
-
setAutoResumeEnabled(enabled: boolean): void;
|
|
82
|
-
}
|
|
83
|
-
export interface InactivityMonitorOptions {
|
|
84
|
-
/** Database instance for querying missions and events */
|
|
85
|
-
db: {
|
|
86
|
-
missions: {
|
|
87
|
-
getByStatus: (status: string) => Promise<Mission[]>;
|
|
88
|
-
};
|
|
89
|
-
events: {
|
|
90
|
-
getLatestByStream: (type: string, id: string) => Promise<Event | null>;
|
|
91
|
-
};
|
|
92
|
-
checkpoints: {
|
|
93
|
-
getLatestByMission: (missionId: string) => Promise<any | null>;
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
/** Detector instance */
|
|
97
|
-
detector: InactivityDetector;
|
|
98
|
-
/** Callback when inactivity is detected */
|
|
99
|
-
onInactivityDetected?: (status: InactivityStatus) => Promise<void>;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Monitors multiple missions for inactivity
|
|
103
|
-
*
|
|
104
|
-
* Periodically checks all in-progress missions and triggers
|
|
105
|
-
* recovery prompts when inactivity is detected.
|
|
106
|
-
*/
|
|
107
|
-
export declare class InactivityMonitor {
|
|
108
|
-
private db;
|
|
109
|
-
private detector;
|
|
110
|
-
private onInactivityDetected?;
|
|
111
|
-
private monitoringTimer?;
|
|
112
|
-
private isMonitoring;
|
|
113
|
-
constructor(options: InactivityMonitorOptions);
|
|
114
|
-
/**
|
|
115
|
-
* Start monitoring missions for inactivity
|
|
116
|
-
*
|
|
117
|
-
* Runs check every 30 seconds by default
|
|
118
|
-
*/
|
|
119
|
-
startMonitoring(intervalMs?: number): void;
|
|
120
|
-
/**
|
|
121
|
-
* Stop monitoring missions
|
|
122
|
-
*/
|
|
123
|
-
stopMonitoring(): void;
|
|
124
|
-
/**
|
|
125
|
-
* Check all in-progress missions for inactivity
|
|
126
|
-
*/
|
|
127
|
-
checkAllMissions(): Promise<InactivityStatus[]>;
|
|
128
|
-
/**
|
|
129
|
-
* Check specific mission for inactivity
|
|
130
|
-
*/
|
|
131
|
-
checkMission(missionId: string): Promise<InactivityStatus | null>;
|
|
132
|
-
/**
|
|
133
|
-
* Get monitoring status
|
|
134
|
-
*/
|
|
135
|
-
isActive(): boolean;
|
|
136
|
-
}
|
|
137
|
-
//# sourceMappingURL=detection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../../src/recovery/detection.ts"],"names":[],"mappings":"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"}
|