swarm-mail 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.
- package/README.md +201 -0
- package/package.json +28 -0
- package/src/adapter.ts +306 -0
- package/src/index.ts +57 -0
- package/src/pglite.ts +189 -0
- package/src/streams/agent-mail.test.ts +777 -0
- package/src/streams/agent-mail.ts +535 -0
- package/src/streams/debug.test.ts +500 -0
- package/src/streams/debug.ts +727 -0
- package/src/streams/effect/ask.integration.test.ts +314 -0
- package/src/streams/effect/ask.ts +202 -0
- package/src/streams/effect/cursor.integration.test.ts +418 -0
- package/src/streams/effect/cursor.ts +288 -0
- package/src/streams/effect/deferred.test.ts +357 -0
- package/src/streams/effect/deferred.ts +445 -0
- package/src/streams/effect/index.ts +17 -0
- package/src/streams/effect/layers.ts +73 -0
- package/src/streams/effect/lock.test.ts +385 -0
- package/src/streams/effect/lock.ts +399 -0
- package/src/streams/effect/mailbox.test.ts +260 -0
- package/src/streams/effect/mailbox.ts +318 -0
- package/src/streams/events.test.ts +924 -0
- package/src/streams/events.ts +329 -0
- package/src/streams/index.test.ts +229 -0
- package/src/streams/index.ts +578 -0
- package/src/streams/migrations.test.ts +359 -0
- package/src/streams/migrations.ts +362 -0
- package/src/streams/projections.test.ts +611 -0
- package/src/streams/projections.ts +564 -0
- package/src/streams/store.integration.test.ts +658 -0
- package/src/streams/store.ts +1129 -0
- package/src/streams/swarm-mail.ts +552 -0
- package/src/types/adapter.ts +392 -0
- package/src/types/database.ts +127 -0
- package/src/types/index.ts +26 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug Tools Tests - TDD RED phase
|
|
3
|
+
*
|
|
4
|
+
* These tests define the expected behavior for debug/inspection tools.
|
|
5
|
+
* Run these first to see them fail, then implement to make them pass.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
8
|
+
import { resetDatabase } from "./index";
|
|
9
|
+
import { initAgent, sendAgentMessage, reserveAgentFiles } from "./agent-mail";
|
|
10
|
+
import {
|
|
11
|
+
debugEvents,
|
|
12
|
+
debugAgent,
|
|
13
|
+
debugMessage,
|
|
14
|
+
debugReservations,
|
|
15
|
+
getEventTimeline,
|
|
16
|
+
inspectState,
|
|
17
|
+
} from "./debug";
|
|
18
|
+
|
|
19
|
+
describe("Debug Tools", () => {
|
|
20
|
+
const projectPath = "/test/debug-project";
|
|
21
|
+
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
await resetDatabase(projectPath);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
await resetDatabase(projectPath);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// debugEvents - Show recent events with filtering
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
describe("debugEvents", () => {
|
|
35
|
+
it("returns recent events in reverse chronological order", async () => {
|
|
36
|
+
// Setup: create some events
|
|
37
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
38
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
39
|
+
|
|
40
|
+
const result = await debugEvents({ projectPath });
|
|
41
|
+
|
|
42
|
+
expect(result.events).toHaveLength(2);
|
|
43
|
+
expect(result.events[0].type).toBe("agent_registered");
|
|
44
|
+
// Most recent first
|
|
45
|
+
expect(result.events[0].agent_name).toBe("Agent2");
|
|
46
|
+
expect(result.events[1].agent_name).toBe("Agent1");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("filters by event type", async () => {
|
|
50
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
51
|
+
await sendAgentMessage({
|
|
52
|
+
projectPath,
|
|
53
|
+
fromAgent: "Agent1",
|
|
54
|
+
toAgents: ["Agent2"],
|
|
55
|
+
subject: "Test",
|
|
56
|
+
body: "Hello",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const result = await debugEvents({
|
|
60
|
+
projectPath,
|
|
61
|
+
types: ["message_sent"],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(result.events).toHaveLength(1);
|
|
65
|
+
expect(result.events[0].type).toBe("message_sent");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("filters by agent name", async () => {
|
|
69
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
70
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
71
|
+
await sendAgentMessage({
|
|
72
|
+
projectPath,
|
|
73
|
+
fromAgent: "Agent1",
|
|
74
|
+
toAgents: ["Agent2"],
|
|
75
|
+
subject: "Test",
|
|
76
|
+
body: "Hello",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const result = await debugEvents({
|
|
80
|
+
projectPath,
|
|
81
|
+
agentName: "Agent1",
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Should include Agent1's registration and message
|
|
85
|
+
expect(result.events.length).toBeGreaterThanOrEqual(2);
|
|
86
|
+
expect(
|
|
87
|
+
result.events.every(
|
|
88
|
+
(e) =>
|
|
89
|
+
e.agent_name === "Agent1" ||
|
|
90
|
+
e.from_agent === "Agent1" ||
|
|
91
|
+
e.to_agents?.includes("Agent1"),
|
|
92
|
+
),
|
|
93
|
+
).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("limits results", async () => {
|
|
97
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
98
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
99
|
+
await initAgent({ projectPath, agentName: "Agent3" });
|
|
100
|
+
|
|
101
|
+
const result = await debugEvents({ projectPath, limit: 2 });
|
|
102
|
+
|
|
103
|
+
expect(result.events).toHaveLength(2);
|
|
104
|
+
expect(result.total).toBe(3);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("includes human-readable timestamps", async () => {
|
|
108
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
109
|
+
|
|
110
|
+
const result = await debugEvents({ projectPath });
|
|
111
|
+
|
|
112
|
+
expect(result.events[0]).toHaveProperty("timestamp_human");
|
|
113
|
+
expect(typeof result.events[0].timestamp_human).toBe("string");
|
|
114
|
+
// Should be ISO format or similar
|
|
115
|
+
expect(result.events[0].timestamp_human).toMatch(/\d{4}-\d{2}-\d{2}/);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// debugAgent - Detailed agent state dump
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
describe("debugAgent", () => {
|
|
124
|
+
it("returns agent details with activity summary", async () => {
|
|
125
|
+
await initAgent({
|
|
126
|
+
projectPath,
|
|
127
|
+
agentName: "TestAgent",
|
|
128
|
+
program: "opencode",
|
|
129
|
+
model: "claude-sonnet",
|
|
130
|
+
taskDescription: "Testing debug tools",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const result = await debugAgent({ projectPath, agentName: "TestAgent" });
|
|
134
|
+
|
|
135
|
+
expect(result.agent).not.toBeNull();
|
|
136
|
+
expect(result.agent!.name).toBe("TestAgent");
|
|
137
|
+
expect(result.agent!.program).toBe("opencode");
|
|
138
|
+
expect(result.agent!.model).toBe("claude-sonnet");
|
|
139
|
+
expect(result.agent!.task_description).toBe("Testing debug tools");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("includes message counts", async () => {
|
|
143
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
144
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
145
|
+
|
|
146
|
+
await sendAgentMessage({
|
|
147
|
+
projectPath,
|
|
148
|
+
fromAgent: "Agent1",
|
|
149
|
+
toAgents: ["Agent2"],
|
|
150
|
+
subject: "Test 1",
|
|
151
|
+
body: "Hello",
|
|
152
|
+
});
|
|
153
|
+
await sendAgentMessage({
|
|
154
|
+
projectPath,
|
|
155
|
+
fromAgent: "Agent1",
|
|
156
|
+
toAgents: ["Agent2"],
|
|
157
|
+
subject: "Test 2",
|
|
158
|
+
body: "World",
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const result = await debugAgent({ projectPath, agentName: "Agent1" });
|
|
162
|
+
|
|
163
|
+
expect(result.stats.messagesSent).toBe(2);
|
|
164
|
+
expect(result.stats.messagesReceived).toBe(0);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("includes active reservations", async () => {
|
|
168
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
169
|
+
await reserveAgentFiles({
|
|
170
|
+
projectPath,
|
|
171
|
+
agentName: "Agent1",
|
|
172
|
+
paths: ["src/a.ts", "src/b.ts"],
|
|
173
|
+
reason: "Testing",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const result = await debugAgent({ projectPath, agentName: "Agent1" });
|
|
177
|
+
|
|
178
|
+
expect(result.reservations).toHaveLength(2);
|
|
179
|
+
expect(result.reservations.map((r) => r.path)).toContain("src/a.ts");
|
|
180
|
+
expect(result.reservations.map((r) => r.path)).toContain("src/b.ts");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("includes recent events for the agent", async () => {
|
|
184
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
185
|
+
await sendAgentMessage({
|
|
186
|
+
projectPath,
|
|
187
|
+
fromAgent: "Agent1",
|
|
188
|
+
toAgents: ["Agent2"],
|
|
189
|
+
subject: "Test",
|
|
190
|
+
body: "Hello",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const result = await debugAgent({
|
|
194
|
+
projectPath,
|
|
195
|
+
agentName: "Agent1",
|
|
196
|
+
includeEvents: true,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(result.recentEvents).toBeDefined();
|
|
200
|
+
expect(result.recentEvents!.length).toBeGreaterThan(0);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("returns null for non-existent agent", async () => {
|
|
204
|
+
const result = await debugAgent({
|
|
205
|
+
projectPath,
|
|
206
|
+
agentName: "NonExistent",
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
expect(result.agent).toBeNull();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// debugMessage - Full message audit trail
|
|
215
|
+
// ============================================================================
|
|
216
|
+
|
|
217
|
+
describe("debugMessage", () => {
|
|
218
|
+
it("returns message with full audit trail", async () => {
|
|
219
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
220
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
221
|
+
|
|
222
|
+
const sendResult = await sendAgentMessage({
|
|
223
|
+
projectPath,
|
|
224
|
+
fromAgent: "Agent1",
|
|
225
|
+
toAgents: ["Agent2"],
|
|
226
|
+
subject: "Important",
|
|
227
|
+
body: "Please review",
|
|
228
|
+
importance: "high",
|
|
229
|
+
threadId: "thread-123",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const result = await debugMessage({
|
|
233
|
+
projectPath,
|
|
234
|
+
messageId: sendResult.messageId,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(result.message).not.toBeNull();
|
|
238
|
+
expect(result.message!.from_agent).toBe("Agent1");
|
|
239
|
+
expect(result.message!.subject).toBe("Important");
|
|
240
|
+
expect(result.message!.body).toBe("Please review");
|
|
241
|
+
expect(result.message!.importance).toBe("high");
|
|
242
|
+
expect(result.message!.thread_id).toBe("thread-123");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("includes recipient status", async () => {
|
|
246
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
247
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
248
|
+
await initAgent({ projectPath, agentName: "Agent3" });
|
|
249
|
+
|
|
250
|
+
const sendResult = await sendAgentMessage({
|
|
251
|
+
projectPath,
|
|
252
|
+
fromAgent: "Agent1",
|
|
253
|
+
toAgents: ["Agent2", "Agent3"],
|
|
254
|
+
subject: "Test",
|
|
255
|
+
body: "Hello",
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const result = await debugMessage({
|
|
259
|
+
projectPath,
|
|
260
|
+
messageId: sendResult.messageId,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(result.recipients).toHaveLength(2);
|
|
264
|
+
expect(result.recipients.map((r) => r.agent_name)).toContain("Agent2");
|
|
265
|
+
expect(result.recipients.map((r) => r.agent_name)).toContain("Agent3");
|
|
266
|
+
// Initially unread
|
|
267
|
+
expect(result.recipients.every((r) => r.read_at === null)).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("includes related events", async () => {
|
|
271
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
272
|
+
|
|
273
|
+
const sendResult = await sendAgentMessage({
|
|
274
|
+
projectPath,
|
|
275
|
+
fromAgent: "Agent1",
|
|
276
|
+
toAgents: ["Agent2"],
|
|
277
|
+
subject: "Test",
|
|
278
|
+
body: "Hello",
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const result = await debugMessage({
|
|
282
|
+
projectPath,
|
|
283
|
+
messageId: sendResult.messageId,
|
|
284
|
+
includeEvents: true,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
expect(result.events).toBeDefined();
|
|
288
|
+
expect(result.events!.some((e) => e.type === "message_sent")).toBe(true);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("returns null for non-existent message", async () => {
|
|
292
|
+
const result = await debugMessage({
|
|
293
|
+
projectPath,
|
|
294
|
+
messageId: 99999,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
expect(result.message).toBeNull();
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// ============================================================================
|
|
302
|
+
// debugReservations - Current reservation state
|
|
303
|
+
// ============================================================================
|
|
304
|
+
|
|
305
|
+
describe("debugReservations", () => {
|
|
306
|
+
it("returns all active reservations", async () => {
|
|
307
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
308
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
309
|
+
|
|
310
|
+
await reserveAgentFiles({
|
|
311
|
+
projectPath,
|
|
312
|
+
agentName: "Agent1",
|
|
313
|
+
paths: ["src/a.ts"],
|
|
314
|
+
reason: "Working on A",
|
|
315
|
+
});
|
|
316
|
+
await reserveAgentFiles({
|
|
317
|
+
projectPath,
|
|
318
|
+
agentName: "Agent2",
|
|
319
|
+
paths: ["src/b.ts"],
|
|
320
|
+
reason: "Working on B",
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const result = await debugReservations({ projectPath });
|
|
324
|
+
|
|
325
|
+
expect(result.reservations).toHaveLength(2);
|
|
326
|
+
expect(result.byAgent).toHaveProperty("Agent1");
|
|
327
|
+
expect(result.byAgent).toHaveProperty("Agent2");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("groups reservations by agent", async () => {
|
|
331
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
332
|
+
|
|
333
|
+
await reserveAgentFiles({
|
|
334
|
+
projectPath,
|
|
335
|
+
agentName: "Agent1",
|
|
336
|
+
paths: ["src/a.ts", "src/b.ts", "src/c.ts"],
|
|
337
|
+
reason: "Working",
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const result = await debugReservations({ projectPath });
|
|
341
|
+
|
|
342
|
+
expect(result.byAgent.Agent1).toHaveLength(3);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("includes expiration info", async () => {
|
|
346
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
347
|
+
|
|
348
|
+
await reserveAgentFiles({
|
|
349
|
+
projectPath,
|
|
350
|
+
agentName: "Agent1",
|
|
351
|
+
paths: ["src/a.ts"],
|
|
352
|
+
ttlSeconds: 3600,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const result = await debugReservations({ projectPath });
|
|
356
|
+
|
|
357
|
+
expect(result.reservations[0]).toHaveProperty("expires_at");
|
|
358
|
+
expect(result.reservations[0]).toHaveProperty("expires_in_human");
|
|
359
|
+
expect(typeof result.reservations[0].expires_in_human).toBe("string");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("detects potential conflicts", async () => {
|
|
363
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
364
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
365
|
+
|
|
366
|
+
// Agent1 reserves src/**
|
|
367
|
+
await reserveAgentFiles({
|
|
368
|
+
projectPath,
|
|
369
|
+
agentName: "Agent1",
|
|
370
|
+
paths: ["src/**"],
|
|
371
|
+
reason: "Broad reservation",
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Agent2 forces reservation of src/specific.ts (to test conflict detection)
|
|
375
|
+
await reserveAgentFiles({
|
|
376
|
+
projectPath,
|
|
377
|
+
agentName: "Agent2",
|
|
378
|
+
paths: ["src/specific.ts"],
|
|
379
|
+
reason: "Specific file",
|
|
380
|
+
force: true, // Force to create overlapping reservation for conflict test
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const result = await debugReservations({
|
|
384
|
+
projectPath,
|
|
385
|
+
checkConflicts: true,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
expect(result.conflicts).toBeDefined();
|
|
389
|
+
expect(result.conflicts!.length).toBeGreaterThan(0);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// ============================================================================
|
|
394
|
+
// getEventTimeline - Visual timeline of events
|
|
395
|
+
// ============================================================================
|
|
396
|
+
|
|
397
|
+
describe("getEventTimeline", () => {
|
|
398
|
+
it("returns events formatted for timeline display", async () => {
|
|
399
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
400
|
+
await sendAgentMessage({
|
|
401
|
+
projectPath,
|
|
402
|
+
fromAgent: "Agent1",
|
|
403
|
+
toAgents: ["Agent2"],
|
|
404
|
+
subject: "Test",
|
|
405
|
+
body: "Hello",
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
const result = await getEventTimeline({ projectPath });
|
|
409
|
+
|
|
410
|
+
expect(result.timeline).toBeDefined();
|
|
411
|
+
expect(Array.isArray(result.timeline)).toBe(true);
|
|
412
|
+
expect(result.timeline.length).toBeGreaterThan(0);
|
|
413
|
+
|
|
414
|
+
// Each entry should have display-friendly format
|
|
415
|
+
const entry = result.timeline[0];
|
|
416
|
+
expect(entry).toHaveProperty("time");
|
|
417
|
+
expect(entry).toHaveProperty("type");
|
|
418
|
+
expect(entry).toHaveProperty("summary");
|
|
419
|
+
expect(entry).toHaveProperty("agent");
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it("filters by time range", async () => {
|
|
423
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
424
|
+
|
|
425
|
+
// Wait to ensure timestamp separation
|
|
426
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
427
|
+
const afterFirst = Date.now();
|
|
428
|
+
|
|
429
|
+
// Wait a bit more
|
|
430
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
431
|
+
|
|
432
|
+
await initAgent({ projectPath, agentName: "Agent2" });
|
|
433
|
+
|
|
434
|
+
const result = await getEventTimeline({
|
|
435
|
+
projectPath,
|
|
436
|
+
since: afterFirst,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Should only include Agent2's registration
|
|
440
|
+
expect(result.timeline).toHaveLength(1);
|
|
441
|
+
expect(result.timeline[0].agent).toBe("Agent2");
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// ============================================================================
|
|
446
|
+
// inspectState - Full state dump for debugging
|
|
447
|
+
// ============================================================================
|
|
448
|
+
|
|
449
|
+
describe("inspectState", () => {
|
|
450
|
+
it("returns complete state snapshot", async () => {
|
|
451
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
452
|
+
await sendAgentMessage({
|
|
453
|
+
projectPath,
|
|
454
|
+
fromAgent: "Agent1",
|
|
455
|
+
toAgents: ["Agent2"],
|
|
456
|
+
subject: "Test",
|
|
457
|
+
body: "Hello",
|
|
458
|
+
});
|
|
459
|
+
await reserveAgentFiles({
|
|
460
|
+
projectPath,
|
|
461
|
+
agentName: "Agent1",
|
|
462
|
+
paths: ["src/a.ts"],
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
const result = await inspectState({ projectPath });
|
|
466
|
+
|
|
467
|
+
expect(result).toHaveProperty("agents");
|
|
468
|
+
expect(result).toHaveProperty("messages");
|
|
469
|
+
expect(result).toHaveProperty("reservations");
|
|
470
|
+
expect(result).toHaveProperty("eventCount");
|
|
471
|
+
expect(result).toHaveProperty("latestSequence");
|
|
472
|
+
|
|
473
|
+
expect(result.agents).toHaveLength(1);
|
|
474
|
+
expect(result.messages).toHaveLength(1);
|
|
475
|
+
expect(result.reservations).toHaveLength(1);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it("includes database stats", async () => {
|
|
479
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
480
|
+
|
|
481
|
+
const result = await inspectState({ projectPath });
|
|
482
|
+
|
|
483
|
+
expect(result.stats).toBeDefined();
|
|
484
|
+
expect(result.stats).toHaveProperty("events");
|
|
485
|
+
expect(result.stats).toHaveProperty("agents");
|
|
486
|
+
expect(result.stats).toHaveProperty("messages");
|
|
487
|
+
expect(result.stats).toHaveProperty("reservations");
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("can export as JSON string", async () => {
|
|
491
|
+
await initAgent({ projectPath, agentName: "Agent1" });
|
|
492
|
+
|
|
493
|
+
const result = await inspectState({ projectPath, format: "json" });
|
|
494
|
+
|
|
495
|
+
expect(typeof result.json).toBe("string");
|
|
496
|
+
const parsed = JSON.parse(result.json!);
|
|
497
|
+
expect(parsed).toHaveProperty("agents");
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
});
|