opencode-swarm-plugin 0.48.0 → 0.49.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 +10 -7
- package/bin/swarm-setup-consolidate.test.ts +84 -0
- package/bin/swarm.test.ts +170 -0
- package/bin/swarm.ts +300 -2
- package/bin/test-setup-manual.md +67 -0
- package/dist/bin/swarm.js +1055 -188
- package/dist/coordinator-guard.d.ts +79 -0
- package/dist/coordinator-guard.d.ts.map +1 -0
- package/dist/examples/plugin-wrapper-template.ts +13 -2
- package/dist/hive.d.ts.map +1 -1
- package/dist/hive.js +5 -4
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +386 -18
- package/dist/memory.d.ts.map +1 -1
- package/dist/plugin.js +383 -18
- package/dist/query-tools.d.ts +5 -0
- package/dist/query-tools.d.ts.map +1 -1
- package/dist/schemas/cell.d.ts +12 -0
- package/dist/schemas/cell.d.ts.map +1 -1
- package/dist/swarm-insights.d.ts +158 -0
- package/dist/swarm-insights.d.ts.map +1 -1
- package/dist/swarm-orchestrate.d.ts +4 -4
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-prompts.js +345 -18
- package/dist/swarm-strategies.d.ts +1 -1
- package/dist/swarm.d.ts +2 -2
- package/examples/plugin-wrapper-template.ts +13 -2
- package/package.json +2 -2
package/dist/swarm-prompts.js
CHANGED
|
@@ -18210,11 +18210,16 @@ echo "Project directory: $1"
|
|
|
18210
18210
|
// src/swarm-insights.ts
|
|
18211
18211
|
var exports_swarm_insights = {};
|
|
18212
18212
|
__export(exports_swarm_insights, {
|
|
18213
|
+
trackCoordinatorViolation: () => trackCoordinatorViolation,
|
|
18214
|
+
getViolationAnalytics: () => getViolationAnalytics,
|
|
18213
18215
|
getStrategyInsights: () => getStrategyInsights,
|
|
18216
|
+
getRejectionAnalytics: () => getRejectionAnalytics,
|
|
18214
18217
|
getPatternInsights: () => getPatternInsights,
|
|
18215
18218
|
getFileInsights: () => getFileInsights,
|
|
18219
|
+
getFileFailureHistory: () => getFileFailureHistory,
|
|
18216
18220
|
getCachedInsights: () => getCachedInsights,
|
|
18217
18221
|
formatInsightsForPrompt: () => formatInsightsForPrompt,
|
|
18222
|
+
formatFileHistoryWarnings: () => formatFileHistoryWarnings,
|
|
18218
18223
|
clearInsightsCache: () => clearInsightsCache
|
|
18219
18224
|
});
|
|
18220
18225
|
async function getStrategyInsights(swarmMail, _task) {
|
|
@@ -18286,6 +18291,136 @@ async function getFileInsights(swarmMail, files) {
|
|
|
18286
18291
|
async function getFileGotchas(_swarmMail, _file2) {
|
|
18287
18292
|
return [];
|
|
18288
18293
|
}
|
|
18294
|
+
async function getFileFailureHistory(swarmMail, files) {
|
|
18295
|
+
if (files.length === 0)
|
|
18296
|
+
return [];
|
|
18297
|
+
const db = await swarmMail.getDatabase();
|
|
18298
|
+
const histories = [];
|
|
18299
|
+
for (const file2 of files) {
|
|
18300
|
+
const query = `
|
|
18301
|
+
SELECT data
|
|
18302
|
+
FROM events
|
|
18303
|
+
WHERE type = 'review_feedback'
|
|
18304
|
+
AND json_extract(data, '$.status') = 'needs_changes'
|
|
18305
|
+
AND json_extract(data, '$.issues') LIKE ?
|
|
18306
|
+
`;
|
|
18307
|
+
const result = await db.query(query, [`%${file2}%`]);
|
|
18308
|
+
if (!result.rows || result.rows.length === 0) {
|
|
18309
|
+
continue;
|
|
18310
|
+
}
|
|
18311
|
+
const issueTexts = [];
|
|
18312
|
+
for (const row of result.rows) {
|
|
18313
|
+
try {
|
|
18314
|
+
const data = JSON.parse(row.data);
|
|
18315
|
+
const issuesStr = data.issues;
|
|
18316
|
+
if (!issuesStr)
|
|
18317
|
+
continue;
|
|
18318
|
+
const issues = JSON.parse(issuesStr);
|
|
18319
|
+
for (const issue2 of issues) {
|
|
18320
|
+
if (issue2.file === file2) {
|
|
18321
|
+
issueTexts.push(issue2.issue);
|
|
18322
|
+
}
|
|
18323
|
+
}
|
|
18324
|
+
} catch (e) {
|
|
18325
|
+
continue;
|
|
18326
|
+
}
|
|
18327
|
+
}
|
|
18328
|
+
if (issueTexts.length === 0) {
|
|
18329
|
+
continue;
|
|
18330
|
+
}
|
|
18331
|
+
const issueCounts = new Map;
|
|
18332
|
+
for (const text of issueTexts) {
|
|
18333
|
+
issueCounts.set(text, (issueCounts.get(text) || 0) + 1);
|
|
18334
|
+
}
|
|
18335
|
+
const topIssues = Array.from(issueCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([text]) => text);
|
|
18336
|
+
histories.push({
|
|
18337
|
+
file: file2,
|
|
18338
|
+
rejectionCount: issueTexts.length,
|
|
18339
|
+
topIssues
|
|
18340
|
+
});
|
|
18341
|
+
}
|
|
18342
|
+
return histories;
|
|
18343
|
+
}
|
|
18344
|
+
async function getRejectionAnalytics(swarmMail) {
|
|
18345
|
+
const db = await swarmMail.getDatabase();
|
|
18346
|
+
const query = `
|
|
18347
|
+
SELECT data
|
|
18348
|
+
FROM events
|
|
18349
|
+
WHERE type = 'review_feedback'
|
|
18350
|
+
ORDER BY timestamp DESC
|
|
18351
|
+
`;
|
|
18352
|
+
const result = await db.query(query, []);
|
|
18353
|
+
if (!result.rows || result.rows.length === 0) {
|
|
18354
|
+
return {
|
|
18355
|
+
totalReviews: 0,
|
|
18356
|
+
approved: 0,
|
|
18357
|
+
rejected: 0,
|
|
18358
|
+
approvalRate: 0,
|
|
18359
|
+
topReasons: []
|
|
18360
|
+
};
|
|
18361
|
+
}
|
|
18362
|
+
let approved = 0;
|
|
18363
|
+
let rejected = 0;
|
|
18364
|
+
const reasonCounts = new Map;
|
|
18365
|
+
for (const row of result.rows) {
|
|
18366
|
+
try {
|
|
18367
|
+
const data = JSON.parse(row.data);
|
|
18368
|
+
if (data.status === "approved") {
|
|
18369
|
+
approved++;
|
|
18370
|
+
} else if (data.status === "needs_changes") {
|
|
18371
|
+
rejected++;
|
|
18372
|
+
if (data.issues) {
|
|
18373
|
+
const issues = JSON.parse(data.issues);
|
|
18374
|
+
for (const issue2 of issues) {
|
|
18375
|
+
const category = categorizeRejectionReason(issue2.issue);
|
|
18376
|
+
reasonCounts.set(category, (reasonCounts.get(category) || 0) + 1);
|
|
18377
|
+
}
|
|
18378
|
+
}
|
|
18379
|
+
}
|
|
18380
|
+
} catch (e) {
|
|
18381
|
+
continue;
|
|
18382
|
+
}
|
|
18383
|
+
}
|
|
18384
|
+
const totalReviews = approved + rejected;
|
|
18385
|
+
const approvalRate = totalReviews > 0 ? approved / totalReviews * 100 : 0;
|
|
18386
|
+
const topReasons = Array.from(reasonCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([category, count]) => ({
|
|
18387
|
+
category,
|
|
18388
|
+
count,
|
|
18389
|
+
percentage: rejected > 0 ? count / rejected * 100 : 0
|
|
18390
|
+
}));
|
|
18391
|
+
return {
|
|
18392
|
+
totalReviews,
|
|
18393
|
+
approved,
|
|
18394
|
+
rejected,
|
|
18395
|
+
approvalRate,
|
|
18396
|
+
topReasons
|
|
18397
|
+
};
|
|
18398
|
+
}
|
|
18399
|
+
function categorizeRejectionReason(reason) {
|
|
18400
|
+
const lowerReason = reason.toLowerCase();
|
|
18401
|
+
if (lowerReason.includes("test") || lowerReason.includes("spec") || lowerReason.includes("coverage")) {
|
|
18402
|
+
return "Missing tests";
|
|
18403
|
+
}
|
|
18404
|
+
if (lowerReason.includes("type") || lowerReason.includes("undefined") || lowerReason.includes("null") || lowerReason.includes("assignable")) {
|
|
18405
|
+
return "Type errors";
|
|
18406
|
+
}
|
|
18407
|
+
if (lowerReason.includes("incomplete") || lowerReason.includes("missing") || lowerReason.includes("forgot") || lowerReason.includes("didn't implement")) {
|
|
18408
|
+
return "Incomplete implementation";
|
|
18409
|
+
}
|
|
18410
|
+
if (lowerReason.includes("wrong file") || lowerReason.includes("modified incorrect") || lowerReason.includes("shouldn't have changed")) {
|
|
18411
|
+
return "Wrong file modified";
|
|
18412
|
+
}
|
|
18413
|
+
if (lowerReason.includes("performance") || lowerReason.includes("slow") || lowerReason.includes("inefficient")) {
|
|
18414
|
+
return "Performance issue";
|
|
18415
|
+
}
|
|
18416
|
+
if (lowerReason.includes("security") || lowerReason.includes("vulnerability") || lowerReason.includes("unsafe")) {
|
|
18417
|
+
return "Security vulnerability";
|
|
18418
|
+
}
|
|
18419
|
+
if (lowerReason.includes("error handling") || lowerReason.includes("try/catch") || lowerReason.includes("exception")) {
|
|
18420
|
+
return "Missing error handling";
|
|
18421
|
+
}
|
|
18422
|
+
return "Other";
|
|
18423
|
+
}
|
|
18289
18424
|
async function getPatternInsights(swarmMail) {
|
|
18290
18425
|
const db = await swarmMail.getDatabase();
|
|
18291
18426
|
const patterns = [];
|
|
@@ -18373,6 +18508,107 @@ async function getCachedInsights(_swarmMail, cacheKey, computeFn) {
|
|
|
18373
18508
|
function clearInsightsCache() {
|
|
18374
18509
|
insightsCache.clear();
|
|
18375
18510
|
}
|
|
18511
|
+
async function trackCoordinatorViolation(swarmMail, violation) {
|
|
18512
|
+
const db = await swarmMail.getDatabase();
|
|
18513
|
+
const query = `
|
|
18514
|
+
INSERT INTO events (type, project_key, timestamp, data)
|
|
18515
|
+
VALUES (?, ?, ?, ?)
|
|
18516
|
+
RETURNING id
|
|
18517
|
+
`;
|
|
18518
|
+
const data = JSON.stringify({
|
|
18519
|
+
session_id: violation.session_id,
|
|
18520
|
+
epic_id: violation.epic_id,
|
|
18521
|
+
event_type: "VIOLATION",
|
|
18522
|
+
violation_type: violation.violation_type,
|
|
18523
|
+
payload: violation.payload
|
|
18524
|
+
});
|
|
18525
|
+
const result = await db.query(query, [
|
|
18526
|
+
"coordinator_violation",
|
|
18527
|
+
violation.project_key,
|
|
18528
|
+
Date.now(),
|
|
18529
|
+
data
|
|
18530
|
+
]);
|
|
18531
|
+
return result.rows[0].id;
|
|
18532
|
+
}
|
|
18533
|
+
async function getViolationAnalytics(swarmMail, projectKey) {
|
|
18534
|
+
const db = await swarmMail.getDatabase();
|
|
18535
|
+
const violationsQuery = projectKey ? `
|
|
18536
|
+
SELECT data
|
|
18537
|
+
FROM events
|
|
18538
|
+
WHERE type = 'coordinator_violation'
|
|
18539
|
+
AND project_key = ?
|
|
18540
|
+
ORDER BY timestamp DESC
|
|
18541
|
+
` : `
|
|
18542
|
+
SELECT data
|
|
18543
|
+
FROM events
|
|
18544
|
+
WHERE type = 'coordinator_violation'
|
|
18545
|
+
ORDER BY timestamp DESC
|
|
18546
|
+
`;
|
|
18547
|
+
const params = projectKey ? [projectKey] : [];
|
|
18548
|
+
const result = await db.query(violationsQuery, params);
|
|
18549
|
+
if (!result.rows || result.rows.length === 0) {
|
|
18550
|
+
return {
|
|
18551
|
+
totalViolations: 0,
|
|
18552
|
+
byType: [],
|
|
18553
|
+
violationRate: 0
|
|
18554
|
+
};
|
|
18555
|
+
}
|
|
18556
|
+
const violationCounts = new Map;
|
|
18557
|
+
let totalViolations = 0;
|
|
18558
|
+
for (const row of result.rows) {
|
|
18559
|
+
try {
|
|
18560
|
+
const data = JSON.parse(row.data);
|
|
18561
|
+
const violationType = data.violation_type;
|
|
18562
|
+
if (violationType) {
|
|
18563
|
+
violationCounts.set(violationType, (violationCounts.get(violationType) || 0) + 1);
|
|
18564
|
+
totalViolations++;
|
|
18565
|
+
}
|
|
18566
|
+
} catch (e) {
|
|
18567
|
+
continue;
|
|
18568
|
+
}
|
|
18569
|
+
}
|
|
18570
|
+
const byType = Array.from(violationCounts.entries()).sort((a, b) => b[1] - a[1]).map(([violationType, count]) => ({
|
|
18571
|
+
violationType,
|
|
18572
|
+
count,
|
|
18573
|
+
percentage: count / totalViolations * 100
|
|
18574
|
+
}));
|
|
18575
|
+
const coordinationQuery = projectKey ? `
|
|
18576
|
+
SELECT COUNT(*) as count
|
|
18577
|
+
FROM events
|
|
18578
|
+
WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
|
|
18579
|
+
AND project_key = ?
|
|
18580
|
+
` : `
|
|
18581
|
+
SELECT COUNT(*) as count
|
|
18582
|
+
FROM events
|
|
18583
|
+
WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
|
|
18584
|
+
`;
|
|
18585
|
+
const coordResult = await db.query(coordinationQuery, params);
|
|
18586
|
+
const coordinationCount = coordResult.rows[0]?.count || 0;
|
|
18587
|
+
const violationRate = coordinationCount > 0 ? totalViolations / coordinationCount * 100 : 0;
|
|
18588
|
+
return {
|
|
18589
|
+
totalViolations,
|
|
18590
|
+
byType,
|
|
18591
|
+
violationRate
|
|
18592
|
+
};
|
|
18593
|
+
}
|
|
18594
|
+
function formatFileHistoryWarnings(histories) {
|
|
18595
|
+
if (histories.length === 0) {
|
|
18596
|
+
return "";
|
|
18597
|
+
}
|
|
18598
|
+
const lines = ["⚠️ FILE HISTORY WARNINGS:"];
|
|
18599
|
+
for (const history of histories) {
|
|
18600
|
+
const workerText = history.rejectionCount === 1 ? "1 previous worker rejected" : `${history.rejectionCount} previous workers rejected`;
|
|
18601
|
+
const issuesText = history.topIssues.join(", ");
|
|
18602
|
+
lines.push(`- ${history.file}: ${workerText} for ${issuesText}`);
|
|
18603
|
+
}
|
|
18604
|
+
let result = lines.join(`
|
|
18605
|
+
`);
|
|
18606
|
+
const maxChars = 300 * 4;
|
|
18607
|
+
if (result.length > maxChars) {
|
|
18608
|
+
result = result.slice(0, maxChars - 3) + "...";
|
|
18609
|
+
}
|
|
18610
|
+
return result;
|
|
18611
|
+
}
|
|
18376
18612
|
var insightsCache, CACHE_TTL_MS;
|
|
18377
18613
|
var init_swarm_insights = __esm(() => {
|
|
18378
18614
|
insightsCache = new Map;
|
|
@@ -20281,7 +20517,8 @@ var CellTreeSchema = exports_external.object({
|
|
|
20281
20517
|
title: exports_external.string().min(1),
|
|
20282
20518
|
description: exports_external.string().optional().default("")
|
|
20283
20519
|
}),
|
|
20284
|
-
subtasks: exports_external.array(SubtaskSpecSchema).min(1)
|
|
20520
|
+
subtasks: exports_external.array(SubtaskSpecSchema).min(1),
|
|
20521
|
+
strategy: exports_external.enum(["file-based", "feature-based", "risk-based", "research-based"]).optional().describe("Decomposition strategy from swarm_select_strategy. If not provided, defaults to feature-based.")
|
|
20285
20522
|
});
|
|
20286
20523
|
var EpicCreateArgsSchema = exports_external.object({
|
|
20287
20524
|
epic_title: exports_external.string().min(1),
|
|
@@ -21188,13 +21425,13 @@ async function autoMigrateFromJSONL(adapter, projectKey) {
|
|
|
21188
21425
|
skipExisting: true
|
|
21189
21426
|
});
|
|
21190
21427
|
if (result.created > 0 || result.updated > 0) {
|
|
21191
|
-
console.
|
|
21428
|
+
console.error(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
|
|
21192
21429
|
}
|
|
21193
21430
|
if (result.errors.length > 0) {
|
|
21194
|
-
console.
|
|
21431
|
+
console.error(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
|
|
21195
21432
|
}
|
|
21196
21433
|
} catch (error45) {
|
|
21197
|
-
console.
|
|
21434
|
+
console.error(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error45 instanceof Error ? error45.message : String(error45));
|
|
21198
21435
|
}
|
|
21199
21436
|
}
|
|
21200
21437
|
function formatCellForOutput(adapterCell) {
|
|
@@ -23364,7 +23601,7 @@ var swarm_complete = tool({
|
|
|
23364
23601
|
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (typecheck, tests)"),
|
|
23365
23602
|
skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (typecheck, tests). Use sparingly! (default: false)"),
|
|
23366
23603
|
planned_files: tool.schema.array(tool.schema.string()).optional().describe("Files that were originally planned to be modified"),
|
|
23367
|
-
start_time: tool.schema.number().
|
|
23604
|
+
start_time: tool.schema.number().describe("Task start timestamp (Unix ms) for duration calculation - REQUIRED for accurate analytics"),
|
|
23368
23605
|
error_count: tool.schema.number().optional().describe("Number of errors encountered during task"),
|
|
23369
23606
|
retry_count: tool.schema.number().optional().describe("Number of retry attempts during task"),
|
|
23370
23607
|
skip_review: tool.schema.boolean().optional().describe("Skip review gate check (default: false). Use only for tasks that don't require coordinator review.")
|
|
@@ -23568,7 +23805,7 @@ This will be recorded as a negative learning signal.`;
|
|
|
23568
23805
|
syncError = error45 instanceof Error ? error45.message : String(error45);
|
|
23569
23806
|
console.warn(`[swarm_complete] Auto-sync failed (non-fatal): ${syncError}`);
|
|
23570
23807
|
}
|
|
23571
|
-
const completionDurationMs =
|
|
23808
|
+
const completionDurationMs = Date.now() - args.start_time;
|
|
23572
23809
|
const eventEpicId = cell.parent_id || (args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id);
|
|
23573
23810
|
try {
|
|
23574
23811
|
const event = createEvent3("subtask_outcome", {
|
|
@@ -23624,12 +23861,18 @@ This will be recorded as a negative learning signal.`;
|
|
|
23624
23861
|
memoryError = `Failed to store memory: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
23625
23862
|
console.warn(`[swarm_complete] ${memoryError}`);
|
|
23626
23863
|
}
|
|
23864
|
+
let reservationsReleased = false;
|
|
23865
|
+
let reservationsReleasedCount = 0;
|
|
23866
|
+
let reservationsReleaseError;
|
|
23627
23867
|
try {
|
|
23628
|
-
await releaseSwarmFiles({
|
|
23868
|
+
const releaseResult = await releaseSwarmFiles({
|
|
23629
23869
|
projectPath: args.project_key,
|
|
23630
23870
|
agentName: args.agent_name
|
|
23631
23871
|
});
|
|
23872
|
+
reservationsReleased = true;
|
|
23873
|
+
reservationsReleasedCount = releaseResult.released;
|
|
23632
23874
|
} catch (error45) {
|
|
23875
|
+
reservationsReleaseError = error45 instanceof Error ? error45.message : String(error45);
|
|
23633
23876
|
console.warn(`[swarm] Failed to release file reservations for ${args.agent_name}:`, error45);
|
|
23634
23877
|
}
|
|
23635
23878
|
const epicId2 = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
|
|
@@ -23665,7 +23908,9 @@ This will be recorded as a negative learning signal.`;
|
|
|
23665
23908
|
success: true,
|
|
23666
23909
|
bead_id: args.bead_id,
|
|
23667
23910
|
closed: true,
|
|
23668
|
-
reservations_released:
|
|
23911
|
+
reservations_released: reservationsReleased,
|
|
23912
|
+
reservations_released_count: reservationsReleasedCount,
|
|
23913
|
+
reservations_release_error: reservationsReleaseError,
|
|
23669
23914
|
synced: syncSuccess,
|
|
23670
23915
|
sync_error: syncError,
|
|
23671
23916
|
message_sent: messageSent,
|
|
@@ -37885,14 +38130,14 @@ async function maybeAutoMigrate(db) {
|
|
|
37885
38130
|
if (memoryCount > 0) {
|
|
37886
38131
|
return;
|
|
37887
38132
|
}
|
|
37888
|
-
console.
|
|
38133
|
+
console.error("[memory] Legacy database detected, starting auto-migration...");
|
|
37889
38134
|
const result = await migrateLegacyMemories({
|
|
37890
38135
|
targetDb: db,
|
|
37891
38136
|
dryRun: false,
|
|
37892
|
-
onProgress: console.
|
|
38137
|
+
onProgress: (msg) => console.error(msg)
|
|
37893
38138
|
});
|
|
37894
38139
|
if (result.migrated > 0) {
|
|
37895
|
-
console.
|
|
38140
|
+
console.error(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
|
|
37896
38141
|
}
|
|
37897
38142
|
if (result.failed > 0) {
|
|
37898
38143
|
console.warn(`[memory] ${result.failed} memories failed to migrate. See errors above.`);
|
|
@@ -37961,6 +38206,7 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
|
|
|
37961
38206
|
async find(args2) {
|
|
37962
38207
|
const limit = args2.limit ?? 10;
|
|
37963
38208
|
let results;
|
|
38209
|
+
let usedFallback = false;
|
|
37964
38210
|
if (args2.fts) {
|
|
37965
38211
|
results = await store.ftsSearch(args2.query, {
|
|
37966
38212
|
limit,
|
|
@@ -37971,14 +38217,23 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
|
|
|
37971
38217
|
const ollama = yield* Ollama;
|
|
37972
38218
|
return yield* ollama.embed(args2.query);
|
|
37973
38219
|
});
|
|
37974
|
-
|
|
37975
|
-
|
|
37976
|
-
|
|
37977
|
-
|
|
37978
|
-
|
|
37979
|
-
|
|
38220
|
+
try {
|
|
38221
|
+
const queryEmbedding = await exports_Effect.runPromise(program.pipe(exports_Effect.provide(ollamaLayer)));
|
|
38222
|
+
results = await store.search(queryEmbedding, {
|
|
38223
|
+
limit,
|
|
38224
|
+
threshold: 0.3,
|
|
38225
|
+
collection: args2.collection
|
|
38226
|
+
});
|
|
38227
|
+
} catch (e) {
|
|
38228
|
+
console.warn("[hivemind] Ollama unavailable, falling back to full-text search");
|
|
38229
|
+
usedFallback = true;
|
|
38230
|
+
results = await store.ftsSearch(args2.query, {
|
|
38231
|
+
limit,
|
|
38232
|
+
collection: args2.collection
|
|
38233
|
+
});
|
|
38234
|
+
}
|
|
37980
38235
|
}
|
|
37981
|
-
|
|
38236
|
+
const response = {
|
|
37982
38237
|
results: results.map((r) => ({
|
|
37983
38238
|
id: r.memory.id,
|
|
37984
38239
|
content: args2.expand ? r.memory.content : truncateContent(r.memory.content),
|
|
@@ -37989,6 +38244,10 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
|
|
|
37989
38244
|
})),
|
|
37990
38245
|
count: results.length
|
|
37991
38246
|
};
|
|
38247
|
+
if (usedFallback) {
|
|
38248
|
+
response.fallback_used = true;
|
|
38249
|
+
}
|
|
38250
|
+
return response;
|
|
37992
38251
|
},
|
|
37993
38252
|
async get(args2) {
|
|
37994
38253
|
return store.get(args2.id);
|
|
@@ -38811,6 +39070,74 @@ Your role is **ONLY** to:
|
|
|
38811
39070
|
|
|
38812
39071
|
**ALWAYS spawn workers, even for sequential tasks.** Sequential just means spawn them in order and wait for each to complete before spawning the next.
|
|
38813
39072
|
|
|
39073
|
+
### Explicit NEVER Rules (With Examples)
|
|
39074
|
+
|
|
39075
|
+
\`\`\`
|
|
39076
|
+
╔═══════════════════════════════════════════════════════════════════════════╗
|
|
39077
|
+
║ ║
|
|
39078
|
+
║ ❌ COORDINATORS NEVER DO THIS: ║
|
|
39079
|
+
║ ║
|
|
39080
|
+
║ - Read implementation files (read(), glob src/**, grep for patterns) ║
|
|
39081
|
+
║ - Edit code (edit(), write() any .ts/.js/.tsx files) ║
|
|
39082
|
+
║ - Run tests (bash "bun test", "npm test", pytest) ║
|
|
39083
|
+
║ - Implement features (adding functions, components, logic) ║
|
|
39084
|
+
║ - Fix bugs (changing code to fix errors) ║
|
|
39085
|
+
║ - Install packages (bash "bun add", "npm install") ║
|
|
39086
|
+
║ - Commit changes (bash "git add", "git commit") ║
|
|
39087
|
+
║ - Reserve files (swarmmail_reserve - workers do this) ║
|
|
39088
|
+
║ ║
|
|
39089
|
+
║ ✅ COORDINATORS ONLY DO THIS: ║
|
|
39090
|
+
║ ║
|
|
39091
|
+
║ - Clarify task scope (ask questions, understand requirements) ║
|
|
39092
|
+
║ - Read package.json/tsconfig.json for structure (metadata only) ║
|
|
39093
|
+
║ - Decompose into subtasks (swarm_plan_prompt, validate_decomposition) ║
|
|
39094
|
+
║ - Spawn workers (swarm_spawn_subtask, Task(subagent_type="worker")) ║
|
|
39095
|
+
║ - Monitor progress (swarmmail_inbox, swarm_status) ║
|
|
39096
|
+
║ - Review completed work (swarm_review, swarm_review_feedback) ║
|
|
39097
|
+
║ - Verify final state (check all workers completed, hive_sync) ║
|
|
39098
|
+
║ ║
|
|
39099
|
+
╚═══════════════════════════════════════════════════════════════════════════╝
|
|
39100
|
+
\`\`\`
|
|
39101
|
+
|
|
39102
|
+
**Examples of Violations:**
|
|
39103
|
+
|
|
39104
|
+
❌ **WRONG** - Coordinator reading implementation:
|
|
39105
|
+
\`\`\`
|
|
39106
|
+
read("src/auth/login.ts") // NO - spawn worker to analyze
|
|
39107
|
+
glob("src/components/**/*.tsx") // NO - spawn worker to inventory
|
|
39108
|
+
grep(pattern="export", include="*.ts") // NO - spawn worker to search
|
|
39109
|
+
\`\`\`
|
|
39110
|
+
|
|
39111
|
+
❌ **WRONG** - Coordinator editing code:
|
|
39112
|
+
\`\`\`
|
|
39113
|
+
edit("src/types.ts", ...) // NO - spawn worker to fix
|
|
39114
|
+
write("src/new.ts", ...) // NO - spawn worker to create
|
|
39115
|
+
\`\`\`
|
|
39116
|
+
|
|
39117
|
+
❌ **WRONG** - Coordinator running tests:
|
|
39118
|
+
\`\`\`
|
|
39119
|
+
bash("bun test src/auth.test.ts") // NO - worker runs tests
|
|
39120
|
+
\`\`\`
|
|
39121
|
+
|
|
39122
|
+
❌ **WRONG** - Coordinator reserving files:
|
|
39123
|
+
\`\`\`
|
|
39124
|
+
swarmmail_reserve(paths=["src/auth.ts"]) // NO - worker reserves their own files
|
|
39125
|
+
swarm_spawn_subtask(bead_id="...", files=["src/auth.ts"])
|
|
39126
|
+
\`\`\`
|
|
39127
|
+
|
|
39128
|
+
✅ **CORRECT** - Coordinator spawning worker:
|
|
39129
|
+
\`\`\`
|
|
39130
|
+
// Coordinator delegates ALL work
|
|
39131
|
+
swarm_spawn_subtask(
|
|
39132
|
+
bead_id="fix-auth-bug",
|
|
39133
|
+
epic_id="epic-123",
|
|
39134
|
+
subtask_title="Fix null check in login handler",
|
|
39135
|
+
files=["src/auth/login.ts", "src/auth/login.test.ts"],
|
|
39136
|
+
shared_context="Bug: login fails when username is null"
|
|
39137
|
+
)
|
|
39138
|
+
Task(subagent_type="swarm-worker", prompt="<from above>")
|
|
39139
|
+
\`\`\`
|
|
39140
|
+
|
|
38814
39141
|
### Why This Matters
|
|
38815
39142
|
|
|
38816
39143
|
| Coordinator Work | Worker Work | Consequence of Mixing |
|
|
@@ -22,8 +22,8 @@ export declare const DecompositionStrategySchema: z.ZodEnum<{
|
|
|
22
22
|
"file-based": "file-based";
|
|
23
23
|
"feature-based": "feature-based";
|
|
24
24
|
"risk-based": "risk-based";
|
|
25
|
-
auto: "auto";
|
|
26
25
|
"research-based": "research-based";
|
|
26
|
+
auto: "auto";
|
|
27
27
|
}>;
|
|
28
28
|
/**
|
|
29
29
|
* Marker words that indicate positive directives
|
package/dist/swarm.d.ts
CHANGED
|
@@ -123,7 +123,7 @@ export declare const swarmTools: {
|
|
|
123
123
|
files_touched: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
124
124
|
skip_verification: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
125
125
|
planned_files: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
126
|
-
start_time: import("zod").
|
|
126
|
+
start_time: import("zod").ZodNumber;
|
|
127
127
|
error_count: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
128
128
|
retry_count: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
129
129
|
skip_review: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
@@ -133,11 +133,11 @@ export declare const swarmTools: {
|
|
|
133
133
|
agent_name: string;
|
|
134
134
|
bead_id: string;
|
|
135
135
|
summary: string;
|
|
136
|
+
start_time: number;
|
|
136
137
|
evaluation?: string | undefined;
|
|
137
138
|
files_touched?: string[] | undefined;
|
|
138
139
|
skip_verification?: boolean | undefined;
|
|
139
140
|
planned_files?: string[] | undefined;
|
|
140
|
-
start_time?: number | undefined;
|
|
141
141
|
error_count?: number | undefined;
|
|
142
142
|
retry_count?: number | undefined;
|
|
143
143
|
skip_review?: boolean | undefined;
|
|
@@ -2775,8 +2775,9 @@ const SwarmPlugin: Plugin = async (
|
|
|
2775
2775
|
has_projection: !!sessionScan.projection?.isSwarm,
|
|
2776
2776
|
});
|
|
2777
2777
|
|
|
2778
|
-
// Hoist snapshot outside try block so
|
|
2778
|
+
// Hoist snapshot and queryDuration outside try block so they're available in fallback path
|
|
2779
2779
|
let snapshot: SwarmStateSnapshot | undefined;
|
|
2780
|
+
let queryDuration = 0; // 0 if using projection, actual duration if using hive query
|
|
2780
2781
|
|
|
2781
2782
|
try {
|
|
2782
2783
|
// =======================================================================
|
|
@@ -2821,7 +2822,7 @@ const SwarmPlugin: Plugin = async (
|
|
|
2821
2822
|
// Fallback to hive query (may be stale)
|
|
2822
2823
|
const queryStart = Date.now();
|
|
2823
2824
|
snapshot = await querySwarmState(input.sessionID);
|
|
2824
|
-
|
|
2825
|
+
queryDuration = Date.now() - queryStart;
|
|
2825
2826
|
|
|
2826
2827
|
logCompaction("info", "fallback_to_hive_query", {
|
|
2827
2828
|
session_id: input.sessionID,
|
|
@@ -2967,6 +2968,16 @@ const SwarmPlugin: Plugin = async (
|
|
|
2967
2968
|
});
|
|
2968
2969
|
}
|
|
2969
2970
|
|
|
2971
|
+
// Guard: Don't double-inject if LLM prompt was already set
|
|
2972
|
+
// This can happen if the error occurred after setting output.prompt but before return
|
|
2973
|
+
if ("prompt" in output && output.prompt) {
|
|
2974
|
+
logCompaction("info", "skipping_static_fallback_prompt_already_set", {
|
|
2975
|
+
session_id: input.sessionID,
|
|
2976
|
+
prompt_length: output.prompt.length,
|
|
2977
|
+
});
|
|
2978
|
+
return;
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2970
2981
|
// Level 3: Fall back to static context WITH dynamic state from snapshot
|
|
2971
2982
|
const header = `[Swarm detected: ${detection.reasons.join(", ")}]\n\n`;
|
|
2972
2983
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "Multi-agent swarm coordination for OpenCode with learning capabilities, beads integration, and Agent Mail",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"minimatch": "^10.1.1",
|
|
65
65
|
"pino": "^9.6.0",
|
|
66
66
|
"pino-roll": "^1.3.0",
|
|
67
|
-
"swarm-mail": "1.7.
|
|
67
|
+
"swarm-mail": "1.7.2",
|
|
68
68
|
"yaml": "^2.8.2",
|
|
69
69
|
"zod": "4.1.8"
|
|
70
70
|
},
|