@agent-relay/daemon 0.1.0

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 (109) hide show
  1. package/dist/agent-manager.d.ts +134 -0
  2. package/dist/agent-manager.d.ts.map +1 -0
  3. package/dist/agent-manager.js +578 -0
  4. package/dist/agent-manager.js.map +1 -0
  5. package/dist/agent-registry.d.ts +99 -0
  6. package/dist/agent-registry.d.ts.map +1 -0
  7. package/dist/agent-registry.js +213 -0
  8. package/dist/agent-registry.js.map +1 -0
  9. package/dist/agent-signing.d.ts +158 -0
  10. package/dist/agent-signing.d.ts.map +1 -0
  11. package/dist/agent-signing.js +523 -0
  12. package/dist/agent-signing.js.map +1 -0
  13. package/dist/api.d.ts +106 -0
  14. package/dist/api.d.ts.map +1 -0
  15. package/dist/api.js +876 -0
  16. package/dist/api.js.map +1 -0
  17. package/dist/auth.d.ts +94 -0
  18. package/dist/auth.d.ts.map +1 -0
  19. package/dist/auth.js +197 -0
  20. package/dist/auth.js.map +1 -0
  21. package/dist/channel-membership-store.d.ts +55 -0
  22. package/dist/channel-membership-store.d.ts.map +1 -0
  23. package/dist/channel-membership-store.js +176 -0
  24. package/dist/channel-membership-store.js.map +1 -0
  25. package/dist/cli-auth.d.ts +89 -0
  26. package/dist/cli-auth.d.ts.map +1 -0
  27. package/dist/cli-auth.js +792 -0
  28. package/dist/cli-auth.js.map +1 -0
  29. package/dist/cloud-sync.d.ts +150 -0
  30. package/dist/cloud-sync.d.ts.map +1 -0
  31. package/dist/cloud-sync.js +446 -0
  32. package/dist/cloud-sync.js.map +1 -0
  33. package/dist/connection.d.ts +130 -0
  34. package/dist/connection.d.ts.map +1 -0
  35. package/dist/connection.js +438 -0
  36. package/dist/connection.js.map +1 -0
  37. package/dist/consensus-integration.d.ts +167 -0
  38. package/dist/consensus-integration.d.ts.map +1 -0
  39. package/dist/consensus-integration.js +371 -0
  40. package/dist/consensus-integration.js.map +1 -0
  41. package/dist/consensus.d.ts +271 -0
  42. package/dist/consensus.d.ts.map +1 -0
  43. package/dist/consensus.js +632 -0
  44. package/dist/consensus.js.map +1 -0
  45. package/dist/delivery-tracker.d.ts +34 -0
  46. package/dist/delivery-tracker.d.ts.map +1 -0
  47. package/dist/delivery-tracker.js +104 -0
  48. package/dist/delivery-tracker.js.map +1 -0
  49. package/dist/enhanced-features.d.ts +118 -0
  50. package/dist/enhanced-features.d.ts.map +1 -0
  51. package/dist/enhanced-features.js +176 -0
  52. package/dist/enhanced-features.js.map +1 -0
  53. package/dist/index.d.ts +31 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +37 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/migrations/index.d.ts +73 -0
  58. package/dist/migrations/index.d.ts.map +1 -0
  59. package/dist/migrations/index.js +241 -0
  60. package/dist/migrations/index.js.map +1 -0
  61. package/dist/orchestrator.d.ts +217 -0
  62. package/dist/orchestrator.d.ts.map +1 -0
  63. package/dist/orchestrator.js +1143 -0
  64. package/dist/orchestrator.js.map +1 -0
  65. package/dist/rate-limiter.d.ts +68 -0
  66. package/dist/rate-limiter.d.ts.map +1 -0
  67. package/dist/rate-limiter.js +130 -0
  68. package/dist/rate-limiter.js.map +1 -0
  69. package/dist/registry.d.ts +9 -0
  70. package/dist/registry.d.ts.map +1 -0
  71. package/dist/registry.js +9 -0
  72. package/dist/registry.js.map +1 -0
  73. package/dist/relay-ledger.d.ts +261 -0
  74. package/dist/relay-ledger.d.ts.map +1 -0
  75. package/dist/relay-ledger.js +532 -0
  76. package/dist/relay-ledger.js.map +1 -0
  77. package/dist/relay-watchdog.d.ts +125 -0
  78. package/dist/relay-watchdog.d.ts.map +1 -0
  79. package/dist/relay-watchdog.js +611 -0
  80. package/dist/relay-watchdog.js.map +1 -0
  81. package/dist/repo-manager.d.ts +116 -0
  82. package/dist/repo-manager.d.ts.map +1 -0
  83. package/dist/repo-manager.js +384 -0
  84. package/dist/repo-manager.js.map +1 -0
  85. package/dist/router.d.ts +370 -0
  86. package/dist/router.d.ts.map +1 -0
  87. package/dist/router.js +1437 -0
  88. package/dist/router.js.map +1 -0
  89. package/dist/server.d.ts +174 -0
  90. package/dist/server.d.ts.map +1 -0
  91. package/dist/server.js +1001 -0
  92. package/dist/server.js.map +1 -0
  93. package/dist/spawn-manager.d.ts +78 -0
  94. package/dist/spawn-manager.d.ts.map +1 -0
  95. package/dist/spawn-manager.js +165 -0
  96. package/dist/spawn-manager.js.map +1 -0
  97. package/dist/sync-queue.d.ts +116 -0
  98. package/dist/sync-queue.d.ts.map +1 -0
  99. package/dist/sync-queue.js +361 -0
  100. package/dist/sync-queue.js.map +1 -0
  101. package/dist/types.d.ts +133 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +6 -0
  104. package/dist/types.js.map +1 -0
  105. package/dist/workspace-manager.d.ts +80 -0
  106. package/dist/workspace-manager.d.ts.map +1 -0
  107. package/dist/workspace-manager.js +314 -0
  108. package/dist/workspace-manager.js.map +1 -0
  109. package/package.json +52 -0
