opencode-swarm-plugin 0.20.0 → 0.22.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 +213 -0
- package/INTEGRATION_EXAMPLE.md +66 -0
- package/README.md +352 -522
- package/dist/index.js +2046 -984
- package/dist/plugin.js +2051 -1017
- package/docs/analysis/subagent-coordination-patterns.md +2 -0
- package/docs/semantic-memory-cli-syntax.md +123 -0
- package/docs/swarm-mail-architecture.md +1147 -0
- package/evals/README.md +116 -0
- package/evals/evalite.config.ts +15 -0
- package/evals/example.eval.ts +32 -0
- package/evals/fixtures/decomposition-cases.ts +105 -0
- package/evals/lib/data-loader.test.ts +288 -0
- package/evals/lib/data-loader.ts +111 -0
- package/evals/lib/llm.ts +115 -0
- package/evals/scorers/index.ts +200 -0
- package/evals/scorers/outcome-scorers.test.ts +27 -0
- package/evals/scorers/outcome-scorers.ts +349 -0
- package/evals/swarm-decomposition.eval.ts +112 -0
- package/package.json +8 -1
- package/scripts/cleanup-test-memories.ts +346 -0
- package/src/beads.ts +49 -0
- package/src/eval-capture.ts +487 -0
- package/src/index.ts +45 -3
- package/src/learning.integration.test.ts +19 -4
- package/src/output-guardrails.test.ts +438 -0
- package/src/output-guardrails.ts +381 -0
- package/src/schemas/index.ts +18 -0
- package/src/schemas/swarm-context.ts +115 -0
- package/src/storage.ts +117 -5
- package/src/streams/events.test.ts +296 -0
- package/src/streams/events.ts +93 -0
- package/src/streams/migrations.test.ts +24 -20
- package/src/streams/migrations.ts +51 -0
- package/src/streams/projections.ts +187 -0
- package/src/streams/store.ts +275 -0
- package/src/swarm-orchestrate.ts +771 -189
- package/src/swarm-prompts.ts +84 -12
- package/src/swarm.integration.test.ts +124 -0
- package/vitest.integration.config.ts +6 -0
- package/vitest.integration.setup.ts +48 -0
package/src/swarm-prompts.ts
CHANGED
|
@@ -273,20 +273,29 @@ Only modify these files. Need others? Message the coordinator.
|
|
|
273
273
|
|
|
274
274
|
{error_context}
|
|
275
275
|
|
|
276
|
-
## [MANDATORY: SWARM MAIL]
|
|
276
|
+
## [MANDATORY: SWARM MAIL INITIALIZATION]
|
|
277
277
|
|
|
278
|
-
**YOU MUST
|
|
278
|
+
**CRITICAL: YOU MUST INITIALIZE SWARM MAIL BEFORE DOING ANY WORK.**
|
|
279
279
|
|
|
280
|
-
|
|
281
|
-
\`\`\`
|
|
282
|
-
swarmmail_init(project_path="$PWD", task_description="{subtask_title}")
|
|
283
|
-
\`\`\`
|
|
280
|
+
This is your FIRST step - before reading files, before planning, before ANY other action.
|
|
284
281
|
|
|
285
|
-
###
|
|
282
|
+
### Step 1: Initialize (REQUIRED - DO THIS FIRST)
|
|
286
283
|
\`\`\`
|
|
287
|
-
|
|
284
|
+
swarmmail_init(project_path="{project_path}", task_description="{bead_id}: {subtask_title}")
|
|
288
285
|
\`\`\`
|
|
289
286
|
|
|
287
|
+
**This registers you with the coordination system and enables:**
|
|
288
|
+
- File reservation tracking
|
|
289
|
+
- Inter-agent communication
|
|
290
|
+
- Progress monitoring
|
|
291
|
+
- Conflict detection
|
|
292
|
+
|
|
293
|
+
**If you skip this step, your work will not be tracked and swarm_complete will fail.**
|
|
294
|
+
|
|
295
|
+
## [SWARM MAIL USAGE]
|
|
296
|
+
|
|
297
|
+
After initialization, use Swarm Mail for coordination:
|
|
298
|
+
|
|
290
299
|
### Check Inbox Regularly
|
|
291
300
|
\`\`\`
|
|
292
301
|
swarmmail_inbox() # Check for coordinator messages
|
|
@@ -340,14 +349,17 @@ As you work, note reusable patterns, best practices, or domain insights:
|
|
|
340
349
|
- Skills make swarms smarter over time
|
|
341
350
|
|
|
342
351
|
## [WORKFLOW]
|
|
343
|
-
1. **swarmmail_init** - Initialize session FIRST
|
|
352
|
+
1. **swarmmail_init** - Initialize session (MANDATORY FIRST STEP)
|
|
344
353
|
2. Read assigned files
|
|
345
354
|
3. Implement changes
|
|
346
355
|
4. **swarmmail_send** - Report progress to coordinator
|
|
347
356
|
5. Verify (typecheck)
|
|
348
357
|
6. **swarm_complete** - Mark done, release reservations
|
|
349
358
|
|
|
350
|
-
**CRITICAL
|
|
359
|
+
**CRITICAL REQUIREMENTS:**
|
|
360
|
+
- Step 1 (swarmmail_init) is NON-NEGOTIABLE - do it before anything else
|
|
361
|
+
- Never work silently - send progress updates via swarmmail_send every significant milestone
|
|
362
|
+
- If you complete without initializing, swarm_complete will detect this and warn/fail
|
|
351
363
|
|
|
352
364
|
Begin now.`;
|
|
353
365
|
|
|
@@ -409,6 +421,12 @@ export function formatSubtaskPromptV2(params: {
|
|
|
409
421
|
shared_context?: string;
|
|
410
422
|
compressed_context?: string;
|
|
411
423
|
error_context?: string;
|
|
424
|
+
project_path?: string;
|
|
425
|
+
recovery_context?: {
|
|
426
|
+
shared_context?: string;
|
|
427
|
+
skills_to_load?: string[];
|
|
428
|
+
coordinator_notes?: string;
|
|
429
|
+
};
|
|
412
430
|
}): string {
|
|
413
431
|
const fileList =
|
|
414
432
|
params.files.length > 0
|
|
@@ -421,8 +439,40 @@ export function formatSubtaskPromptV2(params: {
|
|
|
421
439
|
|
|
422
440
|
const errorSection = params.error_context ? params.error_context : "";
|
|
423
441
|
|
|
442
|
+
// Build recovery context section
|
|
443
|
+
let recoverySection = "";
|
|
444
|
+
if (params.recovery_context) {
|
|
445
|
+
const sections: string[] = [];
|
|
446
|
+
|
|
447
|
+
if (params.recovery_context.shared_context) {
|
|
448
|
+
sections.push(
|
|
449
|
+
`### Recovery Context\n${params.recovery_context.shared_context}`,
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (
|
|
454
|
+
params.recovery_context.skills_to_load &&
|
|
455
|
+
params.recovery_context.skills_to_load.length > 0
|
|
456
|
+
) {
|
|
457
|
+
sections.push(
|
|
458
|
+
`### Skills to Load\nBefore starting work, load these skills for specialized guidance:\n${params.recovery_context.skills_to_load.map((s) => `- skills_use(name="${s}")`).join("\n")}`,
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (params.recovery_context.coordinator_notes) {
|
|
463
|
+
sections.push(
|
|
464
|
+
`### Coordinator Notes\n${params.recovery_context.coordinator_notes}`,
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (sections.length > 0) {
|
|
469
|
+
recoverySection = `\n## [RECOVERY CONTEXT]\n\n${sections.join("\n\n")}`;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
424
473
|
return SUBTASK_PROMPT_V2.replace(/{bead_id}/g, params.bead_id)
|
|
425
474
|
.replace(/{epic_id}/g, params.epic_id)
|
|
475
|
+
.replace(/{project_path}/g, params.project_path || "$PWD")
|
|
426
476
|
.replace("{subtask_title}", params.subtask_title)
|
|
427
477
|
.replace(
|
|
428
478
|
"{subtask_description}",
|
|
@@ -431,7 +481,7 @@ export function formatSubtaskPromptV2(params: {
|
|
|
431
481
|
.replace("{file_list}", fileList)
|
|
432
482
|
.replace("{shared_context}", params.shared_context || "(none)")
|
|
433
483
|
.replace("{compressed_context}", compressedSection)
|
|
434
|
-
.replace("{error_context}", errorSection);
|
|
484
|
+
.replace("{error_context}", errorSection + recoverySection);
|
|
435
485
|
}
|
|
436
486
|
|
|
437
487
|
/**
|
|
@@ -497,6 +547,10 @@ export const swarm_subtask_prompt = tool({
|
|
|
497
547
|
.string()
|
|
498
548
|
.optional()
|
|
499
549
|
.describe("Context shared across all agents"),
|
|
550
|
+
project_path: tool.schema
|
|
551
|
+
.string()
|
|
552
|
+
.optional()
|
|
553
|
+
.describe("Absolute project path for swarmmail_init"),
|
|
500
554
|
},
|
|
501
555
|
async execute(args) {
|
|
502
556
|
const prompt = formatSubtaskPrompt({
|
|
@@ -521,7 +575,7 @@ export const swarm_subtask_prompt = tool({
|
|
|
521
575
|
*/
|
|
522
576
|
export const swarm_spawn_subtask = tool({
|
|
523
577
|
description:
|
|
524
|
-
"Prepare a subtask for spawning. Returns prompt with Agent Mail/beads instructions.",
|
|
578
|
+
"Prepare a subtask for spawning. Returns prompt with Agent Mail/beads instructions. IMPORTANT: Pass project_path for swarmmail_init.",
|
|
525
579
|
args: {
|
|
526
580
|
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
527
581
|
epic_id: tool.schema.string().describe("Parent epic bead ID"),
|
|
@@ -537,6 +591,20 @@ export const swarm_spawn_subtask = tool({
|
|
|
537
591
|
.string()
|
|
538
592
|
.optional()
|
|
539
593
|
.describe("Context shared across all agents"),
|
|
594
|
+
project_path: tool.schema
|
|
595
|
+
.string()
|
|
596
|
+
.optional()
|
|
597
|
+
.describe(
|
|
598
|
+
"Absolute project path for swarmmail_init (REQUIRED for tracking)",
|
|
599
|
+
),
|
|
600
|
+
recovery_context: tool.schema
|
|
601
|
+
.object({
|
|
602
|
+
shared_context: tool.schema.string().optional(),
|
|
603
|
+
skills_to_load: tool.schema.array(tool.schema.string()).optional(),
|
|
604
|
+
coordinator_notes: tool.schema.string().optional(),
|
|
605
|
+
})
|
|
606
|
+
.optional()
|
|
607
|
+
.describe("Recovery context from checkpoint compaction"),
|
|
540
608
|
},
|
|
541
609
|
async execute(args) {
|
|
542
610
|
const prompt = formatSubtaskPromptV2({
|
|
@@ -546,6 +614,8 @@ export const swarm_spawn_subtask = tool({
|
|
|
546
614
|
subtask_description: args.subtask_description || "",
|
|
547
615
|
files: args.files,
|
|
548
616
|
shared_context: args.shared_context,
|
|
617
|
+
project_path: args.project_path,
|
|
618
|
+
recovery_context: args.recovery_context,
|
|
549
619
|
});
|
|
550
620
|
|
|
551
621
|
return JSON.stringify(
|
|
@@ -554,6 +624,8 @@ export const swarm_spawn_subtask = tool({
|
|
|
554
624
|
bead_id: args.bead_id,
|
|
555
625
|
epic_id: args.epic_id,
|
|
556
626
|
files: args.files,
|
|
627
|
+
project_path: args.project_path,
|
|
628
|
+
recovery_context: args.recovery_context,
|
|
557
629
|
},
|
|
558
630
|
null,
|
|
559
631
|
2,
|
|
@@ -1356,4 +1356,128 @@ describe("Swarm Prompt V2 (with Swarm Mail/Beads)", () => {
|
|
|
1356
1356
|
expect(SUBTASK_PROMPT_V2).toContain("CRITICAL");
|
|
1357
1357
|
});
|
|
1358
1358
|
});
|
|
1359
|
+
|
|
1360
|
+
describe("swarm_complete automatic memory capture", () => {
|
|
1361
|
+
let beadsAvailable = false;
|
|
1362
|
+
|
|
1363
|
+
beforeAll(async () => {
|
|
1364
|
+
beadsAvailable = await isBeadsAvailable();
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
it.skipIf(!beadsAvailable)(
|
|
1368
|
+
"includes memory_capture object in response",
|
|
1369
|
+
async () => {
|
|
1370
|
+
// Create a real bead for the test
|
|
1371
|
+
const createResult =
|
|
1372
|
+
await Bun.$`bd create "Test memory capture" -t task --json`
|
|
1373
|
+
.quiet()
|
|
1374
|
+
.nothrow();
|
|
1375
|
+
|
|
1376
|
+
if (createResult.exitCode !== 0) {
|
|
1377
|
+
console.warn(
|
|
1378
|
+
"Could not create bead:",
|
|
1379
|
+
createResult.stderr.toString(),
|
|
1380
|
+
);
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const bead = JSON.parse(createResult.stdout.toString());
|
|
1385
|
+
|
|
1386
|
+
try {
|
|
1387
|
+
const result = await swarm_complete.execute(
|
|
1388
|
+
{
|
|
1389
|
+
project_key: "/tmp/test-memory-capture",
|
|
1390
|
+
agent_name: "test-agent",
|
|
1391
|
+
bead_id: bead.id,
|
|
1392
|
+
summary: "Implemented auto-capture feature",
|
|
1393
|
+
files_touched: ["src/swarm-orchestrate.ts"],
|
|
1394
|
+
skip_verification: true,
|
|
1395
|
+
},
|
|
1396
|
+
mockContext,
|
|
1397
|
+
);
|
|
1398
|
+
|
|
1399
|
+
const parsed = JSON.parse(result);
|
|
1400
|
+
|
|
1401
|
+
// Verify memory capture was attempted
|
|
1402
|
+
expect(parsed).toHaveProperty("memory_capture");
|
|
1403
|
+
expect(parsed.memory_capture).toHaveProperty("attempted", true);
|
|
1404
|
+
expect(parsed.memory_capture).toHaveProperty("stored");
|
|
1405
|
+
expect(parsed.memory_capture).toHaveProperty("information");
|
|
1406
|
+
expect(parsed.memory_capture).toHaveProperty("metadata");
|
|
1407
|
+
|
|
1408
|
+
// Information should contain bead ID and summary
|
|
1409
|
+
expect(parsed.memory_capture.information).toContain(bead.id);
|
|
1410
|
+
expect(parsed.memory_capture.information).toContain(
|
|
1411
|
+
"Implemented auto-capture feature",
|
|
1412
|
+
);
|
|
1413
|
+
|
|
1414
|
+
// Metadata should contain relevant tags
|
|
1415
|
+
expect(parsed.memory_capture.metadata).toContain("swarm");
|
|
1416
|
+
expect(parsed.memory_capture.metadata).toContain("success");
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
// Clean up bead if test fails
|
|
1419
|
+
await Bun.$`bd close ${bead.id} --reason "Test cleanup"`
|
|
1420
|
+
.quiet()
|
|
1421
|
+
.nothrow();
|
|
1422
|
+
throw error;
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
);
|
|
1426
|
+
|
|
1427
|
+
it.skipIf(!beadsAvailable)(
|
|
1428
|
+
"attempts to store in semantic-memory when available",
|
|
1429
|
+
async () => {
|
|
1430
|
+
const createResult =
|
|
1431
|
+
await Bun.$`bd create "Test semantic-memory storage" -t task --json`
|
|
1432
|
+
.quiet()
|
|
1433
|
+
.nothrow();
|
|
1434
|
+
|
|
1435
|
+
if (createResult.exitCode !== 0) {
|
|
1436
|
+
console.warn(
|
|
1437
|
+
"Could not create bead:",
|
|
1438
|
+
createResult.stderr.toString(),
|
|
1439
|
+
);
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
const bead = JSON.parse(createResult.stdout.toString());
|
|
1444
|
+
|
|
1445
|
+
try {
|
|
1446
|
+
const result = await swarm_complete.execute(
|
|
1447
|
+
{
|
|
1448
|
+
project_key: "/tmp/test-memory-storage",
|
|
1449
|
+
agent_name: "test-agent",
|
|
1450
|
+
bead_id: bead.id,
|
|
1451
|
+
summary: "Fixed critical bug in auth flow",
|
|
1452
|
+
files_touched: ["src/auth.ts", "src/middleware.ts"],
|
|
1453
|
+
skip_verification: true,
|
|
1454
|
+
},
|
|
1455
|
+
mockContext,
|
|
1456
|
+
);
|
|
1457
|
+
|
|
1458
|
+
const parsed = JSON.parse(result);
|
|
1459
|
+
|
|
1460
|
+
// If semantic-memory is available, stored should be true
|
|
1461
|
+
// If not, error should explain why
|
|
1462
|
+
if (parsed.memory_capture.stored) {
|
|
1463
|
+
expect(parsed.memory_capture.note).toContain(
|
|
1464
|
+
"automatically stored in semantic-memory",
|
|
1465
|
+
);
|
|
1466
|
+
} else {
|
|
1467
|
+
expect(parsed.memory_capture.error).toBeDefined();
|
|
1468
|
+
expect(
|
|
1469
|
+
parsed.memory_capture.error.includes("not available") ||
|
|
1470
|
+
parsed.memory_capture.error.includes("failed"),
|
|
1471
|
+
).toBe(true);
|
|
1472
|
+
}
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
// Clean up bead if test fails
|
|
1475
|
+
await Bun.$`bd close ${bead.id} --reason "Test cleanup"`
|
|
1476
|
+
.quiet()
|
|
1477
|
+
.nothrow();
|
|
1478
|
+
throw error;
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
);
|
|
1482
|
+
});
|
|
1359
1483
|
});
|
|
@@ -9,5 +9,11 @@ export default defineConfig({
|
|
|
9
9
|
sequence: {
|
|
10
10
|
concurrent: false,
|
|
11
11
|
},
|
|
12
|
+
env: {
|
|
13
|
+
// Enable test-specific collections to isolate test data from production
|
|
14
|
+
TEST_MEMORY_COLLECTIONS: "true",
|
|
15
|
+
},
|
|
16
|
+
// Global setup/teardown hooks
|
|
17
|
+
globalSetup: "./vitest.integration.setup.ts",
|
|
12
18
|
},
|
|
13
19
|
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global setup/teardown for integration tests
|
|
3
|
+
*
|
|
4
|
+
* Ensures test-specific semantic-memory collections are cleaned up
|
|
5
|
+
* after all integration tests complete.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export async function setup() {
|
|
9
|
+
console.log("[vitest] Integration test setup: TEST_MEMORY_COLLECTIONS=true");
|
|
10
|
+
// Setup runs before tests - environment variables are already set via vitest config
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function teardown() {
|
|
14
|
+
console.log(
|
|
15
|
+
"[vitest] Integration test teardown: cleaning up test collections",
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// Clean up test collections
|
|
19
|
+
const testCollections = [
|
|
20
|
+
"swarm-feedback-test",
|
|
21
|
+
"swarm-patterns-test",
|
|
22
|
+
"swarm-maturity-test",
|
|
23
|
+
"swarm-maturity-test-feedback",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
for (const collection of testCollections) {
|
|
27
|
+
try {
|
|
28
|
+
// Attempt to remove test collection data
|
|
29
|
+
// Note: semantic-memory doesn't have a built-in "delete collection" command,
|
|
30
|
+
// so we'll use the remove command with a wildcard or rely on TTL/manual cleanup
|
|
31
|
+
console.log(`[vitest] Attempting to clean collection: ${collection}`);
|
|
32
|
+
|
|
33
|
+
// List items and remove them (semantic-memory may not support bulk delete)
|
|
34
|
+
// This is a best-effort cleanup - some backends may require manual cleanup
|
|
35
|
+
// For now, we'll just log that cleanup should happen
|
|
36
|
+
console.log(
|
|
37
|
+
`[vitest] Note: Collection "${collection}" may need manual cleanup via semantic-memory CLI`,
|
|
38
|
+
);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn(
|
|
41
|
+
`[vitest] Failed to clean collection ${collection}:`,
|
|
42
|
+
error instanceof Error ? error.message : String(error),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log("[vitest] Integration test teardown complete");
|
|
48
|
+
}
|