opencode-swarm-plugin 0.18.0 → 0.20.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/.beads/issues.jsonl +74 -61
- package/.github/workflows/ci.yml +5 -1
- package/README.md +48 -4
- package/dist/index.js +6643 -6326
- package/dist/plugin.js +2726 -2404
- package/package.json +1 -1
- package/src/agent-mail.ts +20 -7
- package/src/anti-patterns.test.ts +1167 -0
- package/src/anti-patterns.ts +29 -11
- package/src/index.ts +8 -0
- package/src/pattern-maturity.test.ts +1160 -0
- package/src/pattern-maturity.ts +51 -13
- package/src/plugin.ts +15 -3
- package/src/schemas/bead.ts +35 -4
- package/src/schemas/evaluation.ts +18 -6
- package/src/schemas/index.ts +25 -2
- package/src/schemas/task.ts +49 -21
- package/src/streams/debug.ts +101 -3
- package/src/streams/events.ts +22 -0
- package/src/streams/index.ts +58 -1
- package/src/streams/migrations.ts +46 -4
- package/src/streams/store.integration.test.ts +110 -0
- package/src/streams/store.ts +311 -126
- package/src/structured.test.ts +1046 -0
- package/src/structured.ts +74 -27
- package/src/swarm-decompose.ts +912 -0
- package/src/swarm-mail.ts +7 -7
- package/src/swarm-orchestrate.ts +1869 -0
- package/src/swarm-prompts.ts +756 -0
- package/src/swarm-strategies.ts +407 -0
- package/src/swarm.ts +23 -3876
- package/src/tool-availability.ts +29 -6
- package/test-bug-fixes.ts +86 -0
|
@@ -1,9 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Schema Migration System
|
|
2
|
+
* Schema Migration System
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Handles database schema evolution for the PGLite event store.
|
|
5
|
+
*
|
|
6
|
+
* ## How It Works
|
|
7
|
+
*
|
|
8
|
+
* 1. Each migration has a unique version number (incrementing integer)
|
|
9
|
+
* 2. On startup, `runMigrations()` checks current schema version
|
|
10
|
+
* 3. Migrations are applied in order until schema is current
|
|
11
|
+
* 4. Version is stored in `schema_version` table
|
|
12
|
+
*
|
|
13
|
+
* ## Adding a New Migration
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // In migrations.ts
|
|
17
|
+
* export const migrations: Migration[] = [
|
|
18
|
+
* // ... existing migrations
|
|
19
|
+
* {
|
|
20
|
+
* version: 3,
|
|
21
|
+
* description: "add_new_column",
|
|
22
|
+
* up: `ALTER TABLE events ADD COLUMN new_col TEXT`,
|
|
23
|
+
* down: `ALTER TABLE events DROP COLUMN new_col`,
|
|
24
|
+
* },
|
|
25
|
+
* ];
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ## Rollback
|
|
29
|
+
*
|
|
30
|
+
* Rollback is supported via `rollbackTo(db, targetVersion)`.
|
|
31
|
+
* Note: Some migrations may not be fully reversible (data loss).
|
|
32
|
+
*
|
|
33
|
+
* ## Best Practices
|
|
34
|
+
*
|
|
35
|
+
* - Always test migrations on a copy of production data
|
|
36
|
+
* - Keep migrations small and focused
|
|
37
|
+
* - Include both `up` and `down` SQL
|
|
38
|
+
* - Use transactions for multi-statement migrations
|
|
39
|
+
* - Document any data transformations
|
|
40
|
+
*
|
|
41
|
+
* @module migrations
|
|
7
42
|
*/
|
|
8
43
|
import type { PGlite } from "@electric-sql/pglite";
|
|
9
44
|
|
|
@@ -11,10 +46,17 @@ import type { PGlite } from "@electric-sql/pglite";
|
|
|
11
46
|
// Types
|
|
12
47
|
// ============================================================================
|
|
13
48
|
|
|
49
|
+
/**
|
|
50
|
+
* A database migration definition.
|
|
51
|
+
*/
|
|
14
52
|
export interface Migration {
|
|
53
|
+
/** Unique version number (must be sequential) */
|
|
15
54
|
version: number;
|
|
55
|
+
/** Human-readable migration description */
|
|
16
56
|
description: string;
|
|
57
|
+
/** SQL to apply the migration */
|
|
17
58
|
up: string;
|
|
59
|
+
/** SQL to rollback the migration (best effort) */
|
|
18
60
|
down: string;
|
|
19
61
|
}
|
|
20
62
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
readEvents,
|
|
18
18
|
getLatestSequence,
|
|
19
19
|
replayEvents,
|
|
20
|
+
replayEventsBatched,
|
|
20
21
|
registerAgent,
|
|
21
22
|
sendMessage,
|
|
22
23
|
reserveFiles,
|
|
@@ -343,6 +344,115 @@ describe("Event Store", () => {
|
|
|
343
344
|
});
|
|
344
345
|
});
|
|
345
346
|
|
|
347
|
+
describe("replayEventsBatched", () => {
|
|
348
|
+
it("should replay events in batches with progress tracking", async () => {
|
|
349
|
+
// Create 50 events
|
|
350
|
+
for (let i = 0; i < 50; i++) {
|
|
351
|
+
await registerAgent(
|
|
352
|
+
"test-project",
|
|
353
|
+
`Agent${i}`,
|
|
354
|
+
{ taskDescription: `Agent ${i}` },
|
|
355
|
+
TEST_PROJECT_PATH,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Manually corrupt the views
|
|
360
|
+
const db = await getDatabase(TEST_PROJECT_PATH);
|
|
361
|
+
await db.query("DELETE FROM agents WHERE project_key = 'test-project'");
|
|
362
|
+
|
|
363
|
+
// Verify views are empty
|
|
364
|
+
const empty = await db.query<{ count: string }>(
|
|
365
|
+
"SELECT COUNT(*) as count FROM agents WHERE project_key = 'test-project'",
|
|
366
|
+
);
|
|
367
|
+
expect(parseInt(empty.rows[0]?.count ?? "0")).toBe(0);
|
|
368
|
+
|
|
369
|
+
// Track progress
|
|
370
|
+
const progressUpdates: Array<{
|
|
371
|
+
processed: number;
|
|
372
|
+
total: number;
|
|
373
|
+
percent: number;
|
|
374
|
+
}> = [];
|
|
375
|
+
|
|
376
|
+
// Replay in batches of 10
|
|
377
|
+
const result = await replayEventsBatched(
|
|
378
|
+
"test-project",
|
|
379
|
+
async (_events, progress) => {
|
|
380
|
+
progressUpdates.push(progress);
|
|
381
|
+
},
|
|
382
|
+
{ batchSize: 10, clearViews: false },
|
|
383
|
+
TEST_PROJECT_PATH,
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
// Verify all events replayed
|
|
387
|
+
expect(result.eventsReplayed).toBe(50);
|
|
388
|
+
|
|
389
|
+
// Verify progress updates
|
|
390
|
+
expect(progressUpdates.length).toBe(5); // 50 events / 10 per batch = 5 batches
|
|
391
|
+
expect(progressUpdates[0]).toMatchObject({
|
|
392
|
+
processed: 10,
|
|
393
|
+
total: 50,
|
|
394
|
+
percent: 20,
|
|
395
|
+
});
|
|
396
|
+
expect(progressUpdates[4]).toMatchObject({
|
|
397
|
+
processed: 50,
|
|
398
|
+
total: 50,
|
|
399
|
+
percent: 100,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Verify views are restored
|
|
403
|
+
const restored = await db.query<{ count: string }>(
|
|
404
|
+
"SELECT COUNT(*) as count FROM agents WHERE project_key = 'test-project'",
|
|
405
|
+
);
|
|
406
|
+
expect(parseInt(restored.rows[0]?.count ?? "0")).toBe(50);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it("should handle zero events gracefully", async () => {
|
|
410
|
+
const progressUpdates: Array<{
|
|
411
|
+
processed: number;
|
|
412
|
+
total: number;
|
|
413
|
+
percent: number;
|
|
414
|
+
}> = [];
|
|
415
|
+
|
|
416
|
+
const result = await replayEventsBatched(
|
|
417
|
+
"test-project",
|
|
418
|
+
async (_events, progress) => {
|
|
419
|
+
progressUpdates.push(progress);
|
|
420
|
+
},
|
|
421
|
+
{ batchSize: 10 },
|
|
422
|
+
TEST_PROJECT_PATH,
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
expect(result.eventsReplayed).toBe(0);
|
|
426
|
+
expect(progressUpdates.length).toBe(0);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it("should use custom batch size", async () => {
|
|
430
|
+
// Create 25 events
|
|
431
|
+
for (let i = 0; i < 25; i++) {
|
|
432
|
+
await registerAgent("test-project", `Agent${i}`, {}, TEST_PROJECT_PATH);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const progressUpdates: Array<{
|
|
436
|
+
processed: number;
|
|
437
|
+
total: number;
|
|
438
|
+
percent: number;
|
|
439
|
+
}> = [];
|
|
440
|
+
|
|
441
|
+
// Replay with batch size of 5
|
|
442
|
+
await replayEventsBatched(
|
|
443
|
+
"test-project",
|
|
444
|
+
async (_events, progress) => {
|
|
445
|
+
progressUpdates.push(progress);
|
|
446
|
+
},
|
|
447
|
+
{ batchSize: 5, clearViews: false },
|
|
448
|
+
TEST_PROJECT_PATH,
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
// Should have 5 batches (25 events / 5 per batch)
|
|
452
|
+
expect(progressUpdates.length).toBe(5);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
346
456
|
describe("getDatabaseStats", () => {
|
|
347
457
|
it("should return correct counts", async () => {
|
|
348
458
|
await registerAgent("test-project", "Agent1", {}, TEST_PROJECT_PATH);
|