@@ -0,0 +1,532 @@
1
+ /**
2
+ * Relay Ledger - SQLite-based tracking of relay file processing
3
+ *
4
+ * Tracks files discovered in agent outboxes through their lifecycle:
5
+ * pending -> processing -> delivered/failed -> archived
6
+ *
7
+ * Features:
8
+ * - Atomic claims with status WHERE clause
9
+ * - Crash recovery (reset processing -> pending on startup)
10
+ * - Archive path tracking for auditing
11
+ * - Configurable retention
12
+ * - Schema migrations for version upgrades
13
+ */
14
+ import Database from 'better-sqlite3';
15
+ import path from 'node:path';
16
+ import fs from 'node:fs';
17
+ import crypto from 'node:crypto';
18
+ import { runMigrations, getPendingMigrations, verifyMigrations } from './migrations/index.js';
19
+ // ============================================================================
20
+ // Constants
21
+ // ============================================================================
22
+ const DEFAULT_MAX_RETRIES = 3;
23
+ const DEFAULT_ARCHIVE_RETENTION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
24
+ const DEFAULT_BUSY_TIMEOUT = 5000;
25
+ // Reserved agent names for special routing
26
+ const RESERVED_AGENT_NAMES = new Set(['Lead', 'System', 'Broadcast', '*']);
27
+ // ============================================================================
28
+ // RelayLedger Class
29
+ // ============================================================================
30
+ export class RelayLedger {
31
+ db;
32
+ config;
33
+ // Prepared statements for performance
34
+ stmtInsert;
35
+ stmtClaim;
36
+ stmtUpdateStatus;
37
+ stmtMarkDelivered;
38
+ stmtMarkFailed;
39
+ stmtMarkArchived;
40
+ stmtGetPending;
41
+ stmtGetByPath;
42
+ stmtGetById;
43
+ stmtResetProcessing;
44
+ stmtCleanupArchived;
45
+ stmtGetStats;
46
+ constructor(config) {
47
+ this.config = {
48
+ dbPath: config.dbPath,
49
+ maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
50
+ archiveRetentionMs: config.archiveRetentionMs ?? DEFAULT_ARCHIVE_RETENTION_MS,
51
+ busyTimeout: config.busyTimeout ?? DEFAULT_BUSY_TIMEOUT,
52
+ };
53
+ // Ensure directory exists
54
+ const dbDir = path.dirname(this.config.dbPath);
55
+ if (!fs.existsSync(dbDir)) {
56
+ fs.mkdirSync(dbDir, { recursive: true });
57
+ }
58
+ // Open database with recommended settings
59
+ this.db = new Database(this.config.dbPath);
60
+ this.db.pragma('journal_mode = WAL');
61
+ this.db.pragma(`busy_timeout = ${this.config.busyTimeout}`);
62
+ this.db.pragma('synchronous = NORMAL');
63
+ this.db.pragma('foreign_keys = ON');
64
+ this.initSchema();
65
+ this.prepareStatements();
66
+ }
67
+ /** Last migration result (for debugging) */
68
+ lastMigrationResult;
69
+ /**
70
+ * Initialize database schema using migrations
71
+ */
72
+ initSchema() {
73
+ // Run migrations (creates tables if they don't exist, or upgrades existing schema)
74
+ this.lastMigrationResult = runMigrations(this.db);
75
+ // Log migration results for debugging
76
+ if (this.lastMigrationResult.applied.length > 0) {
77
+ console.log(`[relay-ledger] Applied migrations: ${this.lastMigrationResult.applied.join(', ')}`);
78
+ }
79
+ if (this.lastMigrationResult.errors.length > 0) {
80
+ console.error(`[relay-ledger] Migration errors:`, this.lastMigrationResult.errors);
81
+ }
82
+ }
83
+ /**
84
+ * Get the last migration result
85
+ */
86
+ getMigrationResult() {
87
+ return this.lastMigrationResult;
88
+ }
89
+ /**
90
+ * Get pending migrations that haven't been applied
91
+ */
92
+ getPendingMigrations() {
93
+ return getPendingMigrations(this.db);
94
+ }
95
+ /**
96
+ * Verify migration checksums match what was originally applied
97
+ */
98
+ verifyMigrationIntegrity() {
99
+ return verifyMigrations(this.db);
100
+ }
101
+ /**
102
+ * Prepare frequently-used statements
103
+ */
104
+ prepareStatements() {
105
+ this.stmtInsert = this.db.prepare(`
106
+ INSERT INTO relay_files (file_id, source_path, symlink_path, agent_name, message_type, discovered_at, file_size, content_hash, file_mtime_ns, file_inode)
107
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
108
+ ON CONFLICT(file_id) DO NOTHING
109
+ `);
110
+ this.stmtClaim = this.db.prepare(`
111
+ UPDATE relay_files
112
+ SET status = 'processing', retries = retries + 1
113
+ WHERE file_id = ? AND status = 'pending' AND retries < max_retries
114
+ RETURNING *
115
+ `);
116
+ this.stmtUpdateStatus = this.db.prepare(`
117
+ UPDATE relay_files SET status = ? WHERE file_id = ?
118
+ `);
119
+ this.stmtMarkDelivered = this.db.prepare(`
120
+ UPDATE relay_files
121
+ SET status = 'delivered', processed_at = ?
122
+ WHERE file_id = ?
123
+ `);
124
+ this.stmtMarkFailed = this.db.prepare(`
125
+ UPDATE relay_files
126
+ SET status = 'failed', processed_at = ?, error = ?
127
+ WHERE file_id = ?
128
+ `);
129
+ this.stmtMarkArchived = this.db.prepare(`
130
+ UPDATE relay_files
131
+ SET status = 'archived', archive_path = ?, archived_at = ?
132
+ WHERE file_id = ?
133
+ `);
134
+ this.stmtGetPending = this.db.prepare(`
135
+ SELECT * FROM relay_files
136
+ WHERE status = 'pending' AND retries < max_retries
137
+ ORDER BY id ASC
138
+ LIMIT ?
139
+ `);
140
+ this.stmtGetByPath = this.db.prepare(`
141
+ SELECT * FROM relay_files WHERE source_path = ?
142
+ `);
143
+ this.stmtGetById = this.db.prepare(`
144
+ SELECT * FROM relay_files WHERE file_id = ?
145
+ `);
146
+ this.stmtResetProcessing = this.db.prepare(`
147
+ UPDATE relay_files
148
+ SET status = 'pending'
149
+ WHERE status = 'processing'
150
+ `);
151
+ this.stmtCleanupArchived = this.db.prepare(`
152
+ DELETE FROM relay_files
153
+ WHERE status = 'archived' AND archived_at < ?
154
+ `);
155
+ this.stmtGetStats = this.db.prepare(`
156
+ SELECT status, COUNT(*) as count FROM relay_files GROUP BY status
157
+ `);
158
+ }
159
+ /**
160
+ * Generate a unique file ID (12-char hex for ~281 trillion combinations)
161
+ */
162
+ generateFileId() {
163
+ return crypto.randomBytes(6).toString('hex');
164
+ }
165
+ /**
166
+ * Check if an agent name is reserved
167
+ */
168
+ isReservedAgentName(name) {
169
+ return RESERVED_AGENT_NAMES.has(name);
170
+ }
171
+ /**
172
+ * Register a newly discovered file
173
+ * @param sourcePath - Canonical path (symlinks resolved)
174
+ * @param agentName - Agent that owns the file
175
+ * @param messageType - Type of message (msg, spawn, release, etc.)
176
+ * @param fileSize - Size in bytes
177
+ * @param contentHash - Optional hash for deduplication
178
+ * @param fileMtimeNs - Optional modification time in nanoseconds
179
+ * @param fileInode - Optional inode number
180
+ * @param symlinkPath - Original path if it was a symlink (for debugging)
181
+ */
182
+ registerFile(sourcePath, agentName, messageType, fileSize, contentHash, fileMtimeNs, fileInode, symlinkPath) {
183
+ const fileId = this.generateFileId();
184
+ const now = Date.now();
185
+ this.stmtInsert.run(fileId, sourcePath, symlinkPath ?? null, agentName, messageType, now, fileSize, contentHash ?? null, fileMtimeNs ?? null, fileInode ?? null);
186
+ return fileId;
187
+ }
188
+ /**
189
+ * Check if a file is already registered
190
+ */
191
+ isFileRegistered(sourcePath) {
192
+ const row = this.stmtGetByPath.get(sourcePath);
193
+ return row !== undefined;
194
+ }
195
+ /**
196
+ * Atomically claim a file for processing
197
+ * Returns the record if claim succeeded, null otherwise
198
+ */
199
+ claimFile(fileId) {
200
+ const row = this.stmtClaim.get(fileId);
201
+ if (!row) {
202
+ // Check why claim failed
203
+ const existing = this.stmtGetById.get(fileId);
204
+ if (!existing) {
205
+ return { success: false, reason: 'File not found' };
206
+ }
207
+ if (existing.status !== 'pending') {
208
+ return { success: false, reason: `File status is ${existing.status}, not pending` };
209
+ }
210
+ if (existing.retries >= existing.max_retries) {
211
+ return { success: false, reason: `Max retries (${existing.max_retries}) exceeded` };
212
+ }
213
+ return { success: false, reason: 'Unknown claim failure' };
214
+ }
215
+ return {
216
+ success: true,
217
+ record: this.rowToRecord(row),
218
+ };
219
+ }
220
+ /**
221
+ * Mark a file as successfully delivered
222
+ */
223
+ markDelivered(fileId) {
224
+ this.stmtMarkDelivered.run(Date.now(), fileId);
225
+ }
226
+ /**
227
+ * Mark a file as failed (will retry if under max retries)
228
+ */
229
+ markFailed(fileId, error) {
230
+ const record = this.getById(fileId);
231
+ if (!record)
232
+ return;
233
+ if (record.retries >= record.maxRetries) {
234
+ // Permanent failure
235
+ this.stmtMarkFailed.run(Date.now(), error, fileId);
236
+ }
237
+ else {
238
+ // Reset to pending for retry
239
+ this.stmtUpdateStatus.run('pending', fileId);
240
+ }
241
+ }
242
+ /**
243
+ * Mark a file as archived (moved to archive location)
244
+ */
245
+ markArchived(fileId, archivePath) {
246
+ this.stmtMarkArchived.run(archivePath, Date.now(), fileId);
247
+ }
248
+ /**
249
+ * Get pending files ready for processing
250
+ */
251
+ getPendingFiles(limit = 100) {
252
+ const rows = this.stmtGetPending.all(limit);
253
+ return rows.map(row => this.rowToRecord(row));
254
+ }
255
+ /**
256
+ * Get a file record by ID
257
+ */
258
+ getById(fileId) {
259
+ const row = this.stmtGetById.get(fileId);
260
+ return row ? this.rowToRecord(row) : null;
261
+ }
262
+ /**
263
+ * Get a file record by source path
264
+ */
265
+ getByPath(sourcePath) {
266
+ const row = this.stmtGetByPath.get(sourcePath);
267
+ return row ? this.rowToRecord(row) : null;
268
+ }
269
+ /**
270
+ * Reset all 'processing' files to 'pending' (crash recovery)
271
+ */
272
+ resetProcessingFiles() {
273
+ const result = this.stmtResetProcessing.run();
274
+ return result.changes;
275
+ }
276
+ /**
277
+ * Mark files as failed if their source file no longer exists
278
+ */
279
+ reconcileWithFilesystem() {
280
+ let reset = 0;
281
+ let failed = 0;
282
+ // Get all processing files
283
+ const processingFiles = this.db
284
+ .prepare(`SELECT * FROM relay_files WHERE status = 'processing'`)
285
+ .all();
286
+ for (const row of processingFiles) {
287
+ if (!fs.existsSync(row.source_path)) {
288
+ // File was deleted while processing - mark as failed
289
+ this.stmtMarkFailed.run(Date.now(), 'Source file deleted during processing', row.file_id);
290
+ failed++;
291
+ }
292
+ else {
293
+ // Reset to pending
294
+ this.stmtUpdateStatus.run('pending', row.file_id);
295
+ reset++;
296
+ }
297
+ }
298
+ return { reset, failed };
299
+ }
300
+ /**
301
+ * Clean up old archived records
302
+ */
303
+ cleanupArchivedRecords() {
304
+ const cutoff = Date.now() - this.config.archiveRetentionMs;
305
+ const result = this.stmtCleanupArchived.run(cutoff);
306
+ return result.changes;
307
+ }
308
+ /**
309
+ * Get statistics about file processing
310
+ */
311
+ getStats() {
312
+ const rows = this.stmtGetStats.all();
313
+ const stats = {
314
+ pending: 0,
315
+ processing: 0,
316
+ delivered: 0,
317
+ failed: 0,
318
+ archived: 0,
319
+ };
320
+ for (const row of rows) {
321
+ stats[row.status] = row.count;
322
+ }
323
+ return stats;
324
+ }
325
+ /**
326
+ * Convert database row to RelayFileRecord
327
+ */
328
+ rowToRecord(row) {
329
+ return {
330
+ id: row.id,
331
+ fileId: row.file_id,
332
+ sourcePath: row.source_path,
333
+ symlinkPath: row.symlink_path,
334
+ archivePath: row.archive_path,
335
+ agentName: row.agent_name,
336
+ messageType: row.message_type,
337
+ status: row.status,
338
+ retries: row.retries,
339
+ maxRetries: row.max_retries,
340
+ discoveredAt: row.discovered_at,
341
+ processedAt: row.processed_at,
342
+ archivedAt: row.archived_at,
343
+ error: row.error,
344
+ contentHash: row.content_hash,
345
+ fileSize: row.file_size,
346
+ fileMtimeNs: row.file_mtime_ns,
347
+ fileInode: row.file_inode,
348
+ };
349
+ }
350
+ // ==========================================================================
351
+ // Agent Management
352
+ // ==========================================================================
353
+ /**
354
+ * Register or update an agent
355
+ */
356
+ registerAgent(agentName, metadata) {
357
+ const now = Date.now();
358
+ const metadataJson = metadata ? JSON.stringify(metadata) : null;
359
+ this.db.prepare(`
360
+ INSERT INTO agents (agent_name, created_at, last_seen_at, status, metadata)
361
+ VALUES (?, ?, ?, 'active', ?)
362
+ ON CONFLICT(agent_name) DO UPDATE SET
363
+ last_seen_at = excluded.last_seen_at,
364
+ status = 'active',
365
+ metadata = COALESCE(excluded.metadata, agents.metadata)
366
+ `).run(agentName, now, now, metadataJson);
367
+ }
368
+ /**
369
+ * Update agent last seen time
370
+ */
371
+ updateAgentLastSeen(agentName) {
372
+ this.db.prepare(`
373
+ UPDATE agents SET last_seen_at = ? WHERE agent_name = ?
374
+ `).run(Date.now(), agentName);
375
+ }
376
+ /**
377
+ * Mark agent as inactive
378
+ */
379
+ markAgentInactive(agentName) {
380
+ this.db.prepare(`
381
+ UPDATE agents SET status = 'inactive' WHERE agent_name = ?
382
+ `).run(agentName);
383
+ }
384
+ /**
385
+ * Get all active agents
386
+ */
387
+ getActiveAgents() {
388
+ const rows = this.db.prepare(`
389
+ SELECT * FROM agents WHERE status = 'active' ORDER BY last_seen_at DESC
390
+ `).all();
391
+ return rows.map(row => ({
392
+ id: row.id,
393
+ agentName: row.agent_name,
394
+ createdAt: row.created_at,
395
+ lastSeenAt: row.last_seen_at,
396
+ status: row.status,
397
+ metadata: row.metadata ? JSON.parse(row.metadata) : null,
398
+ }));
399
+ }
400
+ /**
401
+ * Get agent by name
402
+ */
403
+ getAgent(agentName) {
404
+ const row = this.db.prepare(`
405
+ SELECT * FROM agents WHERE agent_name = ?
406
+ `).get(agentName);
407
+ if (!row)
408
+ return null;
409
+ return {
410
+ id: row.id,
411
+ agentName: row.agent_name,
412
+ createdAt: row.created_at,
413
+ lastSeenAt: row.last_seen_at,
414
+ status: row.status,
415
+ metadata: row.metadata ? JSON.parse(row.metadata) : null,
416
+ };
417
+ }
418
+ // ==========================================================================
419
+ // Orchestrator State
420
+ // ==========================================================================
421
+ /**
422
+ * Save orchestrator state
423
+ */
424
+ saveState(key, value) {
425
+ this.db.prepare(`
426
+ INSERT INTO orchestrator_state (key, value, updated_at)
427
+ VALUES (?, ?, ?)
428
+ ON CONFLICT(key) DO UPDATE SET
429
+ value = excluded.value,
430
+ updated_at = excluded.updated_at
431
+ `).run(key, value, Date.now());
432
+ }
433
+ /**
434
+ * Get orchestrator state
435
+ */
436
+ getState(key) {
437
+ const row = this.db.prepare(`
438
+ SELECT value FROM orchestrator_state WHERE key = ?
439
+ `).get(key);
440
+ return row?.value ?? null;
441
+ }
442
+ /**
443
+ * Delete orchestrator state
444
+ */
445
+ deleteState(key) {
446
+ this.db.prepare(`
447
+ DELETE FROM orchestrator_state WHERE key = ?
448
+ `).run(key);
449
+ }
450
+ /**
451
+ * Get all orchestrator state
452
+ */
453
+ getAllState() {
454
+ const rows = this.db.prepare(`
455
+ SELECT * FROM orchestrator_state ORDER BY key
456
+ `).all();
457
+ return rows.map(row => ({
458
+ key: row.key,
459
+ value: row.value,
460
+ updatedAt: row.updated_at,
461
+ }));
462
+ }
463
+ // ==========================================================================
464
+ // Pending Operations (for crash recovery)
465
+ // ==========================================================================
466
+ /**
467
+ * Add a pending operation
468
+ */
469
+ addPendingOperation(operationType, targetId, payload) {
470
+ const result = this.db.prepare(`
471
+ INSERT INTO pending_operations (operation_type, target_id, payload, created_at)
472
+ VALUES (?, ?, ?, ?)
473
+ `).run(operationType, targetId, payload ?? null, Date.now());
474
+ return result.lastInsertRowid;
475
+ }
476
+ /**
477
+ * Complete a pending operation (remove it)
478
+ */
479
+ completePendingOperation(id) {
480
+ this.db.prepare(`
481
+ DELETE FROM pending_operations WHERE id = ?
482
+ `).run(id);
483
+ }
484
+ /**
485
+ * Fail a pending operation (increment attempts, record error)
486
+ */
487
+ failPendingOperation(id, error) {
488
+ this.db.prepare(`
489
+ UPDATE pending_operations
490
+ SET attempts = attempts + 1, last_attempt_at = ?, error = ?
491
+ WHERE id = ?
492
+ `).run(Date.now(), error, id);
493
+ }
494
+ /**
495
+ * Get pending operations for recovery
496
+ */
497
+ getPendingOperations(maxAttempts = 5) {
498
+ const rows = this.db.prepare(`
499
+ SELECT * FROM pending_operations
500
+ WHERE attempts < ?
501
+ ORDER BY created_at ASC
502
+ `).all(maxAttempts);
503
+ return rows.map(row => ({
504
+ id: row.id,
505
+ operationType: row.operation_type,
506
+ targetId: row.target_id,
507
+ payload: row.payload,
508
+ createdAt: row.created_at,
509
+ attempts: row.attempts,
510
+ lastAttemptAt: row.last_attempt_at,
511
+ error: row.error,
512
+ }));
513
+ }
514
+ /**
515
+ * Cleanup old failed operations
516
+ */
517
+ cleanupFailedOperations(maxAge = 24 * 60 * 60 * 1000) {
518
+ const cutoff = Date.now() - maxAge;
519
+ const result = this.db.prepare(`
520
+ DELETE FROM pending_operations
521
+ WHERE created_at < ? AND attempts >= 5
522
+ `).run(cutoff);
523
+ return result.changes;
524
+ }
525
+ /**
526
+ * Close the database connection
527
+ */
528
+ close() {
529
+ this.db.close();
530
+ }
531
+ }
532
+ //# sourceMappingURL=relay-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-ledger.js","sourceRoot":"","sources":["../src/relay-ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Relay Watchdog - File-based relay message detection and processing
3
+ *
4
+ * Monitors agent outbox directories for new relay files and processes them:
5
+ * 1. Detects new files via fs.watch + periodic reconciliation
6
+ * 2. Validates files (size > 0, not symlink, settled)
7
+ * 3. Claims files atomically via ledger
8
+ * 4. Processes and archives files
9
+ *
10
+ * Features:
11
+ * - fsevents/inotify watchers with overflow fallback
12
+ * - Configurable settle time for file stability
13
+ * - Symlink rejection (security)
14
+ * - Orphaned .pending file cleanup
15
+ * - Crash recovery on startup
16
+ */
17
+ import { EventEmitter } from 'node:events';
18
+ import { type RelayFileRecord } from './relay-ledger.js';
19
+ import { type RelayPaths } from '@agent-relay/config/relay-file-writer';
20
+ export interface WatchdogConfig {
21
+ /** Base relay paths (auto-detected if not provided) */
22
+ relayPaths?: RelayPaths;
23
+ /** Ledger database path (default: ~/.agent-relay/meta/ledger.sqlite) */
24
+ ledgerPath?: string;
25
+ /** Settle time before processing file (default: 500ms) */
26
+ settleTimeMs?: number;
27
+ /** Timeout for malformed/incomplete files (default: 10000ms) */
28
+ malformedTimeoutMs?: number;
29
+ /** Interval for periodic reconciliation (default: 30000ms) */
30
+ reconcileIntervalMs?: number;
31
+ /** Maximum message size in bytes (default: 1MB) */
32
+ maxMessageSizeBytes?: number;
33
+ /** Maximum attachment size in bytes (default: 10MB) */
34
+ maxAttachmentSizeBytes?: number;
35
+ /** Cleanup interval for orphaned files (default: 60000ms) */
36
+ cleanupIntervalMs?: number;
37
+ /** Age threshold for orphaned .pending files (default: 30000ms) */
38
+ orphanedPendingAgeMs?: number;
39
+ /** Archive retention in milliseconds (default: 7 days) */
40
+ archiveRetentionMs?: number;
41
+ /** Enable debug logging */
42
+ debug?: boolean;
43
+ }
44
+ export interface DiscoveredFile {
45
+ path: string;
46
+ agentName: string;
47
+ messageType: string;
48
+ size: number;
49
+ mtime: number;
50
+ contentHash?: string;
51
+ }
52
+ export interface ProcessedFile {
53
+ fileId: string;
54
+ agentName: string;
55
+ messageType: string;
56
+ content: string;
57
+ headers: Record<string, string>;
58
+ body: string;
59
+ }
60
+ export interface WatchdogEvents {
61
+ 'file:discovered': (file: DiscoveredFile) => void;
62
+ 'file:processing': (record: RelayFileRecord) => void;
63
+ 'file:delivered': (result: ProcessedFile) => void;
64
+ 'file:failed': (record: RelayFileRecord, error: Error) => void;
65
+ 'file:archived': (record: RelayFileRecord, archivePath: string) => void;
66
+ 'watcher:overflow': (dir: string) => void;
67
+ 'reconcile:complete': (stats: {
68
+ discovered: number;
69
+ failed: number;
70
+ }) => void;
71
+ error: (error: Error) => void;
72
+ }
73
+ export declare class RelayWatchdog extends EventEmitter {
74
+ private config;
75
+ private relayPaths;
76
+ private ledger;
77
+ private watchers;
78
+ private pendingFiles;
79
+ private reconcileTimer?;
80
+ private cleanupTimer?;
81
+ private running;
82
+ constructor(config?: WatchdogConfig);
83
+ /**
84
+ * Resolve symlinks in relay paths for cloud/production workspace support
85
+ * This ensures we work with canonical paths even when directories are symlinked
86
+ */
87
+ private resolveRelayPaths;
88
+ /**
89
+ * Start the watchdog
90
+ */
91
+ start(): Promise<void>;
92
+ /**
93
+ * Stop the watchdog
94
+ */
95
+ stop(): Promise<void>;
96
+ /**
97
+ * Get ledger statistics
98
+ */
99
+ getStats(): Record<string, number>;
100
+ /**
101
+ * Get pending file count
102
+ */
103
+ getPendingCount(): number;
104
+ private ensureDirectories;
105
+ private startRootWatcher;
106
+ private startAgentWatchers;
107
+ private checkAndWatchAgentDir;
108
+ private startAgentWatcher;
109
+ private handleFileEvent;
110
+ private processDiscoveredFile;
111
+ private validateFile;
112
+ private calculateFileHash;
113
+ private processFile;
114
+ private parseFileContent;
115
+ private archiveFile;
116
+ private fullReconciliation;
117
+ private cleanupOrphanedFiles;
118
+ private shouldIgnore;
119
+ private log;
120
+ }
121
+ export interface RelayWatchdog {
122
+ on<K extends keyof WatchdogEvents>(event: K, listener: WatchdogEvents[K]): this;
123
+ emit<K extends keyof WatchdogEvents>(event: K, ...args: Parameters<WatchdogEvents[K]>): boolean;
124
+ }
125
+ //# sourceMappingURL=relay-watchdog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-watchdog.d.ts","sourceRoot":"","sources":["../src/relay-watchdog.ts"],"names":[],"mappings":"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"}