agent-relay 2.0.37 → 2.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 (81) hide show
  1. package/dist/index.cjs +31903 -33913
  2. package/dist/src/cli/index.js +36 -51
  3. package/dist/src/cli/index.js.map +1 -1
  4. package/package.json +18 -19
  5. package/packages/api-types/package.json +1 -1
  6. package/packages/benchmark/package.json +4 -4
  7. package/packages/bridge/package.json +8 -8
  8. package/packages/cli-tester/package.json +1 -1
  9. package/packages/config/dist/project-namespace.d.ts +28 -0
  10. package/packages/config/dist/project-namespace.d.ts.map +1 -1
  11. package/packages/config/dist/project-namespace.js +42 -0
  12. package/packages/config/dist/project-namespace.js.map +1 -1
  13. package/packages/config/package.json +2 -2
  14. package/packages/config/src/project-namespace.ts +65 -0
  15. package/packages/continuity/dist/formatter.d.ts +8 -2
  16. package/packages/continuity/dist/formatter.d.ts.map +1 -1
  17. package/packages/continuity/dist/formatter.js +142 -7
  18. package/packages/continuity/dist/formatter.js.map +1 -1
  19. package/packages/continuity/dist/index.d.ts +1 -0
  20. package/packages/continuity/dist/index.d.ts.map +1 -1
  21. package/packages/continuity/dist/index.js +2 -0
  22. package/packages/continuity/dist/index.js.map +1 -1
  23. package/packages/continuity/package.json +4 -1
  24. package/packages/continuity/src/formatter.ts +175 -10
  25. package/packages/continuity/src/index.ts +3 -0
  26. package/packages/daemon/dist/enhanced-features.d.ts +2 -3
  27. package/packages/daemon/dist/enhanced-features.d.ts.map +1 -1
  28. package/packages/daemon/dist/enhanced-features.js +1 -0
  29. package/packages/daemon/dist/enhanced-features.js.map +1 -1
  30. package/packages/daemon/dist/index.d.ts +0 -2
  31. package/packages/daemon/dist/index.d.ts.map +1 -1
  32. package/packages/daemon/dist/index.js +0 -3
  33. package/packages/daemon/dist/index.js.map +1 -1
  34. package/packages/daemon/dist/server.d.ts +0 -6
  35. package/packages/daemon/dist/server.d.ts.map +1 -1
  36. package/packages/daemon/dist/server.js +20 -119
  37. package/packages/daemon/dist/server.js.map +1 -1
  38. package/packages/daemon/package.json +12 -14
  39. package/packages/daemon/src/enhanced-features.ts +4 -4
  40. package/packages/daemon/src/index.ts +0 -4
  41. package/packages/daemon/src/server.ts +19 -127
  42. package/packages/daemon/vitest.config.ts +9 -0
  43. package/packages/hooks/package.json +4 -4
  44. package/packages/mcp/package.json +3 -3
  45. package/packages/memory/package.json +2 -2
  46. package/packages/policy/package.json +2 -2
  47. package/packages/protocol/package.json +1 -1
  48. package/packages/resiliency/package.json +1 -1
  49. package/packages/sdk/package.json +2 -2
  50. package/packages/spawner/package.json +1 -1
  51. package/packages/state/package.json +1 -1
  52. package/packages/storage/dist/adapter.d.ts +5 -5
  53. package/packages/storage/dist/adapter.js +9 -9
  54. package/packages/storage/dist/adapter.js.map +1 -1
  55. package/packages/storage/package.json +2 -2
  56. package/packages/storage/src/adapter.ts +9 -9
  57. package/packages/telemetry/package.json +1 -1
  58. package/packages/trajectory/package.json +2 -2
  59. package/packages/user-directory/package.json +2 -2
  60. package/packages/utils/package.json +2 -2
  61. package/packages/wrapper/package.json +6 -6
  62. package/scripts/build-cjs.mjs +2 -0
  63. package/packages/daemon/dist/migrations/index.d.ts +0 -73
  64. package/packages/daemon/dist/migrations/index.d.ts.map +0 -1
  65. package/packages/daemon/dist/migrations/index.js +0 -241
  66. package/packages/daemon/dist/migrations/index.js.map +0 -1
  67. package/packages/daemon/dist/relay-ledger.d.ts +0 -263
  68. package/packages/daemon/dist/relay-ledger.d.ts.map +0 -1
  69. package/packages/daemon/dist/relay-ledger.js +0 -538
  70. package/packages/daemon/dist/relay-ledger.js.map +0 -1
  71. package/packages/daemon/dist/relay-watchdog.d.ts +0 -125
  72. package/packages/daemon/dist/relay-watchdog.d.ts.map +0 -1
  73. package/packages/daemon/dist/relay-watchdog.js +0 -611
  74. package/packages/daemon/dist/relay-watchdog.js.map +0 -1
  75. package/packages/daemon/src/migrations/0001_initial.sql +0 -72
  76. package/packages/daemon/src/migrations/index.test.ts +0 -195
  77. package/packages/daemon/src/migrations/index.ts +0 -286
  78. package/packages/daemon/src/relay-ledger.test.ts +0 -358
  79. package/packages/daemon/src/relay-ledger.ts +0 -713
  80. package/packages/daemon/src/relay-watchdog.test.ts +0 -881
  81. package/packages/daemon/src/relay-watchdog.ts +0 -785
