opencode-swarm-plugin 0.31.7 → 0.33.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/.turbo/turbo-build.log +4 -4
- package/.turbo/turbo-test.log +324 -316
- package/CHANGELOG.md +394 -0
- package/README.md +129 -181
- package/bin/swarm.test.ts +31 -0
- package/bin/swarm.ts +635 -140
- package/dist/compaction-hook.d.ts +1 -1
- package/dist/compaction-hook.d.ts.map +1 -1
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +653 -139
- package/dist/memory-tools.d.ts.map +1 -1
- package/dist/memory.d.ts +5 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/observability-tools.d.ts +116 -0
- package/dist/observability-tools.d.ts.map +1 -0
- package/dist/plugin.js +648 -136
- package/dist/skills.d.ts.map +1 -1
- package/dist/swarm-orchestrate.d.ts +29 -5
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts +66 -0
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm.d.ts +17 -2
- package/dist/swarm.d.ts.map +1 -1
- package/evals/lib/{data-loader.test.ts → data-loader.evalite-test.ts} +7 -6
- package/evals/lib/data-loader.ts +1 -1
- package/evals/scorers/{outcome-scorers.test.ts → outcome-scorers.evalite-test.ts} +1 -1
- package/examples/plugin-wrapper-template.ts +316 -12
- package/global-skills/swarm-coordination/SKILL.md +118 -8
- package/package.json +3 -2
- package/src/compaction-hook.ts +5 -3
- package/src/hive.integration.test.ts +83 -1
- package/src/hive.ts +37 -12
- package/src/index.ts +25 -1
- package/src/mandate-storage.integration.test.ts +601 -0
- package/src/memory-tools.ts +6 -4
- package/src/memory.integration.test.ts +117 -49
- package/src/memory.test.ts +41 -217
- package/src/memory.ts +12 -8
- package/src/observability-tools.test.ts +346 -0
- package/src/observability-tools.ts +594 -0
- package/src/repo-crawl.integration.test.ts +441 -0
- package/src/skills.integration.test.ts +1192 -0
- package/src/skills.test.ts +42 -1
- package/src/skills.ts +8 -4
- package/src/structured.integration.test.ts +817 -0
- package/src/swarm-deferred.integration.test.ts +157 -0
- package/src/swarm-deferred.test.ts +38 -0
- package/src/swarm-mail.integration.test.ts +15 -19
- package/src/swarm-orchestrate.integration.test.ts +282 -0
- package/src/swarm-orchestrate.test.ts +123 -0
- package/src/swarm-orchestrate.ts +279 -201
- package/src/swarm-prompts.test.ts +481 -0
- package/src/swarm-prompts.ts +297 -0
- package/src/swarm-research.integration.test.ts +544 -0
- package/src/swarm-research.test.ts +698 -0
- package/src/swarm-research.ts +472 -0
- package/src/swarm-review.integration.test.ts +290 -0
- package/src/swarm.integration.test.ts +23 -20
- package/src/swarm.ts +6 -3
- package/src/tool-adapter.integration.test.ts +1221 -0
package/src/swarm-orchestrate.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
getAgent,
|
|
45
45
|
createEvent,
|
|
46
46
|
appendEvent,
|
|
47
|
+
getSwarmMailLibSQL,
|
|
47
48
|
} from "swarm-mail";
|
|
48
49
|
import {
|
|
49
50
|
addStrike,
|
|
@@ -386,104 +387,9 @@ interface VerificationGateResult {
|
|
|
386
387
|
blockers: string[];
|
|
387
388
|
}
|
|
388
389
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
interface UbsScanResult {
|
|
393
|
-
exitCode: number;
|
|
394
|
-
bugs: Array<{
|
|
395
|
-
file: string;
|
|
396
|
-
line: number;
|
|
397
|
-
severity: string;
|
|
398
|
-
message: string;
|
|
399
|
-
category: string;
|
|
400
|
-
}>;
|
|
401
|
-
summary: {
|
|
402
|
-
total: number;
|
|
403
|
-
critical: number;
|
|
404
|
-
high: number;
|
|
405
|
-
medium: number;
|
|
406
|
-
low: number;
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Run UBS scan on files before completion
|
|
412
|
-
*
|
|
413
|
-
* @param files - Files to scan
|
|
414
|
-
* @returns Scan result or null if UBS not available
|
|
415
|
-
*/
|
|
416
|
-
async function runUbsScan(files: string[]): Promise<UbsScanResult | null> {
|
|
417
|
-
if (files.length === 0) {
|
|
418
|
-
return null;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Check if UBS is available first
|
|
422
|
-
const ubsAvailable = await isToolAvailable("ubs");
|
|
423
|
-
if (!ubsAvailable) {
|
|
424
|
-
warnMissingTool("ubs");
|
|
425
|
-
return null;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
try {
|
|
429
|
-
// Run UBS scan with JSON output
|
|
430
|
-
const result = await Bun.$`ubs scan ${files.join(" ")} --json`
|
|
431
|
-
.quiet()
|
|
432
|
-
.nothrow();
|
|
433
|
-
|
|
434
|
-
const output = result.stdout.toString();
|
|
435
|
-
if (!output.trim()) {
|
|
436
|
-
return {
|
|
437
|
-
exitCode: result.exitCode,
|
|
438
|
-
bugs: [],
|
|
439
|
-
summary: { total: 0, critical: 0, high: 0, medium: 0, low: 0 },
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
try {
|
|
444
|
-
const parsed = JSON.parse(output);
|
|
445
|
-
|
|
446
|
-
// Basic validation of structure
|
|
447
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
448
|
-
throw new Error("UBS output is not an object");
|
|
449
|
-
}
|
|
450
|
-
if (!Array.isArray(parsed.bugs)) {
|
|
451
|
-
console.warn("[swarm] UBS output missing bugs array, using empty");
|
|
452
|
-
}
|
|
453
|
-
if (typeof parsed.summary !== "object" || parsed.summary === null) {
|
|
454
|
-
console.warn("[swarm] UBS output missing summary object, using empty");
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
exitCode: result.exitCode,
|
|
459
|
-
bugs: Array.isArray(parsed.bugs) ? parsed.bugs : [],
|
|
460
|
-
summary: parsed.summary || {
|
|
461
|
-
total: 0,
|
|
462
|
-
critical: 0,
|
|
463
|
-
high: 0,
|
|
464
|
-
medium: 0,
|
|
465
|
-
low: 0,
|
|
466
|
-
},
|
|
467
|
-
};
|
|
468
|
-
} catch (error) {
|
|
469
|
-
// UBS output wasn't JSON - this is an error condition
|
|
470
|
-
console.error(
|
|
471
|
-
`[swarm] CRITICAL: UBS scan failed to parse JSON output because output is malformed:`,
|
|
472
|
-
error,
|
|
473
|
-
);
|
|
474
|
-
console.error(
|
|
475
|
-
`[swarm] Raw output: ${output}. Try: Run 'ubs doctor' to check installation, verify UBS version with 'ubs --version' (need v1.0.0+), or check if UBS supports --json flag.`,
|
|
476
|
-
);
|
|
477
|
-
return {
|
|
478
|
-
exitCode: result.exitCode,
|
|
479
|
-
bugs: [],
|
|
480
|
-
summary: { total: 0, critical: 0, high: 0, medium: 0, low: 0 },
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
} catch {
|
|
484
|
-
return null;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
390
|
+
// NOTE: UBS scan (runUbsScan, UbsScanResult) removed in v0.31
|
|
391
|
+
// It was slowing down completion without proportional value.
|
|
392
|
+
// Run UBS manually if needed: ubs scan <files>
|
|
487
393
|
|
|
488
394
|
/**
|
|
489
395
|
* Run typecheck verification
|
|
@@ -608,51 +514,22 @@ async function runTestVerification(
|
|
|
608
514
|
* Run the full Verification Gate
|
|
609
515
|
*
|
|
610
516
|
* Implements the Gate Function (IDENTIFY → RUN → READ → VERIFY → CLAIM):
|
|
611
|
-
* 1.
|
|
612
|
-
* 2.
|
|
613
|
-
*
|
|
517
|
+
* 1. Typecheck
|
|
518
|
+
* 2. Tests for touched files
|
|
519
|
+
*
|
|
520
|
+
* NOTE: UBS scan was removed in v0.31 - it was slowing down completion
|
|
521
|
+
* without providing proportional value. Run UBS manually if needed.
|
|
614
522
|
*
|
|
615
523
|
* All steps must pass (or be skipped with valid reason) to proceed.
|
|
616
524
|
*/
|
|
617
525
|
async function runVerificationGate(
|
|
618
526
|
filesTouched: string[],
|
|
619
|
-
|
|
527
|
+
_skipUbs: boolean = false, // Kept for backward compatibility, now ignored
|
|
620
528
|
): Promise<VerificationGateResult> {
|
|
621
529
|
const steps: VerificationStep[] = [];
|
|
622
530
|
const blockers: string[] = [];
|
|
623
531
|
|
|
624
|
-
// Step 1: UBS scan
|
|
625
|
-
if (!skipUbs && filesTouched.length > 0) {
|
|
626
|
-
const ubsResult = await runUbsScan(filesTouched);
|
|
627
|
-
if (ubsResult) {
|
|
628
|
-
const ubsStep: VerificationStep = {
|
|
629
|
-
name: "ubs_scan",
|
|
630
|
-
command: `ubs scan ${filesTouched.join(" ")}`,
|
|
631
|
-
passed: ubsResult.summary.critical === 0,
|
|
632
|
-
exitCode: ubsResult.exitCode,
|
|
633
|
-
};
|
|
634
|
-
|
|
635
|
-
if (!ubsStep.passed) {
|
|
636
|
-
ubsStep.error = `Found ${ubsResult.summary.critical} critical bugs`;
|
|
637
|
-
blockers.push(
|
|
638
|
-
`UBS found ${ubsResult.summary.critical} critical bug(s). Try: Run 'ubs scan ${filesTouched.join(" ")}' to see details, fix critical bugs in reported files, or use skip_ubs_scan=true to bypass (not recommended).`,
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
steps.push(ubsStep);
|
|
643
|
-
} else {
|
|
644
|
-
steps.push({
|
|
645
|
-
name: "ubs_scan",
|
|
646
|
-
command: "ubs scan",
|
|
647
|
-
passed: true,
|
|
648
|
-
exitCode: 0,
|
|
649
|
-
skipped: true,
|
|
650
|
-
skipReason: "UBS not available",
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// Step 2: Typecheck
|
|
532
|
+
// Step 1: Typecheck (UBS scan removed in v0.31)
|
|
656
533
|
const typecheckStep = await runTypecheckVerification();
|
|
657
534
|
steps.push(typecheckStep);
|
|
658
535
|
if (!typecheckStep.passed && !typecheckStep.skipped) {
|
|
@@ -1224,11 +1101,39 @@ export const swarm_broadcast = tool({
|
|
|
1224
1101
|
* 4. VERIFY: All checks must pass
|
|
1225
1102
|
* 5. ONLY THEN: Close the cell
|
|
1226
1103
|
*
|
|
1227
|
-
* Closes cell, releases reservations, notifies coordinator
|
|
1104
|
+
* Closes cell, releases reservations, notifies coordinator, and resolves
|
|
1105
|
+
* a DurableDeferred keyed by bead_id for cross-agent task completion signaling.
|
|
1106
|
+
*
|
|
1107
|
+
* ## DurableDeferred Integration
|
|
1108
|
+
*
|
|
1109
|
+
* When a coordinator spawns workers, it can create a deferred BEFORE spawning:
|
|
1110
|
+
*
|
|
1111
|
+
* ```typescript
|
|
1112
|
+
* const swarmMail = await getSwarmMailLibSQL(projectPath);
|
|
1113
|
+
* const db = await swarmMail.getDatabase();
|
|
1114
|
+
*
|
|
1115
|
+
* // Create deferred keyed by bead_id
|
|
1116
|
+
* const deferredUrl = `deferred:${beadId}`;
|
|
1117
|
+
* await db.query(
|
|
1118
|
+
* `INSERT INTO deferred (url, resolved, expires_at, created_at) VALUES (?, 0, ?, ?)`,
|
|
1119
|
+
* [deferredUrl, Date.now() + 3600000, Date.now()]
|
|
1120
|
+
* );
|
|
1121
|
+
*
|
|
1122
|
+
* // Spawn worker (swarm_spawn_subtask...)
|
|
1123
|
+
*
|
|
1124
|
+
* // Await completion
|
|
1125
|
+
* const result = await db.query<{ value: string }>(
|
|
1126
|
+
* `SELECT value FROM deferred WHERE url = ? AND resolved = 1`,
|
|
1127
|
+
* [deferredUrl]
|
|
1128
|
+
* );
|
|
1129
|
+
* ```
|
|
1130
|
+
*
|
|
1131
|
+
* When the worker calls swarm_complete, it resolves the deferred automatically.
|
|
1132
|
+
* Coordinator can await without polling.
|
|
1228
1133
|
*/
|
|
1229
1134
|
export const swarm_complete = tool({
|
|
1230
1135
|
description:
|
|
1231
|
-
"Mark subtask complete with Verification Gate. Runs
|
|
1136
|
+
"Mark subtask complete with Verification Gate. Runs typecheck and tests before allowing completion.",
|
|
1232
1137
|
args: {
|
|
1233
1138
|
project_key: tool.schema.string().describe("Project path"),
|
|
1234
1139
|
agent_name: tool.schema.string().describe("Your Agent Mail name"),
|
|
@@ -1241,16 +1146,12 @@ export const swarm_complete = tool({
|
|
|
1241
1146
|
files_touched: tool.schema
|
|
1242
1147
|
.array(tool.schema.string())
|
|
1243
1148
|
.optional()
|
|
1244
|
-
.describe("Files modified - will be verified (
|
|
1245
|
-
skip_ubs_scan: tool.schema
|
|
1246
|
-
.boolean()
|
|
1247
|
-
.optional()
|
|
1248
|
-
.describe("Skip UBS bug scan (default: false)"),
|
|
1149
|
+
.describe("Files modified - will be verified (typecheck, tests)"),
|
|
1249
1150
|
skip_verification: tool.schema
|
|
1250
1151
|
.boolean()
|
|
1251
1152
|
.optional()
|
|
1252
1153
|
.describe(
|
|
1253
|
-
"Skip ALL verification (
|
|
1154
|
+
"Skip ALL verification (typecheck, tests). Use sparingly! (default: false)",
|
|
1254
1155
|
),
|
|
1255
1156
|
planned_files: tool.schema
|
|
1256
1157
|
.array(tool.schema.string())
|
|
@@ -1397,7 +1298,7 @@ Continuing with completion, but this should be fixed for future subtasks.`;
|
|
|
1397
1298
|
if (!args.skip_verification && args.files_touched?.length) {
|
|
1398
1299
|
verificationResult = await runVerificationGate(
|
|
1399
1300
|
args.files_touched,
|
|
1400
|
-
|
|
1301
|
+
false,
|
|
1401
1302
|
);
|
|
1402
1303
|
|
|
1403
1304
|
// Block completion if verification failed
|
|
@@ -1431,39 +1332,9 @@ Continuing with completion, but this should be fixed for future subtasks.`;
|
|
|
1431
1332
|
}
|
|
1432
1333
|
}
|
|
1433
1334
|
|
|
1434
|
-
// Legacy UBS-only path
|
|
1435
|
-
|
|
1436
|
-
if
|
|
1437
|
-
!args.skip_verification &&
|
|
1438
|
-
!verificationResult &&
|
|
1439
|
-
args.files_touched?.length &&
|
|
1440
|
-
!args.skip_ubs_scan
|
|
1441
|
-
) {
|
|
1442
|
-
ubsResult = await runUbsScan(args.files_touched);
|
|
1443
|
-
|
|
1444
|
-
// Block completion if critical bugs found
|
|
1445
|
-
if (ubsResult && ubsResult.summary.critical > 0) {
|
|
1446
|
-
return JSON.stringify(
|
|
1447
|
-
{
|
|
1448
|
-
success: false,
|
|
1449
|
-
error: `UBS found ${ubsResult.summary.critical} critical bug(s) that must be fixed before completing`,
|
|
1450
|
-
ubs_scan: {
|
|
1451
|
-
critical_count: ubsResult.summary.critical,
|
|
1452
|
-
bugs: ubsResult.bugs.filter((b) => b.severity === "critical"),
|
|
1453
|
-
},
|
|
1454
|
-
hint: `Fix these critical bugs: ${ubsResult.bugs
|
|
1455
|
-
.filter((b) => b.severity === "critical")
|
|
1456
|
-
.map((b) => `${b.file}:${b.line} - ${b.message}`)
|
|
1457
|
-
.slice(0, 3)
|
|
1458
|
-
.join(
|
|
1459
|
-
"; ",
|
|
1460
|
-
)}. Try: Run 'ubs scan ${args.files_touched?.join(" ") || "."} --json' for full report, fix reported issues, or use skip_ubs_scan=true to bypass (not recommended).`,
|
|
1461
|
-
},
|
|
1462
|
-
null,
|
|
1463
|
-
2,
|
|
1464
|
-
);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1335
|
+
// NOTE: Legacy UBS-only path removed in v0.31
|
|
1336
|
+
// UBS scan was slowing down completion without proportional value.
|
|
1337
|
+
// Run UBS manually if needed: ubs scan <files>
|
|
1467
1338
|
|
|
1468
1339
|
// Contract Validation - check files_touched against WorkerHandoff contract
|
|
1469
1340
|
let contractValidation: { valid: boolean; violations: string[] } | null = null;
|
|
@@ -1570,6 +1441,47 @@ This will be recorded as a negative learning signal.`;
|
|
|
1570
1441
|
);
|
|
1571
1442
|
}
|
|
1572
1443
|
|
|
1444
|
+
// Resolve DurableDeferred for cross-agent task completion signaling
|
|
1445
|
+
// This allows coordinator to await worker completion without polling
|
|
1446
|
+
let deferredResolved = false;
|
|
1447
|
+
let deferredError: string | undefined;
|
|
1448
|
+
try {
|
|
1449
|
+
const swarmMail = await getSwarmMailLibSQL(args.project_key);
|
|
1450
|
+
const db = await swarmMail.getDatabase();
|
|
1451
|
+
|
|
1452
|
+
// Resolve deferred keyed by bead_id
|
|
1453
|
+
// Coordinator should have created this deferred before spawning worker
|
|
1454
|
+
const deferredUrl = `deferred:${args.bead_id}`;
|
|
1455
|
+
|
|
1456
|
+
// Check if deferred exists before resolving
|
|
1457
|
+
const checkResult = await db.query<{ url: string; resolved: number }>(
|
|
1458
|
+
`SELECT url, resolved FROM deferred WHERE url = ? AND resolved = 0`,
|
|
1459
|
+
[deferredUrl],
|
|
1460
|
+
);
|
|
1461
|
+
|
|
1462
|
+
if (checkResult.rows.length > 0) {
|
|
1463
|
+
// Resolve with completion payload
|
|
1464
|
+
await db.query(
|
|
1465
|
+
`UPDATE deferred SET resolved = 1, value = ? WHERE url = ? AND resolved = 0`,
|
|
1466
|
+
[JSON.stringify({ completed: true, summary: args.summary }), deferredUrl],
|
|
1467
|
+
);
|
|
1468
|
+
|
|
1469
|
+
deferredResolved = true;
|
|
1470
|
+
} else {
|
|
1471
|
+
// Deferred doesn't exist - worker was likely not spawned via swarm pattern
|
|
1472
|
+
// This is non-fatal - just log for debugging
|
|
1473
|
+
console.info(
|
|
1474
|
+
`[swarm_complete] No deferred found for ${args.bead_id} - task may not be part of active swarm`,
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
// Non-fatal - deferred resolution is optional for backward compatibility
|
|
1479
|
+
deferredError = error instanceof Error ? error.message : String(error);
|
|
1480
|
+
console.warn(
|
|
1481
|
+
`[swarm_complete] Failed to resolve deferred (non-fatal): ${deferredError}`,
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1573
1485
|
// Sync cell to .hive/issues.jsonl (auto-sync on complete)
|
|
1574
1486
|
// This ensures the worker's completed work persists before process exits
|
|
1575
1487
|
let syncSuccess = false;
|
|
@@ -1737,6 +1649,8 @@ This will be recorded as a negative learning signal.`;
|
|
|
1737
1649
|
sync_error: syncError,
|
|
1738
1650
|
message_sent: messageSent,
|
|
1739
1651
|
message_error: messageError,
|
|
1652
|
+
deferred_resolved: deferredResolved,
|
|
1653
|
+
deferred_error: deferredError,
|
|
1740
1654
|
agent_registration: {
|
|
1741
1655
|
verified: agentRegistered,
|
|
1742
1656
|
warning: registrationWarning || undefined,
|
|
@@ -1755,21 +1669,6 @@ This will be recorded as a negative learning signal.`;
|
|
|
1755
1669
|
: args.skip_verification
|
|
1756
1670
|
? { skipped: true, reason: "skip_verification=true" }
|
|
1757
1671
|
: { skipped: true, reason: "no files_touched provided" },
|
|
1758
|
-
ubs_scan: ubsResult
|
|
1759
|
-
? {
|
|
1760
|
-
ran: true,
|
|
1761
|
-
bugs_found: ubsResult.summary.total,
|
|
1762
|
-
summary: ubsResult.summary,
|
|
1763
|
-
warnings: ubsResult.bugs.filter((b) => b.severity !== "critical"),
|
|
1764
|
-
}
|
|
1765
|
-
: verificationResult
|
|
1766
|
-
? { ran: true, included_in_verification_gate: true }
|
|
1767
|
-
: {
|
|
1768
|
-
ran: false,
|
|
1769
|
-
reason: args.skip_ubs_scan
|
|
1770
|
-
? "skipped"
|
|
1771
|
-
: "no files or ubs unavailable",
|
|
1772
|
-
},
|
|
1773
1672
|
learning_prompt: `## Reflection
|
|
1774
1673
|
|
|
1775
1674
|
Did you learn anything reusable during this subtask? Consider:
|
|
@@ -1820,9 +1719,7 @@ Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
|
|
|
1820
1719
|
// Determine which step failed
|
|
1821
1720
|
let failedStep = "unknown";
|
|
1822
1721
|
if (errorMessage.includes("verification")) {
|
|
1823
|
-
failedStep = "Verification Gate (
|
|
1824
|
-
} else if (errorMessage.includes("UBS") || errorMessage.includes("ubs")) {
|
|
1825
|
-
failedStep = "UBS scan";
|
|
1722
|
+
failedStep = "Verification Gate (typecheck/tests)";
|
|
1826
1723
|
} else if (errorMessage.includes("evaluation")) {
|
|
1827
1724
|
failedStep = "Self-evaluation parsing";
|
|
1828
1725
|
} else if (
|
|
@@ -1863,10 +1760,9 @@ Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
|
|
|
1863
1760
|
errorStack
|
|
1864
1761
|
? `### Stack Trace\n\`\`\`\n${errorStack.slice(0, 1000)}\n\`\`\`\n`
|
|
1865
1762
|
: "",
|
|
1866
|
-
|
|
1763
|
+
`### Context`,
|
|
1867
1764
|
`- **Summary**: ${args.summary}`,
|
|
1868
1765
|
`- **Files touched**: ${args.files_touched?.length ? args.files_touched.join(", ") : "none"}`,
|
|
1869
|
-
`- **Skip UBS**: ${args.skip_ubs_scan ?? false}`,
|
|
1870
1766
|
`- **Skip verification**: ${args.skip_verification ?? false}`,
|
|
1871
1767
|
"",
|
|
1872
1768
|
`### Recovery Actions`,
|
|
@@ -1912,10 +1808,9 @@ Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
|
|
|
1912
1808
|
coordinator_notified: notificationSent,
|
|
1913
1809
|
stack_trace: errorStack?.slice(0, 500),
|
|
1914
1810
|
hint: "Check the error message above. Common issues: bead not found, session not initialized.",
|
|
1915
|
-
|
|
1811
|
+
context: {
|
|
1916
1812
|
summary: args.summary,
|
|
1917
1813
|
files_touched: args.files_touched || [],
|
|
1918
|
-
skip_ubs_scan: args.skip_ubs_scan ?? false,
|
|
1919
1814
|
skip_verification: args.skip_verification ?? false,
|
|
1920
1815
|
},
|
|
1921
1816
|
recovery: {
|
|
@@ -1927,7 +1822,6 @@ Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
|
|
|
1927
1822
|
],
|
|
1928
1823
|
common_fixes: {
|
|
1929
1824
|
"Verification Gate": "Use skip_verification=true to bypass (not recommended)",
|
|
1930
|
-
"UBS scan": "Use skip_ubs_scan=true to bypass",
|
|
1931
1825
|
"Cell close": "Check cell status with hive_query(), may need hive_update() first",
|
|
1932
1826
|
"Self-evaluation": "Check evaluation JSON format matches EvaluationSchema",
|
|
1933
1827
|
},
|
|
@@ -2100,6 +1994,188 @@ export const swarm_record_outcome = tool({
|
|
|
2100
1994
|
},
|
|
2101
1995
|
});
|
|
2102
1996
|
|
|
1997
|
+
// ============================================================================
|
|
1998
|
+
// Research Phase
|
|
1999
|
+
// ============================================================================
|
|
2000
|
+
|
|
2001
|
+
/**
|
|
2002
|
+
* Known technology patterns for extraction from task descriptions
|
|
2003
|
+
* Maps common mentions to normalized package names
|
|
2004
|
+
*/
|
|
2005
|
+
const TECH_PATTERNS: Record<string, RegExp> = {
|
|
2006
|
+
next: /next\.?js|nextjs/i,
|
|
2007
|
+
react: /react(?!ive)/i,
|
|
2008
|
+
zod: /zod/i,
|
|
2009
|
+
typescript: /typescript|ts(?!\w)/i,
|
|
2010
|
+
tailwind: /tailwind(?:css)?/i,
|
|
2011
|
+
prisma: /prisma/i,
|
|
2012
|
+
drizzle: /drizzle(?:-orm)?/i,
|
|
2013
|
+
trpc: /trpc/i,
|
|
2014
|
+
"react-query": /react-query|tanstack.*query/i,
|
|
2015
|
+
axios: /axios/i,
|
|
2016
|
+
"node-fetch": /node-fetch|fetch api/i,
|
|
2017
|
+
};
|
|
2018
|
+
|
|
2019
|
+
/**
|
|
2020
|
+
* Extract technology stack from task description
|
|
2021
|
+
*
|
|
2022
|
+
* Searches for common framework/library mentions and returns
|
|
2023
|
+
* a deduplicated array of normalized names.
|
|
2024
|
+
*
|
|
2025
|
+
* @param task - Task description
|
|
2026
|
+
* @returns Array of detected technology names (normalized, lowercase)
|
|
2027
|
+
*
|
|
2028
|
+
* @example
|
|
2029
|
+
* ```typescript
|
|
2030
|
+
* extractTechStack("Add Next.js API routes with Zod validation")
|
|
2031
|
+
* // => ["next", "zod"]
|
|
2032
|
+
* ```
|
|
2033
|
+
*/
|
|
2034
|
+
export function extractTechStack(task: string): string[] {
|
|
2035
|
+
const detected = new Set<string>();
|
|
2036
|
+
|
|
2037
|
+
for (const [tech, pattern] of Object.entries(TECH_PATTERNS)) {
|
|
2038
|
+
if (pattern.test(task)) {
|
|
2039
|
+
detected.add(tech);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
return Array.from(detected);
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
/**
|
|
2047
|
+
* Research result from documentation discovery phase
|
|
2048
|
+
*/
|
|
2049
|
+
export interface ResearchResult {
|
|
2050
|
+
/** Technologies identified and researched */
|
|
2051
|
+
tech_stack: string[];
|
|
2052
|
+
/** Summaries keyed by technology name */
|
|
2053
|
+
summaries: Record<string, string>;
|
|
2054
|
+
/** Semantic-memory IDs where research is stored */
|
|
2055
|
+
memory_ids: string[];
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
/**
|
|
2059
|
+
* Run research phase before task decomposition
|
|
2060
|
+
*
|
|
2061
|
+
* This is the INTEGRATION point that:
|
|
2062
|
+
* 1. Analyzes task to identify technologies
|
|
2063
|
+
* 2. Spawns researcher agents for each technology (parallel)
|
|
2064
|
+
* 3. Waits for researchers to complete
|
|
2065
|
+
* 4. Collects summaries from semantic-memory
|
|
2066
|
+
* 5. Returns combined context for shared_context
|
|
2067
|
+
*
|
|
2068
|
+
* Flow:
|
|
2069
|
+
* ```
|
|
2070
|
+
* Task received
|
|
2071
|
+
* ↓
|
|
2072
|
+
* extractTechStack(task) → ["next", "zod"]
|
|
2073
|
+
* ↓
|
|
2074
|
+
* For each tech: swarm_spawn_researcher(tech_stack=[tech])
|
|
2075
|
+
* ↓
|
|
2076
|
+
* Spawn Task agents in parallel
|
|
2077
|
+
* ↓
|
|
2078
|
+
* Wait for all to complete
|
|
2079
|
+
* ↓
|
|
2080
|
+
* Collect summaries from swarm mail
|
|
2081
|
+
* ↓
|
|
2082
|
+
* Return ResearchResult → inject into shared_context
|
|
2083
|
+
* ```
|
|
2084
|
+
*
|
|
2085
|
+
* @param task - Task description to analyze
|
|
2086
|
+
* @param projectPath - Absolute path to project root
|
|
2087
|
+
* @param options - Optional configuration
|
|
2088
|
+
* @returns Research results with summaries and memory IDs
|
|
2089
|
+
*
|
|
2090
|
+
* @example
|
|
2091
|
+
* ```typescript
|
|
2092
|
+
* const result = await runResearchPhase(
|
|
2093
|
+
* "Add Next.js API routes with Zod validation",
|
|
2094
|
+
* "/path/to/project"
|
|
2095
|
+
* );
|
|
2096
|
+
* // result.tech_stack => ["next", "zod"]
|
|
2097
|
+
* // result.summaries => { next: "...", zod: "..." }
|
|
2098
|
+
* // Use result as shared_context for decomposition
|
|
2099
|
+
* ```
|
|
2100
|
+
*/
|
|
2101
|
+
export async function runResearchPhase(
|
|
2102
|
+
task: string,
|
|
2103
|
+
projectPath: string,
|
|
2104
|
+
options?: { checkUpgrades?: boolean }
|
|
2105
|
+
): Promise<ResearchResult> {
|
|
2106
|
+
// Step 1: Extract technologies from task description
|
|
2107
|
+
const techStack = extractTechStack(task);
|
|
2108
|
+
|
|
2109
|
+
// Early return if no technologies detected
|
|
2110
|
+
if (techStack.length === 0) {
|
|
2111
|
+
return {
|
|
2112
|
+
tech_stack: [],
|
|
2113
|
+
summaries: {},
|
|
2114
|
+
memory_ids: [],
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
// Step 2: For each technology, spawn a researcher
|
|
2119
|
+
// TODO: Implement researcher spawning using swarm_spawn_researcher
|
|
2120
|
+
// and Task tool. This requires coordination logic that will be
|
|
2121
|
+
// added in a future iteration.
|
|
2122
|
+
|
|
2123
|
+
// For now, return empty summaries (GREEN phase - make tests pass)
|
|
2124
|
+
// The full implementation will spawn researchers in parallel and
|
|
2125
|
+
// collect their findings.
|
|
2126
|
+
|
|
2127
|
+
return {
|
|
2128
|
+
tech_stack: techStack,
|
|
2129
|
+
summaries: {},
|
|
2130
|
+
memory_ids: [],
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
/**
|
|
2135
|
+
* Plugin tool for running research phase
|
|
2136
|
+
*
|
|
2137
|
+
* Exposes research phase as a tool for manual triggering or
|
|
2138
|
+
* integration into orchestration flows.
|
|
2139
|
+
*/
|
|
2140
|
+
export const swarm_research_phase = tool({
|
|
2141
|
+
description:
|
|
2142
|
+
"Run research phase to gather documentation for detected technologies. Returns summaries for injection into shared_context.",
|
|
2143
|
+
args: {
|
|
2144
|
+
task: tool.schema.string().min(1).describe("Task description to analyze"),
|
|
2145
|
+
project_path: tool.schema
|
|
2146
|
+
.string()
|
|
2147
|
+
.describe("Absolute path to project root"),
|
|
2148
|
+
check_upgrades: tool.schema
|
|
2149
|
+
.boolean()
|
|
2150
|
+
.optional()
|
|
2151
|
+
.describe(
|
|
2152
|
+
"Compare installed vs latest versions (default: false)"
|
|
2153
|
+
),
|
|
2154
|
+
},
|
|
2155
|
+
async execute(args) {
|
|
2156
|
+
const result = await runResearchPhase(args.task, args.project_path, {
|
|
2157
|
+
checkUpgrades: args.check_upgrades,
|
|
2158
|
+
});
|
|
2159
|
+
|
|
2160
|
+
return JSON.stringify(
|
|
2161
|
+
{
|
|
2162
|
+
tech_stack: result.tech_stack,
|
|
2163
|
+
summaries: result.summaries,
|
|
2164
|
+
memory_ids: result.memory_ids,
|
|
2165
|
+
summary: {
|
|
2166
|
+
technologies_detected: result.tech_stack.length,
|
|
2167
|
+
summaries_collected: Object.keys(result.summaries).length,
|
|
2168
|
+
memories_stored: result.memory_ids.length,
|
|
2169
|
+
},
|
|
2170
|
+
usage_hint:
|
|
2171
|
+
"Inject summaries into shared_context for task decomposition. Each technology has documentation in semantic-memory.",
|
|
2172
|
+
},
|
|
2173
|
+
null,
|
|
2174
|
+
2
|
|
2175
|
+
);
|
|
2176
|
+
},
|
|
2177
|
+
});
|
|
2178
|
+
|
|
2103
2179
|
/**
|
|
2104
2180
|
* Record an error during subtask execution
|
|
2105
2181
|
*
|
|
@@ -2554,8 +2630,9 @@ export const swarm_recover = tool({
|
|
|
2554
2630
|
},
|
|
2555
2631
|
async execute(args) {
|
|
2556
2632
|
try {
|
|
2557
|
-
const {
|
|
2558
|
-
const
|
|
2633
|
+
const { getSwarmMailLibSQL } = await import("swarm-mail");
|
|
2634
|
+
const swarmMail = await getSwarmMailLibSQL(args.project_key);
|
|
2635
|
+
const db = await swarmMail.getDatabase();
|
|
2559
2636
|
|
|
2560
2637
|
// Query most recent checkpoint for this epic
|
|
2561
2638
|
const result = await db.query<{
|
|
@@ -2863,6 +2940,7 @@ export const orchestrateTools = {
|
|
|
2863
2940
|
swarm_broadcast,
|
|
2864
2941
|
swarm_complete,
|
|
2865
2942
|
swarm_record_outcome,
|
|
2943
|
+
swarm_research_phase,
|
|
2866
2944
|
swarm_accumulate_error,
|
|
2867
2945
|
swarm_get_error_context,
|
|
2868
2946
|
swarm_resolve_error,
|