opencode-swarm-plugin 0.48.1 → 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 +1035 -184
- 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 +376 -16
- package/dist/memory.d.ts.map +1 -1
- package/dist/plugin.js +373 -16
- 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-prompts.d.ts +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-prompts.js +335 -16
- 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/plugin.js
CHANGED
|
@@ -27872,11 +27872,16 @@ echo "Project directory: $1"
|
|
|
27872
27872
|
// src/swarm-insights.ts
|
|
27873
27873
|
var exports_swarm_insights = {};
|
|
27874
27874
|
__export(exports_swarm_insights, {
|
|
27875
|
+
trackCoordinatorViolation: () => trackCoordinatorViolation,
|
|
27876
|
+
getViolationAnalytics: () => getViolationAnalytics,
|
|
27875
27877
|
getStrategyInsights: () => getStrategyInsights,
|
|
27878
|
+
getRejectionAnalytics: () => getRejectionAnalytics,
|
|
27876
27879
|
getPatternInsights: () => getPatternInsights,
|
|
27877
27880
|
getFileInsights: () => getFileInsights,
|
|
27881
|
+
getFileFailureHistory: () => getFileFailureHistory,
|
|
27878
27882
|
getCachedInsights: () => getCachedInsights,
|
|
27879
27883
|
formatInsightsForPrompt: () => formatInsightsForPrompt,
|
|
27884
|
+
formatFileHistoryWarnings: () => formatFileHistoryWarnings,
|
|
27880
27885
|
clearInsightsCache: () => clearInsightsCache
|
|
27881
27886
|
});
|
|
27882
27887
|
async function getStrategyInsights(swarmMail, _task) {
|
|
@@ -27948,6 +27953,136 @@ async function getFileInsights(swarmMail, files) {
|
|
|
27948
27953
|
async function getFileGotchas(_swarmMail, _file2) {
|
|
27949
27954
|
return [];
|
|
27950
27955
|
}
|
|
27956
|
+
async function getFileFailureHistory(swarmMail, files) {
|
|
27957
|
+
if (files.length === 0)
|
|
27958
|
+
return [];
|
|
27959
|
+
const db = await swarmMail.getDatabase();
|
|
27960
|
+
const histories = [];
|
|
27961
|
+
for (const file2 of files) {
|
|
27962
|
+
const query = `
|
|
27963
|
+
SELECT data
|
|
27964
|
+
FROM events
|
|
27965
|
+
WHERE type = 'review_feedback'
|
|
27966
|
+
AND json_extract(data, '$.status') = 'needs_changes'
|
|
27967
|
+
AND json_extract(data, '$.issues') LIKE ?
|
|
27968
|
+
`;
|
|
27969
|
+
const result = await db.query(query, [`%${file2}%`]);
|
|
27970
|
+
if (!result.rows || result.rows.length === 0) {
|
|
27971
|
+
continue;
|
|
27972
|
+
}
|
|
27973
|
+
const issueTexts = [];
|
|
27974
|
+
for (const row of result.rows) {
|
|
27975
|
+
try {
|
|
27976
|
+
const data = JSON.parse(row.data);
|
|
27977
|
+
const issuesStr = data.issues;
|
|
27978
|
+
if (!issuesStr)
|
|
27979
|
+
continue;
|
|
27980
|
+
const issues = JSON.parse(issuesStr);
|
|
27981
|
+
for (const issue2 of issues) {
|
|
27982
|
+
if (issue2.file === file2) {
|
|
27983
|
+
issueTexts.push(issue2.issue);
|
|
27984
|
+
}
|
|
27985
|
+
}
|
|
27986
|
+
} catch (e) {
|
|
27987
|
+
continue;
|
|
27988
|
+
}
|
|
27989
|
+
}
|
|
27990
|
+
if (issueTexts.length === 0) {
|
|
27991
|
+
continue;
|
|
27992
|
+
}
|
|
27993
|
+
const issueCounts = new Map;
|
|
27994
|
+
for (const text of issueTexts) {
|
|
27995
|
+
issueCounts.set(text, (issueCounts.get(text) || 0) + 1);
|
|
27996
|
+
}
|
|
27997
|
+
const topIssues = Array.from(issueCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([text]) => text);
|
|
27998
|
+
histories.push({
|
|
27999
|
+
file: file2,
|
|
28000
|
+
rejectionCount: issueTexts.length,
|
|
28001
|
+
topIssues
|
|
28002
|
+
});
|
|
28003
|
+
}
|
|
28004
|
+
return histories;
|
|
28005
|
+
}
|
|
28006
|
+
async function getRejectionAnalytics(swarmMail) {
|
|
28007
|
+
const db = await swarmMail.getDatabase();
|
|
28008
|
+
const query = `
|
|
28009
|
+
SELECT data
|
|
28010
|
+
FROM events
|
|
28011
|
+
WHERE type = 'review_feedback'
|
|
28012
|
+
ORDER BY timestamp DESC
|
|
28013
|
+
`;
|
|
28014
|
+
const result = await db.query(query, []);
|
|
28015
|
+
if (!result.rows || result.rows.length === 0) {
|
|
28016
|
+
return {
|
|
28017
|
+
totalReviews: 0,
|
|
28018
|
+
approved: 0,
|
|
28019
|
+
rejected: 0,
|
|
28020
|
+
approvalRate: 0,
|
|
28021
|
+
topReasons: []
|
|
28022
|
+
};
|
|
28023
|
+
}
|
|
28024
|
+
let approved = 0;
|
|
28025
|
+
let rejected = 0;
|
|
28026
|
+
const reasonCounts = new Map;
|
|
28027
|
+
for (const row of result.rows) {
|
|
28028
|
+
try {
|
|
28029
|
+
const data = JSON.parse(row.data);
|
|
28030
|
+
if (data.status === "approved") {
|
|
28031
|
+
approved++;
|
|
28032
|
+
} else if (data.status === "needs_changes") {
|
|
28033
|
+
rejected++;
|
|
28034
|
+
if (data.issues) {
|
|
28035
|
+
const issues = JSON.parse(data.issues);
|
|
28036
|
+
for (const issue2 of issues) {
|
|
28037
|
+
const category = categorizeRejectionReason(issue2.issue);
|
|
28038
|
+
reasonCounts.set(category, (reasonCounts.get(category) || 0) + 1);
|
|
28039
|
+
}
|
|
28040
|
+
}
|
|
28041
|
+
}
|
|
28042
|
+
} catch (e) {
|
|
28043
|
+
continue;
|
|
28044
|
+
}
|
|
28045
|
+
}
|
|
28046
|
+
const totalReviews = approved + rejected;
|
|
28047
|
+
const approvalRate = totalReviews > 0 ? approved / totalReviews * 100 : 0;
|
|
28048
|
+
const topReasons = Array.from(reasonCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([category, count]) => ({
|
|
28049
|
+
category,
|
|
28050
|
+
count,
|
|
28051
|
+
percentage: rejected > 0 ? count / rejected * 100 : 0
|
|
28052
|
+
}));
|
|
28053
|
+
return {
|
|
28054
|
+
totalReviews,
|
|
28055
|
+
approved,
|
|
28056
|
+
rejected,
|
|
28057
|
+
approvalRate,
|
|
28058
|
+
topReasons
|
|
28059
|
+
};
|
|
28060
|
+
}
|
|
28061
|
+
function categorizeRejectionReason(reason) {
|
|
28062
|
+
const lowerReason = reason.toLowerCase();
|
|
28063
|
+
if (lowerReason.includes("test") || lowerReason.includes("spec") || lowerReason.includes("coverage")) {
|
|
28064
|
+
return "Missing tests";
|
|
28065
|
+
}
|
|
28066
|
+
if (lowerReason.includes("type") || lowerReason.includes("undefined") || lowerReason.includes("null") || lowerReason.includes("assignable")) {
|
|
28067
|
+
return "Type errors";
|
|
28068
|
+
}
|
|
28069
|
+
if (lowerReason.includes("incomplete") || lowerReason.includes("missing") || lowerReason.includes("forgot") || lowerReason.includes("didn't implement")) {
|
|
28070
|
+
return "Incomplete implementation";
|
|
28071
|
+
}
|
|
28072
|
+
if (lowerReason.includes("wrong file") || lowerReason.includes("modified incorrect") || lowerReason.includes("shouldn't have changed")) {
|
|
28073
|
+
return "Wrong file modified";
|
|
28074
|
+
}
|
|
28075
|
+
if (lowerReason.includes("performance") || lowerReason.includes("slow") || lowerReason.includes("inefficient")) {
|
|
28076
|
+
return "Performance issue";
|
|
28077
|
+
}
|
|
28078
|
+
if (lowerReason.includes("security") || lowerReason.includes("vulnerability") || lowerReason.includes("unsafe")) {
|
|
28079
|
+
return "Security vulnerability";
|
|
28080
|
+
}
|
|
28081
|
+
if (lowerReason.includes("error handling") || lowerReason.includes("try/catch") || lowerReason.includes("exception")) {
|
|
28082
|
+
return "Missing error handling";
|
|
28083
|
+
}
|
|
28084
|
+
return "Other";
|
|
28085
|
+
}
|
|
27951
28086
|
async function getPatternInsights(swarmMail) {
|
|
27952
28087
|
const db = await swarmMail.getDatabase();
|
|
27953
28088
|
const patterns = [];
|
|
@@ -28035,6 +28170,107 @@ async function getCachedInsights(_swarmMail, cacheKey, computeFn) {
|
|
|
28035
28170
|
function clearInsightsCache() {
|
|
28036
28171
|
insightsCache.clear();
|
|
28037
28172
|
}
|
|
28173
|
+
async function trackCoordinatorViolation(swarmMail, violation) {
|
|
28174
|
+
const db = await swarmMail.getDatabase();
|
|
28175
|
+
const query = `
|
|
28176
|
+
INSERT INTO events (type, project_key, timestamp, data)
|
|
28177
|
+
VALUES (?, ?, ?, ?)
|
|
28178
|
+
RETURNING id
|
|
28179
|
+
`;
|
|
28180
|
+
const data = JSON.stringify({
|
|
28181
|
+
session_id: violation.session_id,
|
|
28182
|
+
epic_id: violation.epic_id,
|
|
28183
|
+
event_type: "VIOLATION",
|
|
28184
|
+
violation_type: violation.violation_type,
|
|
28185
|
+
payload: violation.payload
|
|
28186
|
+
});
|
|
28187
|
+
const result = await db.query(query, [
|
|
28188
|
+
"coordinator_violation",
|
|
28189
|
+
violation.project_key,
|
|
28190
|
+
Date.now(),
|
|
28191
|
+
data
|
|
28192
|
+
]);
|
|
28193
|
+
return result.rows[0].id;
|
|
28194
|
+
}
|
|
28195
|
+
async function getViolationAnalytics(swarmMail, projectKey) {
|
|
28196
|
+
const db = await swarmMail.getDatabase();
|
|
28197
|
+
const violationsQuery = projectKey ? `
|
|
28198
|
+
SELECT data
|
|
28199
|
+
FROM events
|
|
28200
|
+
WHERE type = 'coordinator_violation'
|
|
28201
|
+
AND project_key = ?
|
|
28202
|
+
ORDER BY timestamp DESC
|
|
28203
|
+
` : `
|
|
28204
|
+
SELECT data
|
|
28205
|
+
FROM events
|
|
28206
|
+
WHERE type = 'coordinator_violation'
|
|
28207
|
+
ORDER BY timestamp DESC
|
|
28208
|
+
`;
|
|
28209
|
+
const params = projectKey ? [projectKey] : [];
|
|
28210
|
+
const result = await db.query(violationsQuery, params);
|
|
28211
|
+
if (!result.rows || result.rows.length === 0) {
|
|
28212
|
+
return {
|
|
28213
|
+
totalViolations: 0,
|
|
28214
|
+
byType: [],
|
|
28215
|
+
violationRate: 0
|
|
28216
|
+
};
|
|
28217
|
+
}
|
|
28218
|
+
const violationCounts = new Map;
|
|
28219
|
+
let totalViolations = 0;
|
|
28220
|
+
for (const row of result.rows) {
|
|
28221
|
+
try {
|
|
28222
|
+
const data = JSON.parse(row.data);
|
|
28223
|
+
const violationType = data.violation_type;
|
|
28224
|
+
if (violationType) {
|
|
28225
|
+
violationCounts.set(violationType, (violationCounts.get(violationType) || 0) + 1);
|
|
28226
|
+
totalViolations++;
|
|
28227
|
+
}
|
|
28228
|
+
} catch (e) {
|
|
28229
|
+
continue;
|
|
28230
|
+
}
|
|
28231
|
+
}
|
|
28232
|
+
const byType = Array.from(violationCounts.entries()).sort((a, b) => b[1] - a[1]).map(([violationType, count]) => ({
|
|
28233
|
+
violationType,
|
|
28234
|
+
count,
|
|
28235
|
+
percentage: count / totalViolations * 100
|
|
28236
|
+
}));
|
|
28237
|
+
const coordinationQuery = projectKey ? `
|
|
28238
|
+
SELECT COUNT(*) as count
|
|
28239
|
+
FROM events
|
|
28240
|
+
WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
|
|
28241
|
+
AND project_key = ?
|
|
28242
|
+
` : `
|
|
28243
|
+
SELECT COUNT(*) as count
|
|
28244
|
+
FROM events
|
|
28245
|
+
WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
|
|
28246
|
+
`;
|
|
28247
|
+
const coordResult = await db.query(coordinationQuery, params);
|
|
28248
|
+
const coordinationCount = coordResult.rows[0]?.count || 0;
|
|
28249
|
+
const violationRate = coordinationCount > 0 ? totalViolations / coordinationCount * 100 : 0;
|
|
28250
|
+
return {
|
|
28251
|
+
totalViolations,
|
|
28252
|
+
byType,
|
|
28253
|
+
violationRate
|
|
28254
|
+
};
|
|
28255
|
+
}
|
|
28256
|
+
function formatFileHistoryWarnings(histories) {
|
|
28257
|
+
if (histories.length === 0) {
|
|
28258
|
+
return "";
|
|
28259
|
+
}
|
|
28260
|
+
const lines = ["⚠️ FILE HISTORY WARNINGS:"];
|
|
28261
|
+
for (const history of histories) {
|
|
28262
|
+
const workerText = history.rejectionCount === 1 ? "1 previous worker rejected" : `${history.rejectionCount} previous workers rejected`;
|
|
28263
|
+
const issuesText = history.topIssues.join(", ");
|
|
28264
|
+
lines.push(`- ${history.file}: ${workerText} for ${issuesText}`);
|
|
28265
|
+
}
|
|
28266
|
+
let result = lines.join(`
|
|
28267
|
+
`);
|
|
28268
|
+
const maxChars = 300 * 4;
|
|
28269
|
+
if (result.length > maxChars) {
|
|
28270
|
+
result = result.slice(0, maxChars - 3) + "...";
|
|
28271
|
+
}
|
|
28272
|
+
return result;
|
|
28273
|
+
}
|
|
28038
28274
|
var insightsCache, CACHE_TTL_MS;
|
|
28039
28275
|
var init_swarm_insights = __esm(() => {
|
|
28040
28276
|
insightsCache = new Map;
|
|
@@ -39248,7 +39484,8 @@ var CellTreeSchema = exports_external.object({
|
|
|
39248
39484
|
title: exports_external.string().min(1),
|
|
39249
39485
|
description: exports_external.string().optional().default("")
|
|
39250
39486
|
}),
|
|
39251
|
-
subtasks: exports_external.array(SubtaskSpecSchema).min(1)
|
|
39487
|
+
subtasks: exports_external.array(SubtaskSpecSchema).min(1),
|
|
39488
|
+
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.")
|
|
39252
39489
|
});
|
|
39253
39490
|
var EpicCreateArgsSchema = exports_external.object({
|
|
39254
39491
|
epic_title: exports_external.string().min(1),
|
|
@@ -39839,13 +40076,13 @@ async function autoMigrateFromJSONL(adapter, projectKey) {
|
|
|
39839
40076
|
skipExisting: true
|
|
39840
40077
|
});
|
|
39841
40078
|
if (result.created > 0 || result.updated > 0) {
|
|
39842
|
-
console.
|
|
40079
|
+
console.error(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
|
|
39843
40080
|
}
|
|
39844
40081
|
if (result.errors.length > 0) {
|
|
39845
|
-
console.
|
|
40082
|
+
console.error(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
|
|
39846
40083
|
}
|
|
39847
40084
|
} catch (error45) {
|
|
39848
|
-
console.
|
|
40085
|
+
console.error(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error45 instanceof Error ? error45.message : String(error45));
|
|
39849
40086
|
}
|
|
39850
40087
|
}
|
|
39851
40088
|
function formatCellForOutput(adapterCell) {
|
|
@@ -46476,7 +46713,7 @@ var swarm_complete = tool({
|
|
|
46476
46713
|
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (typecheck, tests)"),
|
|
46477
46714
|
skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (typecheck, tests). Use sparingly! (default: false)"),
|
|
46478
46715
|
planned_files: tool.schema.array(tool.schema.string()).optional().describe("Files that were originally planned to be modified"),
|
|
46479
|
-
start_time: tool.schema.number().
|
|
46716
|
+
start_time: tool.schema.number().describe("Task start timestamp (Unix ms) for duration calculation - REQUIRED for accurate analytics"),
|
|
46480
46717
|
error_count: tool.schema.number().optional().describe("Number of errors encountered during task"),
|
|
46481
46718
|
retry_count: tool.schema.number().optional().describe("Number of retry attempts during task"),
|
|
46482
46719
|
skip_review: tool.schema.boolean().optional().describe("Skip review gate check (default: false). Use only for tasks that don't require coordinator review.")
|
|
@@ -46680,7 +46917,7 @@ This will be recorded as a negative learning signal.`;
|
|
|
46680
46917
|
syncError = error45 instanceof Error ? error45.message : String(error45);
|
|
46681
46918
|
console.warn(`[swarm_complete] Auto-sync failed (non-fatal): ${syncError}`);
|
|
46682
46919
|
}
|
|
46683
|
-
const completionDurationMs =
|
|
46920
|
+
const completionDurationMs = Date.now() - args.start_time;
|
|
46684
46921
|
const eventEpicId = cell.parent_id || (args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id);
|
|
46685
46922
|
try {
|
|
46686
46923
|
const event = createEvent3("subtask_outcome", {
|
|
@@ -61021,14 +61258,14 @@ async function maybeAutoMigrate(db) {
|
|
|
61021
61258
|
if (memoryCount > 0) {
|
|
61022
61259
|
return;
|
|
61023
61260
|
}
|
|
61024
|
-
console.
|
|
61261
|
+
console.error("[memory] Legacy database detected, starting auto-migration...");
|
|
61025
61262
|
const result = await migrateLegacyMemories({
|
|
61026
61263
|
targetDb: db,
|
|
61027
61264
|
dryRun: false,
|
|
61028
|
-
onProgress: console.
|
|
61265
|
+
onProgress: (msg) => console.error(msg)
|
|
61029
61266
|
});
|
|
61030
61267
|
if (result.migrated > 0) {
|
|
61031
|
-
console.
|
|
61268
|
+
console.error(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
|
|
61032
61269
|
}
|
|
61033
61270
|
if (result.failed > 0) {
|
|
61034
61271
|
console.warn(`[memory] ${result.failed} memories failed to migrate. See errors above.`);
|
|
@@ -61097,6 +61334,7 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
|
|
|
61097
61334
|
async find(args2) {
|
|
61098
61335
|
const limit = args2.limit ?? 10;
|
|
61099
61336
|
let results;
|
|
61337
|
+
let usedFallback = false;
|
|
61100
61338
|
if (args2.fts) {
|
|
61101
61339
|
results = await store.ftsSearch(args2.query, {
|
|
61102
61340
|
limit,
|
|
@@ -61107,14 +61345,23 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
|
|
|
61107
61345
|
const ollama = yield* Ollama;
|
|
61108
61346
|
return yield* ollama.embed(args2.query);
|
|
61109
61347
|
});
|
|
61110
|
-
|
|
61111
|
-
|
|
61112
|
-
|
|
61113
|
-
|
|
61114
|
-
|
|
61115
|
-
|
|
61348
|
+
try {
|
|
61349
|
+
const queryEmbedding = await exports_Effect.runPromise(program.pipe(exports_Effect.provide(ollamaLayer)));
|
|
61350
|
+
results = await store.search(queryEmbedding, {
|
|
61351
|
+
limit,
|
|
61352
|
+
threshold: 0.3,
|
|
61353
|
+
collection: args2.collection
|
|
61354
|
+
});
|
|
61355
|
+
} catch (e) {
|
|
61356
|
+
console.warn("[hivemind] Ollama unavailable, falling back to full-text search");
|
|
61357
|
+
usedFallback = true;
|
|
61358
|
+
results = await store.ftsSearch(args2.query, {
|
|
61359
|
+
limit,
|
|
61360
|
+
collection: args2.collection
|
|
61361
|
+
});
|
|
61362
|
+
}
|
|
61116
61363
|
}
|
|
61117
|
-
|
|
61364
|
+
const response = {
|
|
61118
61365
|
results: results.map((r) => ({
|
|
61119
61366
|
id: r.memory.id,
|
|
61120
61367
|
content: args2.expand ? r.memory.content : truncateContent(r.memory.content),
|
|
@@ -61125,6 +61372,10 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
|
|
|
61125
61372
|
})),
|
|
61126
61373
|
count: results.length
|
|
61127
61374
|
};
|
|
61375
|
+
if (usedFallback) {
|
|
61376
|
+
response.fallback_used = true;
|
|
61377
|
+
}
|
|
61378
|
+
return response;
|
|
61128
61379
|
},
|
|
61129
61380
|
async get(args2) {
|
|
61130
61381
|
return store.get(args2.id);
|
|
@@ -65148,6 +65399,104 @@ function isInCoordinatorContext(sessionId) {
|
|
|
65148
65399
|
return true;
|
|
65149
65400
|
}
|
|
65150
65401
|
|
|
65402
|
+
// src/coordinator-guard.ts
|
|
65403
|
+
class CoordinatorGuardError extends Error {
|
|
65404
|
+
violationType;
|
|
65405
|
+
payload;
|
|
65406
|
+
suggestion;
|
|
65407
|
+
constructor(message, violationType, payload = {}, suggestion) {
|
|
65408
|
+
super(message);
|
|
65409
|
+
this.name = "CoordinatorGuardError";
|
|
65410
|
+
this.violationType = violationType;
|
|
65411
|
+
this.payload = payload;
|
|
65412
|
+
this.suggestion = suggestion;
|
|
65413
|
+
}
|
|
65414
|
+
}
|
|
65415
|
+
var FILE_MODIFICATION_TOOLS = ["edit", "write"];
|
|
65416
|
+
var RESERVATION_TOOLS = ["swarmmail_reserve", "agentmail_reserve"];
|
|
65417
|
+
var TEST_EXECUTION_PATTERNS = [
|
|
65418
|
+
/\bbun\s+test\b/i,
|
|
65419
|
+
/\bnpm\s+(run\s+)?test/i,
|
|
65420
|
+
/\byarn\s+(run\s+)?test/i,
|
|
65421
|
+
/\bpnpm\s+(run\s+)?test/i,
|
|
65422
|
+
/\bjest\b/i,
|
|
65423
|
+
/\bvitest\b/i,
|
|
65424
|
+
/\bmocha\b/i,
|
|
65425
|
+
/\bava\b/i,
|
|
65426
|
+
/\btape\b/i,
|
|
65427
|
+
/\.test\.(ts|js|tsx|jsx)\b/i,
|
|
65428
|
+
/\.spec\.(ts|js|tsx|jsx)\b/i
|
|
65429
|
+
];
|
|
65430
|
+
function isCoordinator(agentContext) {
|
|
65431
|
+
return agentContext === "coordinator";
|
|
65432
|
+
}
|
|
65433
|
+
function checkCoordinatorGuard(params) {
|
|
65434
|
+
const { agentContext, toolName, toolArgs } = params;
|
|
65435
|
+
if (!isCoordinator(agentContext)) {
|
|
65436
|
+
return { blocked: false };
|
|
65437
|
+
}
|
|
65438
|
+
if (FILE_MODIFICATION_TOOLS.includes(toolName)) {
|
|
65439
|
+
const file2 = toolArgs.filePath || "unknown";
|
|
65440
|
+
return {
|
|
65441
|
+
blocked: true,
|
|
65442
|
+
error: new CoordinatorGuardError(`❌ COORDINATOR VIOLATION: Coordinators must spawn a worker to edit files.
|
|
65443
|
+
|
|
65444
|
+
You attempted to ${toolName}: ${file2}
|
|
65445
|
+
|
|
65446
|
+
Coordinators orchestrate work, they don't implement it.
|
|
65447
|
+
|
|
65448
|
+
Instead:
|
|
65449
|
+
1. Use swarm_spawn_subtask to spawn a worker for this file
|
|
65450
|
+
2. Let the worker reserve the file and make edits
|
|
65451
|
+
3. Review the worker's output when complete
|
|
65452
|
+
|
|
65453
|
+
This guard exists to prevent the #1 coordinator anti-pattern.`, "coordinator_edited_file", { tool: toolName, file: file2 }, "Use swarm_spawn_subtask to spawn a worker, then let the worker edit the file")
|
|
65454
|
+
};
|
|
65455
|
+
}
|
|
65456
|
+
if (toolName === "bash") {
|
|
65457
|
+
const command = toolArgs.command || "";
|
|
65458
|
+
const isTestCommand = TEST_EXECUTION_PATTERNS.some((pattern) => pattern.test(command));
|
|
65459
|
+
if (isTestCommand) {
|
|
65460
|
+
return {
|
|
65461
|
+
blocked: true,
|
|
65462
|
+
error: new CoordinatorGuardError(`❌ COORDINATOR VIOLATION: Coordinators must not run tests.
|
|
65463
|
+
|
|
65464
|
+
You attempted to run: ${command}
|
|
65465
|
+
|
|
65466
|
+
Workers run tests as part of their implementation verification.
|
|
65467
|
+
Coordinators review the test results.
|
|
65468
|
+
|
|
65469
|
+
Instead:
|
|
65470
|
+
1. Let workers run tests in their implementation workflow
|
|
65471
|
+
2. Workers call swarm_complete which runs tests automatically
|
|
65472
|
+
3. Review test results from worker output
|
|
65473
|
+
|
|
65474
|
+
This guard prevents coordinators from doing workers' verification work.`, "coordinator_ran_tests", { tool: toolName, command }, "Let workers run tests via swarm_complete")
|
|
65475
|
+
};
|
|
65476
|
+
}
|
|
65477
|
+
}
|
|
65478
|
+
if (RESERVATION_TOOLS.includes(toolName)) {
|
|
65479
|
+
const paths = toolArgs.paths || [];
|
|
65480
|
+
return {
|
|
65481
|
+
blocked: true,
|
|
65482
|
+
error: new CoordinatorGuardError(`❌ COORDINATOR VIOLATION: Coordinators must not reserve files.
|
|
65483
|
+
|
|
65484
|
+
You attempted to reserve: ${paths.join(", ")}
|
|
65485
|
+
|
|
65486
|
+
Workers reserve files before editing to prevent conflicts.
|
|
65487
|
+
Coordinators don't edit files, so they don't reserve them.
|
|
65488
|
+
|
|
65489
|
+
Instead:
|
|
65490
|
+
1. Spawn workers via swarm_spawn_subtask
|
|
65491
|
+
2. Workers will reserve files they need to modify
|
|
65492
|
+
3. Coordinate if multiple workers need the same files
|
|
65493
|
+
|
|
65494
|
+
This guard prevents coordinators from performing worker setup steps.`, "coordinator_reserved_files", { tool: toolName, paths }, "Spawn workers who will reserve files themselves")
|
|
65495
|
+
};
|
|
65496
|
+
}
|
|
65497
|
+
return { blocked: false };
|
|
65498
|
+
}
|
|
65499
|
+
|
|
65151
65500
|
// src/compaction-hook.ts
|
|
65152
65501
|
import { checkSwarmHealth as checkSwarmHealth3 } from "swarm-mail";
|
|
65153
65502
|
|
|
@@ -66571,6 +66920,14 @@ var SwarmPlugin = async (input) => {
|
|
|
66571
66920
|
}
|
|
66572
66921
|
if (isInCoordinatorContext(sessionId)) {
|
|
66573
66922
|
const ctx = getCoordinatorContext(sessionId);
|
|
66923
|
+
const guardResult = checkCoordinatorGuard({
|
|
66924
|
+
agentContext: "coordinator",
|
|
66925
|
+
toolName,
|
|
66926
|
+
toolArgs: output.args
|
|
66927
|
+
});
|
|
66928
|
+
if (guardResult.blocked && guardResult.error) {
|
|
66929
|
+
throw guardResult.error;
|
|
66930
|
+
}
|
|
66574
66931
|
const violation = detectCoordinatorViolation({
|
|
66575
66932
|
sessionId,
|
|
66576
66933
|
epicId: ctx.epicId || "unknown",
|
package/dist/query-tools.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface QueryResult {
|
|
|
15
15
|
executionTimeMs: number;
|
|
16
16
|
}
|
|
17
17
|
export declare const presetQueries: Record<PresetQueryName, string>;
|
|
18
|
+
/**
|
|
19
|
+
* Get database path from project path.
|
|
20
|
+
* Uses global database (~/.config/swarm-tools/swarm.db)
|
|
21
|
+
*/
|
|
22
|
+
export declare function getDbPath(): string;
|
|
18
23
|
/**
|
|
19
24
|
* Execute custom SQL against the events table (low-level).
|
|
20
25
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-tools.d.ts","sourceRoot":"","sources":["../src/query-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"query-tools.d.ts","sourceRoot":"","sources":["../src/query-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAOlD,MAAM,MAAM,eAAe,GACxB,uBAAuB,GACvB,sBAAsB,GACtB,gBAAgB,GAChB,qBAAqB,GACrB,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,kBAAkB,GAClB,kBAAkB,GAClB,wBAAwB,GACxB,sBAAsB,CAAC;AAE1B,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACxB;AAMD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAgKzD,CAAC;AAMF;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAeD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CACjC,EAAE,EAAE,eAAe,EACnB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,OAAO,EAAE,GAChB,OAAO,CAAC,WAAW,CAAC,CAiBtB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACvC,EAAE,EAAE,eAAe,EACnB,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAStB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACpC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,GAAG,EAAE,CAAC,CAIhB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAClC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,EAAE,CAAC,CAIhB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAkEzD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CA2BvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAExD"}
|
package/dist/schemas/cell.d.ts
CHANGED
|
@@ -165,6 +165,12 @@ export declare const CellTreeSchema: z.ZodObject<{
|
|
|
165
165
|
dependencies: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
166
166
|
estimated_complexity: z.ZodDefault<z.ZodNumber>;
|
|
167
167
|
}, z.core.$strip>>;
|
|
168
|
+
strategy: z.ZodOptional<z.ZodEnum<{
|
|
169
|
+
"file-based": "file-based";
|
|
170
|
+
"feature-based": "feature-based";
|
|
171
|
+
"risk-based": "risk-based";
|
|
172
|
+
"research-based": "research-based";
|
|
173
|
+
}>>;
|
|
168
174
|
}, z.core.$strip>;
|
|
169
175
|
export type CellTree = z.infer<typeof CellTreeSchema>;
|
|
170
176
|
/** Arguments for creating an epic with subtasks */
|
|
@@ -395,6 +401,12 @@ export declare const BeadTreeSchema: z.ZodObject<{
|
|
|
395
401
|
dependencies: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
396
402
|
estimated_complexity: z.ZodDefault<z.ZodNumber>;
|
|
397
403
|
}, z.core.$strip>>;
|
|
404
|
+
strategy: z.ZodOptional<z.ZodEnum<{
|
|
405
|
+
"file-based": "file-based";
|
|
406
|
+
"feature-based": "feature-based";
|
|
407
|
+
"risk-based": "risk-based";
|
|
408
|
+
"research-based": "research-based";
|
|
409
|
+
}>>;
|
|
398
410
|
}, z.core.$strip>;
|
|
399
411
|
/** @deprecated Use CellTree instead */
|
|
400
412
|
export type BeadTree = CellTree;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../../src/schemas/cell.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,0BAA0B;AAC1B,eAAO,MAAM,gBAAgB;;;;;EAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,uBAAuB;AACvB,eAAO,MAAM,cAAc;;;;;;EAMzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB;;;;;;;;iBAG/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,oCAAoC;AACpC,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;iBAY/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,oCAAoC;AACpC,eAAO,MAAM,oBAAoB;;;;;;;;;;iBAK/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,mCAAmC;AACnC,eAAO,MAAM,mBAAmB;;;iBAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,mCAAmC;AACnC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;iBAM9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;;;iBAc5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../../src/schemas/cell.ts"],"names":[],"mappings":"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"}
|