@@ -1,358 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import os from 'node:os';
5
- import { RelayLedger, type RelayFileRecord } from './relay-ledger.js';
6
-
7
- describe('RelayLedger', () => {
8
- let testDir: string;
9
- let ledger: RelayLedger;
10
-
11
- beforeEach(async () => {
12
- // Create temp test directory
13
- testDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'relay-ledger-test-'));
14
- const dbPath = path.join(testDir, 'test-ledger.sqlite');
15
-
16
- ledger = new RelayLedger({
17
- dbPath,
18
- maxRetries: 3,
19
- archiveRetentionMs: 7 * 24 * 60 * 60 * 1000,
20
- });
21
- });
22
-
23
- afterEach(async () => {
24
- ledger.close();
25
- try {
26
- await fs.promises.rm(testDir, { recursive: true, force: true });
27
- } catch {
28
- // Ignore cleanup errors
29
- }
30
- });
31
-
32
- describe('generateFileId', () => {
33
- it('should generate 12-character hex string', () => {
34
- const id = ledger.generateFileId();
35
- expect(id).toMatch(/^[a-f0-9]{12}$/);
36
- });
37
-
38
- it('should generate unique IDs', () => {
39
- const ids = new Set<string>();
40
- for (let i = 0; i < 1000; i++) {
41
- ids.add(ledger.generateFileId());
42
- }
43
- expect(ids.size).toBe(1000);
44
- });
45
- });
46
-
47
- describe('isReservedAgentName', () => {
48
- it('should return true for reserved names', () => {
49
- expect(ledger.isReservedAgentName('Lead')).toBe(true);
50
- expect(ledger.isReservedAgentName('System')).toBe(true);
51
- expect(ledger.isReservedAgentName('Broadcast')).toBe(true);
52
- expect(ledger.isReservedAgentName('*')).toBe(true);
53
- });
54
-
55
- it('should return false for non-reserved names', () => {
56
- expect(ledger.isReservedAgentName('Worker1')).toBe(false);
57
- expect(ledger.isReservedAgentName('MyAgent')).toBe(false);
58
- });
59
- });
60
-
61
- describe('registerFile', () => {
62
- it('should register a new file', () => {
63
- const fileId = ledger.registerFile(
64
- '/tmp/test/outbox/Agent1/msg',
65
- 'Agent1',
66
- 'msg',
67
- 256
68
- );
69
-
70
- expect(fileId).toMatch(/^[a-f0-9]{12}$/);
71
-
72
- const record = ledger.getById(fileId);
73
- expect(record).not.toBeNull();
74
- expect(record!.sourcePath).toBe('/tmp/test/outbox/Agent1/msg');
75
- expect(record!.agentName).toBe('Agent1');
76
- expect(record!.messageType).toBe('msg');
77
- expect(record!.status).toBe('pending');
78
- expect(record!.fileSize).toBe(256);
79
- });
80
-
81
- it('should not duplicate files with same ID', () => {
82
- const fileId1 = ledger.registerFile('/path1', 'Agent1', 'msg', 100);
83
- // Try to register same path again - should be ignored (ON CONFLICT DO NOTHING)
84
- const stats = ledger.getStats();
85
- expect(stats.pending).toBe(1);
86
- });
87
-
88
- it('should store content hash', () => {
89
- const fileId = ledger.registerFile('/path', 'Agent1', 'msg', 100, 'abc123hash');
90
- const record = ledger.getById(fileId);
91
- expect(record!.contentHash).toBe('abc123hash');
92
- });
93
- });
94
-
95
- describe('isFileRegistered', () => {
96
- it('should return true for registered files', () => {
97
- ledger.registerFile('/test/path', 'Agent1', 'msg', 100);
98
- expect(ledger.isFileRegistered('/test/path')).toBe(true);
99
- });
100
-
101
- it('should return false for unregistered files', () => {
102
- expect(ledger.isFileRegistered('/unknown/path')).toBe(false);
103
- });
104
- });
105
-
106
- describe('claimFile', () => {
107
- it('should successfully claim a pending file', () => {
108
- const fileId = ledger.registerFile('/path', 'Agent1', 'msg', 100);
109
-
110
- const result = ledger.claimFile(fileId);
111
-
112
- expect(result.success).toBe(true);
113
- expect(result.record).toBeDefined();
114
- expect(result.record!.status).toBe('processing');
115
- expect(result.record!.retries).toBe(1);
116
- });
117
-
118
- it('should fail to claim non-existent file', () => {
119
- const result = ledger.claimFile('nonexistent');
120
-
121
- expect(result.success).toBe(false);
122
- expect(result.reason).toBe('File not found');
123
- });
124
-
125
- it('should fail to claim already processing file', () => {
126
- const fileId = ledger.registerFile('/path', 'Agent1', 'msg', 100);
127
- ledger.claimFile(fileId); // First claim succeeds
128
-
129
- const result = ledger.claimFile(fileId); // Second claim fails
130
-
131
- expect(result.success).toBe(false);
132
- expect(result.reason).toContain('processing');
133
- });
134
-
135
- it('should fail to claim file at max retries', () => {
136
- const fileId = ledger.registerFile('/path', 'Agent1', 'msg', 100);
137
-
138
- // Claim and fail 3 times (max retries)
139
- for (let i = 0; i < 3; i++) {
140
- const claim = ledger.claimFile(fileId);
141
- if (claim.success) {
142
- ledger.markFailed(fileId, 'test error');
143
- }
144
- }
145
-
146
- // Next claim should fail due to max retries
147
- const result = ledger.claimFile(fileId);
148
- expect(result.success).toBe(false);
149
- });
150
- });
151
-
152
- describe('markDelivered', () => {
153
- it('should mark file as delivered', () => {
154
- const fileId = ledger.registerFile('/path', 'Agent1', 'msg', 100);
155
- ledger.claimFile(fileId);
156
-
157
- ledger.markDelivered(fileId);
158
-
159
- const record = ledger.getById(fileId);
160
- expect(record!.status).toBe('delivered');
161
- expect(record!.processedAt).not.toBeNull();
162
- });
163
- });
164
-
165
- describe('markFailed', () => {
166
- it('should reset to pending if under max retries', () => {
167
- const fileId = ledger.registerFile('/path', 'Agent1', 'msg', 100);
168
- ledger.claimFile(fileId);
169
-
170
- ledger.markFailed(fileId, 'test error');
171
-
172
- const record = ledger.getById(fileId);
173
- expect(record!.status).toBe('pending');
174
- expect(record!.retries).toBe(1);
175
- });
176
-
177
- it('should mark as failed at max retries', () => {
178
- const fileId = ledger.registerFile('/path', 'Agent1', 'msg', 100);
179
-
180
- // Exhaust all retries
181
- for (let i = 0; i < 3; i++) {
182
- ledger.claimFile(fileId);
183
- ledger.markFailed(fileId, 'test error');
184
- }
185
-
186
- // Claim again to reach max
187
- ledger.claimFile(fileId);
188
- ledger.markFailed(fileId, 'final error');
189
-
190
- const record = ledger.getById(fileId);
191
- expect(record!.status).toBe('failed');
192
- expect(record!.error).toBe('final error');
193
- });
194
- });
195
-
196
- describe('markArchived', () => {
197
- it('should mark file as archived with path', () => {
198
- const fileId = ledger.registerFile('/original/path', 'Agent1', 'msg', 100);
199
- ledger.claimFile(fileId);
200
- ledger.markDelivered(fileId);
201
-
202
- ledger.markArchived(fileId, '/archive/path');
203
-
204
- const record = ledger.getById(fileId);
205
- expect(record!.status).toBe('archived');
206
- expect(record!.archivePath).toBe('/archive/path');
207
- expect(record!.archivedAt).not.toBeNull();
208
- });
209
- });
210
-
211
- describe('getPendingFiles', () => {
212
- it('should return pending files in order', () => {
213
- ledger.registerFile('/path1', 'Agent1', 'msg1', 100);
214
- ledger.registerFile('/path2', 'Agent2', 'msg2', 200);
215
- ledger.registerFile('/path3', 'Agent1', 'msg3', 300);
216
-
217
- const pending = ledger.getPendingFiles();
218
-
219
- expect(pending).toHaveLength(3);
220
- // Ordered by id (insertion order)
221
- expect(pending[0].sourcePath).toBe('/path1');
222
- expect(pending[1].sourcePath).toBe('/path2');
223
- expect(pending[2].sourcePath).toBe('/path3');
224
- });
225
-
226
- it('should respect limit', () => {
227
- for (let i = 0; i < 10; i++) {
228
- ledger.registerFile(`/path${i}`, 'Agent', `msg${i}`, 100);
229
- }
230
-
231
- const pending = ledger.getPendingFiles(5);
232
-
233
- expect(pending).toHaveLength(5);
234
- });
235
-
236
- it('should not return processing or delivered files', () => {
237
- const id1 = ledger.registerFile('/path1', 'Agent', 'msg1', 100);
238
- const id2 = ledger.registerFile('/path2', 'Agent', 'msg2', 100);
239
- ledger.registerFile('/path3', 'Agent', 'msg3', 100);
240
-
241
- ledger.claimFile(id1); // Now processing
242
- ledger.claimFile(id2);
243
- ledger.markDelivered(id2); // Now delivered
244
-
245
- const pending = ledger.getPendingFiles();
246
-
247
- expect(pending).toHaveLength(1);
248
- expect(pending[0].sourcePath).toBe('/path3');
249
- });
250
- });
251
-
252
- describe('resetProcessingFiles', () => {
253
- it('should reset all processing files to pending', () => {
254
- const id1 = ledger.registerFile('/path1', 'Agent', 'msg1', 100);
255
- const id2 = ledger.registerFile('/path2', 'Agent', 'msg2', 100);
256
- ledger.registerFile('/path3', 'Agent', 'msg3', 100);
257
-
258
- ledger.claimFile(id1);
259
- ledger.claimFile(id2);
260
-
261
- const resetCount = ledger.resetProcessingFiles();
262
-
263
- expect(resetCount).toBe(2);
264
-
265
- const pending = ledger.getPendingFiles();
266
- expect(pending).toHaveLength(3);
267
- });
268
- });
269
-
270
- describe('reconcileWithFilesystem', () => {
271
- it('should mark missing files as failed', async () => {
272
- // Create a real file
273
- const realFile = path.join(testDir, 'real-file');
274
- await fs.promises.writeFile(realFile, 'content');
275
-
276
- const id1 = ledger.registerFile(realFile, 'Agent', 'msg1', 7);
277
- const id2 = ledger.registerFile('/nonexistent/path', 'Agent', 'msg2', 100);
278
-
279
- ledger.claimFile(id1);
280
- ledger.claimFile(id2);
281
-
282
- const result = ledger.reconcileWithFilesystem();
283
-
284
- expect(result.reset).toBe(1); // real file reset to pending
285
- expect(result.failed).toBe(1); // missing file marked failed
286
-
287
- const record1 = ledger.getById(id1);
288
- const record2 = ledger.getById(id2);
289
- expect(record1!.status).toBe('pending');
290
- expect(record2!.status).toBe('failed');
291
- });
292
- });
293
-
294
- describe('cleanupArchivedRecords', () => {
295
- it('should delete old archived records', async () => {
296
- const fileId = ledger.registerFile('/path', 'Agent', 'msg', 100);
297
- ledger.claimFile(fileId);
298
- ledger.markDelivered(fileId);
299
- ledger.markArchived(fileId, '/archive/path');
300
-
301
- // Using a ledger with 1ms retention for test
302
- const shortRetentionLedger = new RelayLedger({
303
- dbPath: path.join(testDir, 'short-retention.sqlite'),
304
- archiveRetentionMs: 1, // 1ms retention
305
- });
306
-
307
- const id = shortRetentionLedger.registerFile('/path2', 'Agent', 'msg', 100);
308
- shortRetentionLedger.claimFile(id);
309
- shortRetentionLedger.markDelivered(id);
310
- shortRetentionLedger.markArchived(id, '/archive');
311
-
312
- // Wait a bit to ensure archived_at is in the past relative to retention
313
- await new Promise(resolve => setTimeout(resolve, 10));
314
-
315
- const deleted = shortRetentionLedger.cleanupArchivedRecords();
316
- expect(deleted).toBe(1);
317
-
318
- shortRetentionLedger.close();
319
- });
320
- });
321
-
322
- describe('getStats', () => {
323
- it('should return correct counts per status', () => {
324
- ledger.registerFile('/path1', 'Agent', 'msg1', 100);
325
- ledger.registerFile('/path2', 'Agent', 'msg2', 100);
326
- const id3 = ledger.registerFile('/path3', 'Agent', 'msg3', 100);
327
- const id4 = ledger.registerFile('/path4', 'Agent', 'msg4', 100);
328
-
329
- ledger.claimFile(id3);
330
- ledger.claimFile(id4);
331
- ledger.markDelivered(id4);
332
-
333
- const stats = ledger.getStats();
334
-
335
- expect(stats.pending).toBe(2);
336
- expect(stats.processing).toBe(1);
337
- expect(stats.delivered).toBe(1);
338
- expect(stats.failed).toBe(0);
339
- expect(stats.archived).toBe(0);
340
- });
341
- });
342
-
343
- describe('getByPath', () => {
344
- it('should return record by source path', () => {
345
- ledger.registerFile('/unique/path', 'Agent1', 'msg', 256);
346
-
347
- const record = ledger.getByPath('/unique/path');
348
-
349
- expect(record).not.toBeNull();
350
- expect(record!.agentName).toBe('Agent1');
351
- });
352
-
353
- it('should return null for unknown path', () => {
354
- const record = ledger.getByPath('/unknown');
355
- expect(record).toBeNull();
356
- });
357
- });
358
- });