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.
- package/dist/index.cjs +31903 -33913
- package/dist/src/cli/index.js +36 -51
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +18 -19
- package/packages/api-types/package.json +1 -1
- package/packages/benchmark/package.json +4 -4
- package/packages/bridge/package.json +8 -8
- package/packages/cli-tester/package.json +1 -1
- package/packages/config/dist/project-namespace.d.ts +28 -0
- package/packages/config/dist/project-namespace.d.ts.map +1 -1
- package/packages/config/dist/project-namespace.js +42 -0
- package/packages/config/dist/project-namespace.js.map +1 -1
- package/packages/config/package.json +2 -2
- package/packages/config/src/project-namespace.ts +65 -0
- package/packages/continuity/dist/formatter.d.ts +8 -2
- package/packages/continuity/dist/formatter.d.ts.map +1 -1
- package/packages/continuity/dist/formatter.js +142 -7
- package/packages/continuity/dist/formatter.js.map +1 -1
- package/packages/continuity/dist/index.d.ts +1 -0
- package/packages/continuity/dist/index.d.ts.map +1 -1
- package/packages/continuity/dist/index.js +2 -0
- package/packages/continuity/dist/index.js.map +1 -1
- package/packages/continuity/package.json +4 -1
- package/packages/continuity/src/formatter.ts +175 -10
- package/packages/continuity/src/index.ts +3 -0
- package/packages/daemon/dist/enhanced-features.d.ts +2 -3
- package/packages/daemon/dist/enhanced-features.d.ts.map +1 -1
- package/packages/daemon/dist/enhanced-features.js +1 -0
- package/packages/daemon/dist/enhanced-features.js.map +1 -1
- package/packages/daemon/dist/index.d.ts +0 -2
- package/packages/daemon/dist/index.d.ts.map +1 -1
- package/packages/daemon/dist/index.js +0 -3
- package/packages/daemon/dist/index.js.map +1 -1
- package/packages/daemon/dist/server.d.ts +0 -6
- package/packages/daemon/dist/server.d.ts.map +1 -1
- package/packages/daemon/dist/server.js +20 -119
- package/packages/daemon/dist/server.js.map +1 -1
- package/packages/daemon/package.json +12 -14
- package/packages/daemon/src/enhanced-features.ts +4 -4
- package/packages/daemon/src/index.ts +0 -4
- package/packages/daemon/src/server.ts +19 -127
- package/packages/daemon/vitest.config.ts +9 -0
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +3 -3
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/dist/adapter.d.ts +5 -5
- package/packages/storage/dist/adapter.js +9 -9
- package/packages/storage/dist/adapter.js.map +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/storage/src/adapter.ts +9 -9
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/packages/wrapper/package.json +6 -6
- package/scripts/build-cjs.mjs +2 -0
- package/packages/daemon/dist/migrations/index.d.ts +0 -73
- package/packages/daemon/dist/migrations/index.d.ts.map +0 -1
- package/packages/daemon/dist/migrations/index.js +0 -241
- package/packages/daemon/dist/migrations/index.js.map +0 -1
- package/packages/daemon/dist/relay-ledger.d.ts +0 -263
- package/packages/daemon/dist/relay-ledger.d.ts.map +0 -1
- package/packages/daemon/dist/relay-ledger.js +0 -538
- package/packages/daemon/dist/relay-ledger.js.map +0 -1
- package/packages/daemon/dist/relay-watchdog.d.ts +0 -125
- package/packages/daemon/dist/relay-watchdog.d.ts.map +0 -1
- package/packages/daemon/dist/relay-watchdog.js +0 -611
- package/packages/daemon/dist/relay-watchdog.js.map +0 -1
- package/packages/daemon/src/migrations/0001_initial.sql +0 -72
- package/packages/daemon/src/migrations/index.test.ts +0 -195
- package/packages/daemon/src/migrations/index.ts +0 -286
- package/packages/daemon/src/relay-ledger.test.ts +0 -358
- package/packages/daemon/src/relay-ledger.ts +0 -713
- package/packages/daemon/src/relay-watchdog.test.ts +0 -881
- 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
|
-
});
|