opencode-swarm 6.41.0 → 6.41.2
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/index.js +1 -1
- package/dist/hooks/guardrails.d.ts +1 -1
- package/dist/index.js +75 -26
- package/dist/tools/declare-scope.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -32643,7 +32643,7 @@ async function executeWriteRetro(args, directory) {
|
|
|
32643
32643
|
var write_retro = createSwarmTool({
|
|
32644
32644
|
description: "Write a retrospective evidence bundle for a completed phase. " + "Accepts flat retro fields and writes a correctly-wrapped EvidenceBundle to " + ".swarm/evidence/retro-{phase}/evidence.json. " + "Use this instead of manually writing retro JSON to avoid schema validation failures in phase_complete.",
|
|
32645
32645
|
args: {
|
|
32646
|
-
phase: tool.schema.number().int().
|
|
32646
|
+
phase: tool.schema.number().int().min(1).max(99).describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
32647
32647
|
summary: tool.schema.string().describe("Human-readable summary of the phase"),
|
|
32648
32648
|
task_count: tool.schema.number().int().min(1).max(9999).describe("Count of tasks completed in this phase"),
|
|
32649
32649
|
task_complexity: tool.schema.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
|
|
@@ -106,7 +106,7 @@ export declare function validateAndRecordAttestation(dir: string, findingId: str
|
|
|
106
106
|
/**
|
|
107
107
|
* Checks whether the given agent is authorised to write to the given file path.
|
|
108
108
|
*/
|
|
109
|
-
export declare function checkFileAuthority(agentName: string, filePath: string,
|
|
109
|
+
export declare function checkFileAuthority(agentName: string, filePath: string, cwd: string): {
|
|
110
110
|
allowed: true;
|
|
111
111
|
} | {
|
|
112
112
|
allowed: false;
|
package/dist/index.js
CHANGED
|
@@ -41431,6 +41431,9 @@ function isValidTaskId(taskId) {
|
|
|
41431
41431
|
if (taskId === null || taskId === undefined) {
|
|
41432
41432
|
return false;
|
|
41433
41433
|
}
|
|
41434
|
+
if (typeof taskId !== "string") {
|
|
41435
|
+
return false;
|
|
41436
|
+
}
|
|
41434
41437
|
const trimmed = taskId.trim();
|
|
41435
41438
|
return trimmed.length > 0;
|
|
41436
41439
|
}
|
|
@@ -47071,7 +47074,7 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47071
47074
|
var write_retro = createSwarmTool({
|
|
47072
47075
|
description: "Write a retrospective evidence bundle for a completed phase. " + "Accepts flat retro fields and writes a correctly-wrapped EvidenceBundle to " + ".swarm/evidence/retro-{phase}/evidence.json. " + "Use this instead of manually writing retro JSON to avoid schema validation failures in phase_complete.",
|
|
47073
47076
|
args: {
|
|
47074
|
-
phase: tool.schema.number().int().
|
|
47077
|
+
phase: tool.schema.number().int().min(1).max(99).describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
47075
47078
|
summary: tool.schema.string().describe("Human-readable summary of the phase"),
|
|
47076
47079
|
task_count: tool.schema.number().int().min(1).max(9999).describe("Count of tasks completed in this phase"),
|
|
47077
47080
|
task_complexity: tool.schema.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
|
|
@@ -52386,10 +52389,11 @@ function getCurrentTaskId(sessionId) {
|
|
|
52386
52389
|
const session = swarmState.agentSessions.get(sessionId);
|
|
52387
52390
|
return session?.currentTaskId ?? `${sessionId}:unknown`;
|
|
52388
52391
|
}
|
|
52389
|
-
function isInDeclaredScope(filePath, scopeEntries) {
|
|
52390
|
-
const
|
|
52392
|
+
function isInDeclaredScope(filePath, scopeEntries, cwd) {
|
|
52393
|
+
const dir = cwd ?? process.cwd();
|
|
52394
|
+
const resolvedFile = path32.resolve(dir, filePath);
|
|
52391
52395
|
return scopeEntries.some((scope) => {
|
|
52392
|
-
const resolvedScope = path32.resolve(scope);
|
|
52396
|
+
const resolvedScope = path32.resolve(dir, scope);
|
|
52393
52397
|
if (resolvedFile === resolvedScope)
|
|
52394
52398
|
return true;
|
|
52395
52399
|
const rel = path32.relative(resolvedScope, resolvedFile);
|
|
@@ -52847,7 +52851,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
52847
52851
|
}
|
|
52848
52852
|
session.partialGateWarningsIssuedForTask?.delete(session.currentTaskId);
|
|
52849
52853
|
if (session.declaredCoderScope !== null) {
|
|
52850
|
-
const undeclaredFiles = session.modifiedFilesThisCoderTask.map((f) => f.replace(/[\r\n\t]/g, "_")).filter((f) => !isInDeclaredScope(f, session.declaredCoderScope));
|
|
52854
|
+
const undeclaredFiles = session.modifiedFilesThisCoderTask.map((f) => f.replace(/[\r\n\t]/g, "_")).filter((f) => !isInDeclaredScope(f, session.declaredCoderScope, directory));
|
|
52851
52855
|
if (undeclaredFiles.length >= 1) {
|
|
52852
52856
|
const safeTaskId = String(session.currentTaskId ?? "").replace(/[\r\n\t]/g, "_");
|
|
52853
52857
|
session.lastScopeViolation = `Scope violation for task ${safeTaskId}: ` + `${undeclaredFiles.length} undeclared files modified: ` + undeclaredFiles.join(", ");
|
|
@@ -53290,9 +53294,11 @@ var AGENT_AUTHORITY_RULES = {
|
|
|
53290
53294
|
blockedZones: ["generated"]
|
|
53291
53295
|
}
|
|
53292
53296
|
};
|
|
53293
|
-
function checkFileAuthority(agentName, filePath,
|
|
53297
|
+
function checkFileAuthority(agentName, filePath, cwd) {
|
|
53294
53298
|
const normalizedAgent = agentName.toLowerCase();
|
|
53295
|
-
const
|
|
53299
|
+
const dir = cwd || process.cwd();
|
|
53300
|
+
const resolved = path32.resolve(dir, filePath);
|
|
53301
|
+
const normalizedPath = path32.relative(dir, resolved).replace(/\\/g, "/");
|
|
53296
53302
|
const rules = AGENT_AUTHORITY_RULES[normalizedAgent];
|
|
53297
53303
|
if (!rules) {
|
|
53298
53304
|
return { allowed: false, reason: `Unknown agent: ${agentName}` };
|
|
@@ -57670,6 +57676,19 @@ function parseFilePaths(description, filesTouched) {
|
|
|
57670
57676
|
}
|
|
57671
57677
|
return filePaths;
|
|
57672
57678
|
}
|
|
57679
|
+
function buildVerifySummary(tasksChecked, tasksSkipped, tasksBlocked) {
|
|
57680
|
+
if (tasksBlocked > 0) {
|
|
57681
|
+
return `Blocked: ${tasksBlocked} task(s) with missing identifiers`;
|
|
57682
|
+
}
|
|
57683
|
+
const verified = tasksChecked - tasksSkipped;
|
|
57684
|
+
if (tasksSkipped === tasksChecked) {
|
|
57685
|
+
return `All ${tasksChecked} completed task(s) skipped \u2014 research/inventory tasks`;
|
|
57686
|
+
}
|
|
57687
|
+
if (tasksSkipped > 0) {
|
|
57688
|
+
return `${verified} task(s) verified, ${tasksSkipped} skipped (research tasks)`;
|
|
57689
|
+
}
|
|
57690
|
+
return `All ${tasksChecked} completed tasks verified successfully`;
|
|
57691
|
+
}
|
|
57673
57692
|
async function executeCompletionVerify(args2, directory) {
|
|
57674
57693
|
const phase = Number(args2.phase);
|
|
57675
57694
|
if (Number.isNaN(phase) || phase < 1 || !Number.isFinite(phase)) {
|
|
@@ -57731,7 +57750,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
57731
57750
|
return JSON.stringify(result2, null, 2);
|
|
57732
57751
|
}
|
|
57733
57752
|
let tasksChecked = 0;
|
|
57734
|
-
|
|
57753
|
+
let tasksSkipped = 0;
|
|
57735
57754
|
let tasksBlocked = 0;
|
|
57736
57755
|
const blockedTasks = [];
|
|
57737
57756
|
for (const task of targetPhase.tasks) {
|
|
@@ -57742,13 +57761,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
57742
57761
|
const fileTargets = parseFilePaths(task.description, task.files_touched);
|
|
57743
57762
|
const identifiers = parseIdentifiers(task.description);
|
|
57744
57763
|
if (fileTargets.length === 0) {
|
|
57745
|
-
|
|
57746
|
-
task_id: task.id,
|
|
57747
|
-
identifier: "",
|
|
57748
|
-
file_path: "",
|
|
57749
|
-
reason: "No file targets \u2014 cannot verify completion without files_touched"
|
|
57750
|
-
});
|
|
57751
|
-
tasksBlocked++;
|
|
57764
|
+
tasksSkipped++;
|
|
57752
57765
|
continue;
|
|
57753
57766
|
}
|
|
57754
57767
|
if (identifiers.length === 0) {
|
|
@@ -57766,6 +57779,19 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
57766
57779
|
for (const filePath of fileTargets) {
|
|
57767
57780
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
57768
57781
|
const resolvedPath = path45.resolve(directory, normalizedPath);
|
|
57782
|
+
const projectRoot = path45.resolve(directory);
|
|
57783
|
+
const relative6 = path45.relative(projectRoot, resolvedPath);
|
|
57784
|
+
const withinProject = relative6 === "" || !relative6.startsWith("..") && !path45.isAbsolute(relative6);
|
|
57785
|
+
if (!withinProject) {
|
|
57786
|
+
blockedTasks.push({
|
|
57787
|
+
task_id: task.id,
|
|
57788
|
+
identifier: "",
|
|
57789
|
+
file_path: filePath,
|
|
57790
|
+
reason: `File path '${filePath}' escapes the project directory \u2014 cannot verify completion`
|
|
57791
|
+
});
|
|
57792
|
+
hasFileReadFailure = true;
|
|
57793
|
+
continue;
|
|
57794
|
+
}
|
|
57769
57795
|
let fileContent;
|
|
57770
57796
|
try {
|
|
57771
57797
|
fileContent = fs33.readFileSync(resolvedPath, "utf-8");
|
|
@@ -57824,7 +57850,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
57824
57850
|
timestamp: now,
|
|
57825
57851
|
agent: "completion_verify",
|
|
57826
57852
|
verdict: tasksBlocked === 0 ? "pass" : "fail",
|
|
57827
|
-
summary:
|
|
57853
|
+
summary: buildVerifySummary(tasksChecked, tasksSkipped, tasksBlocked),
|
|
57828
57854
|
phase,
|
|
57829
57855
|
tasks_checked: tasksChecked,
|
|
57830
57856
|
tasks_skipped: tasksSkipped,
|
|
@@ -58966,7 +58992,29 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
58966
58992
|
};
|
|
58967
58993
|
}
|
|
58968
58994
|
}
|
|
58969
|
-
const
|
|
58995
|
+
const rawMergedFiles = [...args2.files, ...args2.whitelist ?? []];
|
|
58996
|
+
const warnings = [];
|
|
58997
|
+
const normalizeErrors = [];
|
|
58998
|
+
const dir = normalizedDir || fallbackDir || process.cwd();
|
|
58999
|
+
const mergedFiles = rawMergedFiles.map((file3) => {
|
|
59000
|
+
if (path48.isAbsolute(file3)) {
|
|
59001
|
+
const relativePath = path48.relative(dir, file3).replace(/\\/g, "/");
|
|
59002
|
+
if (relativePath.startsWith("..")) {
|
|
59003
|
+
normalizeErrors.push(`Path '${file3}' resolves outside the project directory`);
|
|
59004
|
+
return file3;
|
|
59005
|
+
}
|
|
59006
|
+
warnings.push(`Absolute path normalized to relative: '${relativePath}' (was '${file3}')`);
|
|
59007
|
+
return relativePath;
|
|
59008
|
+
}
|
|
59009
|
+
return file3;
|
|
59010
|
+
});
|
|
59011
|
+
if (normalizeErrors.length > 0) {
|
|
59012
|
+
return {
|
|
59013
|
+
success: false,
|
|
59014
|
+
message: "Validation failed",
|
|
59015
|
+
errors: normalizeErrors
|
|
59016
|
+
};
|
|
59017
|
+
}
|
|
58970
59018
|
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
58971
59019
|
session.declaredCoderScope = mergedFiles;
|
|
58972
59020
|
session.lastScopeViolation = null;
|
|
@@ -58975,7 +59023,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
58975
59023
|
success: true,
|
|
58976
59024
|
message: "Scope declared successfully",
|
|
58977
59025
|
taskId: args2.taskId,
|
|
58978
|
-
fileCount: mergedFiles.length
|
|
59026
|
+
fileCount: mergedFiles.length,
|
|
59027
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
58979
59028
|
};
|
|
58980
59029
|
}
|
|
58981
59030
|
var declare_scope = createSwarmTool({
|
|
@@ -61480,7 +61529,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
61480
61529
|
var phase_complete = createSwarmTool({
|
|
61481
61530
|
description: "Mark a phase as complete and track which agents were dispatched. Used for phase completion gating and tracking. Accepts phase number and optional summary. Returns list of agents that were dispatched.",
|
|
61482
61531
|
args: {
|
|
61483
|
-
phase: tool.schema.number().describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
61532
|
+
phase: tool.schema.number().int().min(1).describe("The phase number being completed \u2014 a positive integer (e.g., 1, 2, 3)"),
|
|
61484
61533
|
summary: tool.schema.string().optional().describe("Optional summary of what was accomplished in this phase"),
|
|
61485
61534
|
sessionID: tool.schema.string().optional().describe("Session ID for tracking state (auto-provided by plugin context)")
|
|
61486
61535
|
},
|
|
@@ -64018,13 +64067,13 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
64018
64067
|
resolved = path56.resolve(baseDir, inputPath);
|
|
64019
64068
|
}
|
|
64020
64069
|
const workspaceResolved = path56.resolve(workspaceDir);
|
|
64021
|
-
let
|
|
64070
|
+
let relative8;
|
|
64022
64071
|
if (isWinAbs) {
|
|
64023
|
-
|
|
64072
|
+
relative8 = path56.win32.relative(workspaceResolved, resolved);
|
|
64024
64073
|
} else {
|
|
64025
|
-
|
|
64074
|
+
relative8 = path56.relative(workspaceResolved, resolved);
|
|
64026
64075
|
}
|
|
64027
|
-
if (
|
|
64076
|
+
if (relative8.startsWith("..")) {
|
|
64028
64077
|
return "path traversal detected";
|
|
64029
64078
|
}
|
|
64030
64079
|
return null;
|
|
@@ -64933,7 +64982,7 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
64933
64982
|
success: false,
|
|
64934
64983
|
message: "Plan rejected: invalid phase or task IDs",
|
|
64935
64984
|
errors: validationErrors,
|
|
64936
|
-
recovery_guidance: "
|
|
64985
|
+
recovery_guidance: "Phase IDs must be positive integers: 1, 2, 3 (not 0, -1, or decimals). " + 'Task IDs must use N.M format: "1.1", "2.3", "3.1". ' + "Call save_plan again with corrected ids. " + "Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
64937
64986
|
};
|
|
64938
64987
|
}
|
|
64939
64988
|
const placeholderIssues = detectPlaceholderContent(args2);
|
|
@@ -65046,7 +65095,7 @@ var save_plan = createSwarmTool({
|
|
|
65046
65095
|
title: tool.schema.string().min(1).describe("Plan title \u2014 the REAL project name from the spec. NOT a placeholder like [Project]."),
|
|
65047
65096
|
swarm_id: tool.schema.string().min(1).describe('Swarm identifier (e.g. "mega")'),
|
|
65048
65097
|
phases: tool.schema.array(tool.schema.object({
|
|
65049
|
-
id: tool.schema.number().int().
|
|
65098
|
+
id: tool.schema.number().int().min(1).describe("Phase number \u2014 a positive integer starting at 1. Use 1, 2, 3, etc."),
|
|
65050
65099
|
name: tool.schema.string().min(1).describe("Descriptive phase name derived from the spec"),
|
|
65051
65100
|
tasks: tool.schema.array(tool.schema.object({
|
|
65052
65101
|
id: tool.schema.string().min(1).regex(/^\d+\.\d+(\.\d+)*$/, 'Task ID must be in N.M format, e.g. "1.1"').describe('Task ID in N.M format, e.g. "1.1", "2.3"'),
|
|
@@ -67629,7 +67678,7 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
67629
67678
|
var write_drift_evidence = createSwarmTool({
|
|
67630
67679
|
description: "Write drift verification evidence for a completed phase. " + "Normalizes verdict (APPROVED->approved, NEEDS_REVISION->rejected) and writes " + "a gate-contract formatted EvidenceBundle to .swarm/evidence/{phase}/drift-verifier.json. " + "Use this after critic_drift_verifier delegation to persist the verification result.",
|
|
67631
67680
|
args: {
|
|
67632
|
-
phase: tool.schema.number().int().
|
|
67681
|
+
phase: tool.schema.number().int().min(1).describe("The phase number for the drift verification (e.g., 1, 2, 3)"),
|
|
67633
67682
|
verdict: tool.schema.enum(["APPROVED", "NEEDS_REVISION"]).describe("Verdict of the drift verification: 'APPROVED' or 'NEEDS_REVISION'"),
|
|
67634
67683
|
summary: tool.schema.string().describe("Human-readable summary of the drift verification")
|
|
67635
67684
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.41.
|
|
3
|
+
"version": "6.41.2",
|
|
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",
|