opencode-swarm 7.98.0 → 7.98.1
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/dist/cli/{guardrail-explain-xdv74tfk.js → guardrail-explain-cm08h4mt.js} +2 -2
- package/dist/cli/{index-wmm21nsk.js → index-0rgde8qc.js} +1 -1
- package/dist/cli/{index-h6h8qfsh.js → index-attgb1ma.js} +2 -2
- package/dist/cli/{index-pc10e4d7.js → index-zgba613y.js} +58 -59
- package/dist/cli/index.js +1 -1
- package/dist/hooks/knowledge-curator.d.ts +1 -0
- package/dist/index.js +184 -72
- package/dist/services/external-skill-validator.d.ts +10 -2
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
handleGuardrailExplain
|
|
4
|
-
} from "./index-
|
|
5
|
-
import"./index-
|
|
4
|
+
} from "./index-0rgde8qc.js";
|
|
5
|
+
import"./index-zgba613y.js";
|
|
6
6
|
import"./index-5z2e78tv.js";
|
|
7
7
|
import"./index-2a6ppa65.js";
|
|
8
8
|
import"./index-fjxjb66n.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
handleGuardrailExplain
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-0rgde8qc.js";
|
|
5
5
|
import {
|
|
6
6
|
handleGuardrailLog
|
|
7
7
|
} from "./index-gnd1280x.js";
|
|
@@ -78,7 +78,7 @@ import {
|
|
|
78
78
|
handleWriteRetroCommand,
|
|
79
79
|
normalizeSwarmCommandInput,
|
|
80
80
|
resolveCommand
|
|
81
|
-
} from "./index-
|
|
81
|
+
} from "./index-zgba613y.js";
|
|
82
82
|
import"./index-5z2e78tv.js";
|
|
83
83
|
import"./index-2a6ppa65.js";
|
|
84
84
|
import"./index-fjxjb66n.js";
|
|
@@ -909,7 +909,7 @@ var init_executor = __esm(() => {
|
|
|
909
909
|
// package.json
|
|
910
910
|
var package_default = {
|
|
911
911
|
name: "opencode-swarm",
|
|
912
|
-
version: "7.98.
|
|
912
|
+
version: "7.98.1",
|
|
913
913
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
914
914
|
main: "dist/index.js",
|
|
915
915
|
types: "dist/index.d.ts",
|
|
@@ -10193,48 +10193,49 @@ function recordSeenRetroSection(key, value, timestamp) {
|
|
|
10193
10193
|
function hashContent(content) {
|
|
10194
10194
|
return createHash3("sha1").update(content).digest("hex");
|
|
10195
10195
|
}
|
|
10196
|
-
function isWriteToSwarmPlan(input) {
|
|
10197
|
-
if (typeof input !== "object" || input === null)
|
|
10198
|
-
return false;
|
|
10199
|
-
const record = input;
|
|
10200
|
-
const toolName = record.toolName;
|
|
10201
|
-
if (typeof toolName !== "string")
|
|
10202
|
-
return false;
|
|
10203
|
-
if (!["write", "edit", "apply_patch", "swarm_apply_patch"].includes(toolName))
|
|
10204
|
-
return false;
|
|
10205
|
-
const rawPath = record.path;
|
|
10206
|
-
const rawFile = record.file;
|
|
10207
|
-
const pathField = typeof rawPath === "string" ? rawPath.replace(/\\/g, "/") : undefined;
|
|
10208
|
-
const fileField = typeof rawFile === "string" ? rawFile.replace(/\\/g, "/") : undefined;
|
|
10209
|
-
if (typeof pathField === "string" && pathField.includes(".swarm/plan.md")) {
|
|
10210
|
-
return true;
|
|
10211
|
-
}
|
|
10212
|
-
if (typeof fileField === "string" && fileField.includes(".swarm/plan.md")) {
|
|
10213
|
-
return true;
|
|
10214
|
-
}
|
|
10215
|
-
return false;
|
|
10216
|
-
}
|
|
10217
10196
|
function isWriteToEvidenceFile(input) {
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10197
|
+
const trigger = normalizeWriteTrigger(input);
|
|
10198
|
+
return isEvidencePath(trigger?.filePath);
|
|
10199
|
+
}
|
|
10200
|
+
var WRITE_TOOLS = new Set([
|
|
10201
|
+
"write",
|
|
10202
|
+
"edit",
|
|
10203
|
+
"apply_patch",
|
|
10204
|
+
"swarm_apply_patch"
|
|
10205
|
+
]);
|
|
10206
|
+
function isRecord(value) {
|
|
10207
|
+
return typeof value === "object" && value !== null;
|
|
10208
|
+
}
|
|
10209
|
+
function normalizePathField(value) {
|
|
10210
|
+
return typeof value === "string" ? value.replace(/\\/g, "/") : null;
|
|
10211
|
+
}
|
|
10212
|
+
function firstPathFromRecord(record) {
|
|
10213
|
+
return normalizePathField(record.path) ?? normalizePathField(record.filePath) ?? normalizePathField(record.file);
|
|
10214
|
+
}
|
|
10215
|
+
function normalizeWriteTrigger(input, output) {
|
|
10216
|
+
if (!isRecord(input))
|
|
10217
|
+
return null;
|
|
10218
|
+
const toolName = typeof input.toolName === "string" ? input.toolName : typeof input.tool === "string" ? input.tool : null;
|
|
10219
|
+
if (!toolName || !WRITE_TOOLS.has(toolName))
|
|
10220
|
+
return null;
|
|
10221
|
+
const inputArgs = isRecord(input.args) ? input.args : null;
|
|
10222
|
+
const outputArgs = isRecord(output) && isRecord(output.args) ? output.args : null;
|
|
10223
|
+
const filePath = firstPathFromRecord(input) ?? (inputArgs ? firstPathFromRecord(inputArgs) : null) ?? (outputArgs ? firstPathFromRecord(outputArgs) : null);
|
|
10224
|
+
if (!filePath)
|
|
10225
|
+
return null;
|
|
10226
|
+
return {
|
|
10227
|
+
toolName,
|
|
10228
|
+
filePath,
|
|
10229
|
+
sessionID: typeof input.sessionID === "string" ? input.sessionID : "default"
|
|
10230
|
+
};
|
|
10231
|
+
}
|
|
10232
|
+
function isEvidencePath(filePath) {
|
|
10233
|
+
if (!filePath)
|
|
10225
10234
|
return false;
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
const evidenceRegex = /\.swarm\/+evidence\/+/i;
|
|
10231
|
-
if (typeof pathField === "string" && evidenceRegex.test(pathField)) {
|
|
10232
|
-
return true;
|
|
10233
|
-
}
|
|
10234
|
-
if (typeof fileField === "string" && evidenceRegex.test(fileField)) {
|
|
10235
|
-
return true;
|
|
10236
|
-
}
|
|
10237
|
-
return false;
|
|
10235
|
+
return /(?:^|\/)\.swarm\/+evidence\//i.test(filePath);
|
|
10236
|
+
}
|
|
10237
|
+
function isPlanPath(filePath) {
|
|
10238
|
+
return filePath?.includes(".swarm/plan.md") ?? false;
|
|
10238
10239
|
}
|
|
10239
10240
|
function extractRetrospectiveSection(planContent) {
|
|
10240
10241
|
const headingRegex = /^###\s+Lessons\s+Learned$/m;
|
|
@@ -10941,24 +10942,22 @@ async function runAutoPromotion(directory, config) {
|
|
|
10941
10942
|
}
|
|
10942
10943
|
}
|
|
10943
10944
|
function createKnowledgeCuratorHook(directory, config, options = {}) {
|
|
10944
|
-
const handler = async (input,
|
|
10945
|
+
const handler = async (input, output) => {
|
|
10945
10946
|
pruneSeenRetroSections();
|
|
10946
10947
|
if (!config.enabled)
|
|
10947
10948
|
return;
|
|
10948
|
-
|
|
10949
|
+
const trigger = normalizeWriteTrigger(input, output);
|
|
10950
|
+
if (!trigger)
|
|
10951
|
+
return;
|
|
10952
|
+
const isPlanTrigger = isPlanPath(trigger.filePath);
|
|
10953
|
+
const isEvidenceTrigger = isEvidencePath(trigger.filePath) && !isPlanTrigger;
|
|
10954
|
+
if (!isPlanTrigger && !isEvidenceTrigger)
|
|
10949
10955
|
return;
|
|
10950
|
-
const sessionID = input?.sessionID ?? "default";
|
|
10951
|
-
const isEvidenceTrigger = isWriteToEvidenceFile(input) && !isWriteToSwarmPlan(input);
|
|
10952
10956
|
if (isEvidenceTrigger) {
|
|
10953
|
-
const
|
|
10954
|
-
const
|
|
10955
|
-
const rawFile = record.file;
|
|
10956
|
-
const filePath = typeof rawPath === "string" ? rawPath.replace(/\\/g, "/") : typeof rawFile === "string" ? rawFile.replace(/\\/g, "/") : null;
|
|
10957
|
-
if (!filePath)
|
|
10958
|
-
return;
|
|
10959
|
-
const evidenceKey = `evidence:${sessionID}:${filePath}`;
|
|
10957
|
+
const relativeEvidencePath = trigger.filePath.replace(/^.*\.swarm\//, "");
|
|
10958
|
+
const evidenceKey = `evidence:${trigger.sessionID}:${relativeEvidencePath}`;
|
|
10960
10959
|
const lastSeenEvidence = seenRetroSections.get(evidenceKey);
|
|
10961
|
-
const evidenceContent = await readSwarmFileAsync(directory,
|
|
10960
|
+
const evidenceContent = await readSwarmFileAsync(directory, relativeEvidencePath);
|
|
10962
10961
|
if (!evidenceContent)
|
|
10963
10962
|
return;
|
|
10964
10963
|
let evidenceData;
|
|
@@ -10986,7 +10985,7 @@ function createKnowledgeCuratorHook(directory, config, options = {}) {
|
|
|
10986
10985
|
const projectName2 = evidenceData.project_name ?? "unknown";
|
|
10987
10986
|
const phaseNumber2 = typeof evidenceData.phase_number === "number" ? evidenceData.phase_number : 1;
|
|
10988
10987
|
await _internals17.curateAndStoreSwarm(lessons, projectName2, { phase_number: phaseNumber2 }, directory, config, {
|
|
10989
|
-
llmDelegate: options.llmDelegateFactory?.(sessionID),
|
|
10988
|
+
llmDelegate: options.llmDelegateFactory?.(trigger.sessionID),
|
|
10990
10989
|
enrichmentQuota: options.enrichmentQuota
|
|
10991
10990
|
});
|
|
10992
10991
|
return;
|
|
@@ -10997,7 +10996,7 @@ function createKnowledgeCuratorHook(directory, config, options = {}) {
|
|
|
10997
10996
|
const section = extractRetrospectiveSection(planContent);
|
|
10998
10997
|
if (!section)
|
|
10999
10998
|
return;
|
|
11000
|
-
if (!checkRetroChanged(sessionID, section))
|
|
10999
|
+
if (!checkRetroChanged(trigger.sessionID, section))
|
|
11001
11000
|
return;
|
|
11002
11001
|
const allLessons = extractLessonsFromRetro(section);
|
|
11003
11002
|
if (allLessons.length === 0)
|
|
@@ -11011,7 +11010,7 @@ function createKnowledgeCuratorHook(directory, config, options = {}) {
|
|
|
11011
11010
|
const phaseMatch = /^Phase:\s*(\d+)/m.exec(planContent);
|
|
11012
11011
|
const phaseNumber = phaseMatch ? parseInt(phaseMatch[1], 10) : 1;
|
|
11013
11012
|
await _internals17.curateAndStoreSwarm(normalLessons, projectName, { phase_number: phaseNumber }, directory, config, {
|
|
11014
|
-
llmDelegate: options.llmDelegateFactory?.(sessionID),
|
|
11013
|
+
llmDelegate: options.llmDelegateFactory?.(trigger.sessionID),
|
|
11015
11014
|
enrichmentQuota: options.enrichmentQuota
|
|
11016
11015
|
});
|
|
11017
11016
|
};
|
|
@@ -31857,7 +31856,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
31857
31856
|
async function handleHelpCommand(ctx) {
|
|
31858
31857
|
const targetCommand = ctx.args.join(" ");
|
|
31859
31858
|
if (!targetCommand) {
|
|
31860
|
-
const { buildHelpText } = await import("./index-
|
|
31859
|
+
const { buildHelpText } = await import("./index-attgb1ma.js");
|
|
31861
31860
|
return buildHelpText();
|
|
31862
31861
|
}
|
|
31863
31862
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -31866,7 +31865,7 @@ async function handleHelpCommand(ctx) {
|
|
|
31866
31865
|
return _internals45.buildDetailedHelp(resolved.key, resolved.entry);
|
|
31867
31866
|
}
|
|
31868
31867
|
const similar = _internals45.findSimilarCommands(targetCommand);
|
|
31869
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
31868
|
+
const { buildHelpText: fullHelp } = await import("./index-attgb1ma.js");
|
|
31870
31869
|
if (similar.length > 0) {
|
|
31871
31870
|
return `Command '/swarm ${targetCommand}' not found.
|
|
31872
31871
|
|
|
@@ -31999,7 +31998,7 @@ var COMMAND_REGISTRY = {
|
|
|
31999
31998
|
},
|
|
32000
31999
|
"guardrail explain": {
|
|
32001
32000
|
handler: async (ctx) => {
|
|
32002
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
32001
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-cm08h4mt.js");
|
|
32003
32002
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
32004
32003
|
},
|
|
32005
32004
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
package/dist/cli/index.js
CHANGED
|
@@ -21,6 +21,7 @@ declare function hashContent(content: string): string;
|
|
|
21
21
|
* Exported for testing purposes only.
|
|
22
22
|
*/
|
|
23
23
|
export declare function isWriteToEvidenceFile(input: unknown): boolean;
|
|
24
|
+
export declare function isEvidencePath(filePath: string | undefined | null): boolean;
|
|
24
25
|
/** Build the v3-schema enrichment prompt for a single prose lesson. */
|
|
25
26
|
export declare function buildV3EnrichmentPrompt(lesson: string, category: string, tags: string[]): string;
|
|
26
27
|
/**
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.98.
|
|
72
|
+
version: "7.98.1",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -70430,48 +70430,43 @@ function recordSeenRetroSection(key, value, timestamp) {
|
|
|
70430
70430
|
function hashContent2(content) {
|
|
70431
70431
|
return createHash9("sha1").update(content).digest("hex");
|
|
70432
70432
|
}
|
|
70433
|
-
function isWriteToSwarmPlan(input) {
|
|
70434
|
-
if (typeof input !== "object" || input === null)
|
|
70435
|
-
return false;
|
|
70436
|
-
const record3 = input;
|
|
70437
|
-
const toolName = record3.toolName;
|
|
70438
|
-
if (typeof toolName !== "string")
|
|
70439
|
-
return false;
|
|
70440
|
-
if (!["write", "edit", "apply_patch", "swarm_apply_patch"].includes(toolName))
|
|
70441
|
-
return false;
|
|
70442
|
-
const rawPath = record3.path;
|
|
70443
|
-
const rawFile = record3.file;
|
|
70444
|
-
const pathField = typeof rawPath === "string" ? rawPath.replace(/\\/g, "/") : undefined;
|
|
70445
|
-
const fileField = typeof rawFile === "string" ? rawFile.replace(/\\/g, "/") : undefined;
|
|
70446
|
-
if (typeof pathField === "string" && pathField.includes(".swarm/plan.md")) {
|
|
70447
|
-
return true;
|
|
70448
|
-
}
|
|
70449
|
-
if (typeof fileField === "string" && fileField.includes(".swarm/plan.md")) {
|
|
70450
|
-
return true;
|
|
70451
|
-
}
|
|
70452
|
-
return false;
|
|
70453
|
-
}
|
|
70454
70433
|
function isWriteToEvidenceFile(input) {
|
|
70455
|
-
|
|
70456
|
-
|
|
70457
|
-
|
|
70458
|
-
|
|
70459
|
-
|
|
70460
|
-
|
|
70461
|
-
|
|
70434
|
+
const trigger = normalizeWriteTrigger(input);
|
|
70435
|
+
return isEvidencePath(trigger?.filePath);
|
|
70436
|
+
}
|
|
70437
|
+
function isRecord(value) {
|
|
70438
|
+
return typeof value === "object" && value !== null;
|
|
70439
|
+
}
|
|
70440
|
+
function normalizePathField(value) {
|
|
70441
|
+
return typeof value === "string" ? value.replace(/\\/g, "/") : null;
|
|
70442
|
+
}
|
|
70443
|
+
function firstPathFromRecord(record3) {
|
|
70444
|
+
return normalizePathField(record3.path) ?? normalizePathField(record3.filePath) ?? normalizePathField(record3.file);
|
|
70445
|
+
}
|
|
70446
|
+
function normalizeWriteTrigger(input, output) {
|
|
70447
|
+
if (!isRecord(input))
|
|
70448
|
+
return null;
|
|
70449
|
+
const toolName = typeof input.toolName === "string" ? input.toolName : typeof input.tool === "string" ? input.tool : null;
|
|
70450
|
+
if (!toolName || !WRITE_TOOLS.has(toolName))
|
|
70451
|
+
return null;
|
|
70452
|
+
const inputArgs = isRecord(input.args) ? input.args : null;
|
|
70453
|
+
const outputArgs = isRecord(output) && isRecord(output.args) ? output.args : null;
|
|
70454
|
+
const filePath = firstPathFromRecord(input) ?? (inputArgs ? firstPathFromRecord(inputArgs) : null) ?? (outputArgs ? firstPathFromRecord(outputArgs) : null);
|
|
70455
|
+
if (!filePath)
|
|
70456
|
+
return null;
|
|
70457
|
+
return {
|
|
70458
|
+
toolName,
|
|
70459
|
+
filePath,
|
|
70460
|
+
sessionID: typeof input.sessionID === "string" ? input.sessionID : "default"
|
|
70461
|
+
};
|
|
70462
|
+
}
|
|
70463
|
+
function isEvidencePath(filePath) {
|
|
70464
|
+
if (!filePath)
|
|
70462
70465
|
return false;
|
|
70463
|
-
|
|
70464
|
-
|
|
70465
|
-
|
|
70466
|
-
|
|
70467
|
-
const evidenceRegex = /\.swarm\/+evidence\/+/i;
|
|
70468
|
-
if (typeof pathField === "string" && evidenceRegex.test(pathField)) {
|
|
70469
|
-
return true;
|
|
70470
|
-
}
|
|
70471
|
-
if (typeof fileField === "string" && evidenceRegex.test(fileField)) {
|
|
70472
|
-
return true;
|
|
70473
|
-
}
|
|
70474
|
-
return false;
|
|
70466
|
+
return /(?:^|\/)\.swarm\/+evidence\//i.test(filePath);
|
|
70467
|
+
}
|
|
70468
|
+
function isPlanPath(filePath) {
|
|
70469
|
+
return filePath?.includes(".swarm/plan.md") ?? false;
|
|
70475
70470
|
}
|
|
70476
70471
|
function extractRetrospectiveSection(planContent) {
|
|
70477
70472
|
const headingRegex = /^###\s+Lessons\s+Learned$/m;
|
|
@@ -71153,24 +71148,22 @@ async function runAutoPromotion(directory, config3) {
|
|
|
71153
71148
|
}
|
|
71154
71149
|
}
|
|
71155
71150
|
function createKnowledgeCuratorHook(directory, config3, options = {}) {
|
|
71156
|
-
const handler = async (input,
|
|
71151
|
+
const handler = async (input, output) => {
|
|
71157
71152
|
pruneSeenRetroSections();
|
|
71158
71153
|
if (!config3.enabled)
|
|
71159
71154
|
return;
|
|
71160
|
-
|
|
71155
|
+
const trigger = normalizeWriteTrigger(input, output);
|
|
71156
|
+
if (!trigger)
|
|
71157
|
+
return;
|
|
71158
|
+
const isPlanTrigger = isPlanPath(trigger.filePath);
|
|
71159
|
+
const isEvidenceTrigger = isEvidencePath(trigger.filePath) && !isPlanTrigger;
|
|
71160
|
+
if (!isPlanTrigger && !isEvidenceTrigger)
|
|
71161
71161
|
return;
|
|
71162
|
-
const sessionID = input?.sessionID ?? "default";
|
|
71163
|
-
const isEvidenceTrigger = isWriteToEvidenceFile(input) && !isWriteToSwarmPlan(input);
|
|
71164
71162
|
if (isEvidenceTrigger) {
|
|
71165
|
-
const
|
|
71166
|
-
const
|
|
71167
|
-
const rawFile = record3.file;
|
|
71168
|
-
const filePath = typeof rawPath === "string" ? rawPath.replace(/\\/g, "/") : typeof rawFile === "string" ? rawFile.replace(/\\/g, "/") : null;
|
|
71169
|
-
if (!filePath)
|
|
71170
|
-
return;
|
|
71171
|
-
const evidenceKey = `evidence:${sessionID}:${filePath}`;
|
|
71163
|
+
const relativeEvidencePath = trigger.filePath.replace(/^.*\.swarm\//, "");
|
|
71164
|
+
const evidenceKey = `evidence:${trigger.sessionID}:${relativeEvidencePath}`;
|
|
71172
71165
|
const lastSeenEvidence = seenRetroSections.get(evidenceKey);
|
|
71173
|
-
const evidenceContent = await readSwarmFileAsync(directory,
|
|
71166
|
+
const evidenceContent = await readSwarmFileAsync(directory, relativeEvidencePath);
|
|
71174
71167
|
if (!evidenceContent)
|
|
71175
71168
|
return;
|
|
71176
71169
|
let evidenceData;
|
|
@@ -71198,7 +71191,7 @@ function createKnowledgeCuratorHook(directory, config3, options = {}) {
|
|
|
71198
71191
|
const projectName2 = evidenceData.project_name ?? "unknown";
|
|
71199
71192
|
const phaseNumber2 = typeof evidenceData.phase_number === "number" ? evidenceData.phase_number : 1;
|
|
71200
71193
|
await _internals35.curateAndStoreSwarm(lessons, projectName2, { phase_number: phaseNumber2 }, directory, config3, {
|
|
71201
|
-
llmDelegate: options.llmDelegateFactory?.(sessionID),
|
|
71194
|
+
llmDelegate: options.llmDelegateFactory?.(trigger.sessionID),
|
|
71202
71195
|
enrichmentQuota: options.enrichmentQuota
|
|
71203
71196
|
});
|
|
71204
71197
|
return;
|
|
@@ -71209,7 +71202,7 @@ function createKnowledgeCuratorHook(directory, config3, options = {}) {
|
|
|
71209
71202
|
const section = extractRetrospectiveSection(planContent);
|
|
71210
71203
|
if (!section)
|
|
71211
71204
|
return;
|
|
71212
|
-
if (!checkRetroChanged(sessionID, section))
|
|
71205
|
+
if (!checkRetroChanged(trigger.sessionID, section))
|
|
71213
71206
|
return;
|
|
71214
71207
|
const allLessons = extractLessonsFromRetro(section);
|
|
71215
71208
|
if (allLessons.length === 0)
|
|
@@ -71223,13 +71216,13 @@ function createKnowledgeCuratorHook(directory, config3, options = {}) {
|
|
|
71223
71216
|
const phaseMatch = /^Phase:\s*(\d+)/m.exec(planContent);
|
|
71224
71217
|
const phaseNumber = phaseMatch ? parseInt(phaseMatch[1], 10) : 1;
|
|
71225
71218
|
await _internals35.curateAndStoreSwarm(normalLessons, projectName, { phase_number: phaseNumber }, directory, config3, {
|
|
71226
|
-
llmDelegate: options.llmDelegateFactory?.(sessionID),
|
|
71219
|
+
llmDelegate: options.llmDelegateFactory?.(trigger.sessionID),
|
|
71227
71220
|
enrichmentQuota: options.enrichmentQuota
|
|
71228
71221
|
});
|
|
71229
71222
|
};
|
|
71230
71223
|
return safeHook(handler);
|
|
71231
71224
|
}
|
|
71232
|
-
var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, ENRICHMENT_BATCH_SIZE = 6, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals35;
|
|
71225
|
+
var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, WRITE_TOOLS, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, ENRICHMENT_BATCH_SIZE = 6, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals35;
|
|
71233
71226
|
var init_knowledge_curator = __esm(() => {
|
|
71234
71227
|
init_skill_improver_quota();
|
|
71235
71228
|
init_synonym_map();
|
|
@@ -71241,6 +71234,12 @@ var init_knowledge_curator = __esm(() => {
|
|
|
71241
71234
|
init_micro_reflector();
|
|
71242
71235
|
init_utils2();
|
|
71243
71236
|
seenRetroSections = new Map;
|
|
71237
|
+
WRITE_TOOLS = new Set([
|
|
71238
|
+
"write",
|
|
71239
|
+
"edit",
|
|
71240
|
+
"apply_patch",
|
|
71241
|
+
"swarm_apply_patch"
|
|
71242
|
+
]);
|
|
71244
71243
|
ENRICHMENT_ALLOWED_FIELDS = [
|
|
71245
71244
|
"triggers",
|
|
71246
71245
|
"required_actions",
|
|
@@ -125232,7 +125231,7 @@ init_state2();
|
|
|
125232
125231
|
init_delegation_gate();
|
|
125233
125232
|
init_normalize_tool_name();
|
|
125234
125233
|
import * as path154 from "node:path";
|
|
125235
|
-
var
|
|
125234
|
+
var WRITE_TOOLS2 = new Set(WRITE_TOOL_NAMES);
|
|
125236
125235
|
function createScopeGuardHook(config3, directory, injectAdvisory) {
|
|
125237
125236
|
const enabled = config3.enabled ?? true;
|
|
125238
125237
|
const _skipInTurbo = config3.skip_in_turbo ?? false;
|
|
@@ -125241,7 +125240,7 @@ function createScopeGuardHook(config3, directory, injectAdvisory) {
|
|
|
125241
125240
|
if (!enabled)
|
|
125242
125241
|
return;
|
|
125243
125242
|
const toolName = normalizeToolName(input.tool);
|
|
125244
|
-
if (!
|
|
125243
|
+
if (!WRITE_TOOLS2.has(toolName))
|
|
125245
125244
|
return;
|
|
125246
125245
|
const sessionId = input.sessionID;
|
|
125247
125246
|
const activeAgent = swarmState.activeAgent.get(sessionId);
|
|
@@ -135730,8 +135729,99 @@ function scanInvisibleFormatChars(text, fieldName) {
|
|
|
135730
135729
|
}
|
|
135731
135730
|
return findings;
|
|
135732
135731
|
}
|
|
135733
|
-
|
|
135734
|
-
|
|
135732
|
+
var CODE_SPAN_OR_FENCE_REGEX = /```[\s\S]*?```|~~~[\s\S]*?~~~|`[^`\n]*`/g;
|
|
135733
|
+
var CODE_WARNING_ONLY_PATTERNS = new Set([
|
|
135734
|
+
"backtick_execution",
|
|
135735
|
+
"shell_substitution",
|
|
135736
|
+
"disk_format",
|
|
135737
|
+
"force_kill",
|
|
135738
|
+
"process_group_kill",
|
|
135739
|
+
"kill_all_processes"
|
|
135740
|
+
]);
|
|
135741
|
+
function splitMarkdownCodeSegments(text) {
|
|
135742
|
+
const segments = [];
|
|
135743
|
+
let cursor = 0;
|
|
135744
|
+
for (const match of text.matchAll(CODE_SPAN_OR_FENCE_REGEX)) {
|
|
135745
|
+
const index = match.index ?? 0;
|
|
135746
|
+
if (index > cursor) {
|
|
135747
|
+
segments.push({ value: text.slice(cursor, index), isCode: false });
|
|
135748
|
+
}
|
|
135749
|
+
segments.push({ value: match[0], isCode: true });
|
|
135750
|
+
cursor = index + match[0].length;
|
|
135751
|
+
}
|
|
135752
|
+
if (cursor < text.length) {
|
|
135753
|
+
segments.push({ value: text.slice(cursor), isCode: false });
|
|
135754
|
+
}
|
|
135755
|
+
return segments.length === 0 ? [{ value: text, isCode: false }] : segments;
|
|
135756
|
+
}
|
|
135757
|
+
function getUnsafeInstructionMetadata(name) {
|
|
135758
|
+
const entry = UNSAFE_INSTRUCTION_PATTERNS.find((item) => item.name === name);
|
|
135759
|
+
return {
|
|
135760
|
+
description: entry?.description ?? "Destructive file removal command",
|
|
135761
|
+
severity: entry?.severity ?? "error"
|
|
135762
|
+
};
|
|
135763
|
+
}
|
|
135764
|
+
function tokenizeShellArgs(input) {
|
|
135765
|
+
const tokens = [];
|
|
135766
|
+
const tokenRegex = /"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+/g;
|
|
135767
|
+
for (const match of input.matchAll(tokenRegex)) {
|
|
135768
|
+
const token = match[1] ?? match[2] ?? match[0];
|
|
135769
|
+
if (token.length > 0)
|
|
135770
|
+
tokens.push(token);
|
|
135771
|
+
}
|
|
135772
|
+
return tokens;
|
|
135773
|
+
}
|
|
135774
|
+
function rmFlagHasShortOption(token, option) {
|
|
135775
|
+
return /^-[A-Za-z]+$/.test(token) && token.slice(1).includes(option);
|
|
135776
|
+
}
|
|
135777
|
+
function isRmOption(token) {
|
|
135778
|
+
return token.startsWith("-") && token !== "-";
|
|
135779
|
+
}
|
|
135780
|
+
function isDangerousRemovalTarget(rawTarget) {
|
|
135781
|
+
const target = rawTarget.replace(/\\/g, "/");
|
|
135782
|
+
if (/^[A-Za-z]:[/\\]?$/.test(target))
|
|
135783
|
+
return true;
|
|
135784
|
+
return target === "/" || target === "/*" || target === "." || target === ".." || target.startsWith(".swarm") || target.startsWith("/.swarm") || target.startsWith("~") || target.startsWith("$") || target.startsWith("${") || target.startsWith("%") || target.startsWith("/home") || target.startsWith("/Users") || target.startsWith("/etc") || target.startsWith("/bin") || target.startsWith("/sbin") || target.startsWith("/usr") || target.startsWith("/var") || /^[A-Za-z]:\/(?:Users|Windows)(?:\/|$)/.test(target);
|
|
135785
|
+
}
|
|
135786
|
+
function findDangerousRemovalCommands(text) {
|
|
135787
|
+
const commands = [];
|
|
135788
|
+
const rmCommandRegex = /\b(?<sudo>sudo\s+)?rm\b(?<args>[^\r\n`;&|]*)/gi;
|
|
135789
|
+
for (const match of text.matchAll(rmCommandRegex)) {
|
|
135790
|
+
const args2 = tokenizeShellArgs(match.groups?.args ?? "");
|
|
135791
|
+
const hasRecursive = args2.some((token) => token === "--recursive" || token === "-R" || rmFlagHasShortOption(token, "r") || rmFlagHasShortOption(token, "R"));
|
|
135792
|
+
const hasForce = args2.some((token) => token === "--force" || token === "-f" || rmFlagHasShortOption(token, "f"));
|
|
135793
|
+
if (!hasRecursive || !hasForce)
|
|
135794
|
+
continue;
|
|
135795
|
+
if (!args2.some((token) => !isRmOption(token) && isDangerousRemovalTarget(token))) {
|
|
135796
|
+
continue;
|
|
135797
|
+
}
|
|
135798
|
+
const pattern = match.groups?.sudo === undefined ? "destructive_file_removal" : "privileged_file_removal";
|
|
135799
|
+
commands.push({
|
|
135800
|
+
pattern,
|
|
135801
|
+
description: getUnsafeInstructionMetadata(pattern).description,
|
|
135802
|
+
match: match[0].trim().slice(0, 100)
|
|
135803
|
+
});
|
|
135804
|
+
}
|
|
135805
|
+
return commands;
|
|
135806
|
+
}
|
|
135807
|
+
function hasDangerousRemovalTarget(text) {
|
|
135808
|
+
return findDangerousRemovalCommands(text).length > 0;
|
|
135809
|
+
}
|
|
135810
|
+
function shouldScanUnsafePatternInSegment(entry, segment) {
|
|
135811
|
+
if (!segment.isCode)
|
|
135812
|
+
return true;
|
|
135813
|
+
if (CODE_WARNING_ONLY_PATTERNS.has(entry.name))
|
|
135814
|
+
return false;
|
|
135815
|
+
if (entry.name === "destructive_file_removal" || entry.name === "privileged_file_removal") {
|
|
135816
|
+
return false;
|
|
135817
|
+
}
|
|
135818
|
+
return true;
|
|
135819
|
+
}
|
|
135820
|
+
function getUnsafeScanSegments(field, value) {
|
|
135821
|
+
if (field !== "skill_body") {
|
|
135822
|
+
return [{ value, isCode: false }];
|
|
135823
|
+
}
|
|
135824
|
+
return splitMarkdownCodeSegments(value);
|
|
135735
135825
|
}
|
|
135736
135826
|
function scanPromptInjection(candidate, trustLevel = "low") {
|
|
135737
135827
|
const fields = extractCandidateFields(candidate);
|
|
@@ -135799,17 +135889,37 @@ function scanUnsafeInstructions(candidate, trustLevel = "low") {
|
|
|
135799
135889
|
const fieldsScanned = [];
|
|
135800
135890
|
for (const { field, value } of fields) {
|
|
135801
135891
|
fieldsScanned.push(field);
|
|
135802
|
-
const
|
|
135892
|
+
const scanSegments = getUnsafeScanSegments(field, value);
|
|
135893
|
+
if (field === "skill_body") {
|
|
135894
|
+
for (const segment of scanSegments) {
|
|
135895
|
+
if (!segment.isCode)
|
|
135896
|
+
continue;
|
|
135897
|
+
for (const command of findDangerousRemovalCommands(segment.value)) {
|
|
135898
|
+
findings.push({
|
|
135899
|
+
pattern: command.pattern,
|
|
135900
|
+
field,
|
|
135901
|
+
description: command.description,
|
|
135902
|
+
severity: "error",
|
|
135903
|
+
match: command.match
|
|
135904
|
+
});
|
|
135905
|
+
}
|
|
135906
|
+
}
|
|
135907
|
+
}
|
|
135803
135908
|
for (const entry of UNSAFE_INSTRUCTION_PATTERNS) {
|
|
135804
|
-
const
|
|
135805
|
-
|
|
135806
|
-
|
|
135807
|
-
|
|
135808
|
-
|
|
135809
|
-
|
|
135810
|
-
|
|
135811
|
-
|
|
135812
|
-
|
|
135909
|
+
for (const segment of scanSegments) {
|
|
135910
|
+
if (!shouldScanUnsafePatternInSegment(entry, segment))
|
|
135911
|
+
continue;
|
|
135912
|
+
const match = entry.pattern.exec(segment.value);
|
|
135913
|
+
if (match !== null) {
|
|
135914
|
+
findings.push({
|
|
135915
|
+
pattern: entry.name,
|
|
135916
|
+
field,
|
|
135917
|
+
description: entry.description,
|
|
135918
|
+
severity: entry.severity,
|
|
135919
|
+
match: match[0].slice(0, 100)
|
|
135920
|
+
});
|
|
135921
|
+
break;
|
|
135922
|
+
}
|
|
135813
135923
|
}
|
|
135814
135924
|
}
|
|
135815
135925
|
}
|
|
@@ -135973,7 +136083,9 @@ function evaluateCandidate(candidate, options) {
|
|
|
135973
136083
|
var _internals103 = {
|
|
135974
136084
|
getTimestamp: () => new Date().toISOString(),
|
|
135975
136085
|
computeSha256: (content) => createHash21("sha256").update(content).digest("hex"),
|
|
135976
|
-
|
|
136086
|
+
splitMarkdownCodeSegments,
|
|
136087
|
+
hasDangerousRemovalTarget,
|
|
136088
|
+
isDangerousRemovalTarget
|
|
135977
136089
|
};
|
|
135978
136090
|
|
|
135979
136091
|
// src/tools/external-skill-discover.ts
|
|
@@ -105,7 +105,13 @@ export declare const VALIDATION_RATE_LIMITS: {
|
|
|
105
105
|
/** Timeout for individual fetch operations in milliseconds. */
|
|
106
106
|
readonly fetch_timeout_ms: 30000;
|
|
107
107
|
};
|
|
108
|
-
|
|
108
|
+
interface MarkdownSegment {
|
|
109
|
+
value: string;
|
|
110
|
+
isCode: boolean;
|
|
111
|
+
}
|
|
112
|
+
declare function splitMarkdownCodeSegments(text: string): MarkdownSegment[];
|
|
113
|
+
declare function isDangerousRemovalTarget(rawTarget: string): boolean;
|
|
114
|
+
declare function hasDangerousRemovalTarget(text: string): boolean;
|
|
109
115
|
/**
|
|
110
116
|
* Scan an external skill candidate for prompt-injection patterns.
|
|
111
117
|
*
|
|
@@ -158,6 +164,8 @@ export declare function evaluateCandidate(candidate: ExternalSkillCandidate, opt
|
|
|
158
164
|
export declare const _internals: {
|
|
159
165
|
getTimestamp: () => string;
|
|
160
166
|
computeSha256: (content: string) => string;
|
|
161
|
-
|
|
167
|
+
splitMarkdownCodeSegments: typeof splitMarkdownCodeSegments;
|
|
168
|
+
hasDangerousRemovalTarget: typeof hasDangerousRemovalTarget;
|
|
169
|
+
isDangerousRemovalTarget: typeof isDangerousRemovalTarget;
|
|
162
170
|
};
|
|
163
171
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.98.
|
|
3
|
+
"version": "7.98.1",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|