opencode-swarm 7.21.0 → 7.21.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/index.js +1 -1
- package/dist/index.js +49 -61
- package/dist/tools/update-task-status.d.ts +4 -5
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.21.
|
|
37
|
+
version: "7.21.1",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var package_default;
|
|
|
33
33
|
var init_package = __esm(() => {
|
|
34
34
|
package_default = {
|
|
35
35
|
name: "opencode-swarm",
|
|
36
|
-
version: "7.21.
|
|
36
|
+
version: "7.21.1",
|
|
37
37
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
38
38
|
main: "dist/index.js",
|
|
39
39
|
types: "dist/index.d.ts",
|
|
@@ -90226,6 +90226,7 @@ function phaseIsExplicitlyNonCode(directory, phase) {
|
|
|
90226
90226
|
}
|
|
90227
90227
|
|
|
90228
90228
|
// src/tools/phase-complete.ts
|
|
90229
|
+
init_gate_evidence();
|
|
90229
90230
|
init_curator();
|
|
90230
90231
|
init_knowledge_curator();
|
|
90231
90232
|
init_knowledge_reader();
|
|
@@ -90761,6 +90762,23 @@ function safeWarn(message, error93) {
|
|
|
90761
90762
|
warn(message, error93 instanceof Error ? error93.message : String(error93));
|
|
90762
90763
|
} catch {}
|
|
90763
90764
|
}
|
|
90765
|
+
var TASK_GATE_INFERABLE_AGENTS = new Set([
|
|
90766
|
+
"coder",
|
|
90767
|
+
"reviewer",
|
|
90768
|
+
"test_engineer"
|
|
90769
|
+
]);
|
|
90770
|
+
function canInferMissingAgentsFromTaskGates(agentsMissing) {
|
|
90771
|
+
return agentsMissing.every((agent) => TASK_GATE_INFERABLE_AGENTS.has(agent));
|
|
90772
|
+
}
|
|
90773
|
+
async function allCompletedTasksHavePassedGateEvidence(directory, tasks) {
|
|
90774
|
+
for (const task of tasks) {
|
|
90775
|
+
if (task.status !== "completed")
|
|
90776
|
+
return false;
|
|
90777
|
+
if (!await hasPassedAllGates(directory, task.id))
|
|
90778
|
+
return false;
|
|
90779
|
+
}
|
|
90780
|
+
return tasks.length > 0;
|
|
90781
|
+
}
|
|
90764
90782
|
function collectCrossSessionDispatchedAgents(phaseReferenceTimestamp, callerSessionId) {
|
|
90765
90783
|
const agents = new Set;
|
|
90766
90784
|
const contributorSessionIds = [];
|
|
@@ -90772,12 +90790,9 @@ function collectCrossSessionDispatchedAgents(phaseReferenceTimestamp, callerSess
|
|
|
90772
90790
|
agents.add(agent);
|
|
90773
90791
|
}
|
|
90774
90792
|
}
|
|
90775
|
-
const
|
|
90776
|
-
|
|
90777
|
-
|
|
90778
|
-
agents.add(stripKnownSwarmPrefix(delegation.from));
|
|
90779
|
-
agents.add(stripKnownSwarmPrefix(delegation.to));
|
|
90780
|
-
}
|
|
90793
|
+
for (const delegation of _getDelegationsSince(callerSessionId, phaseReferenceTimestamp)) {
|
|
90794
|
+
agents.add(stripKnownSwarmPrefix(delegation.from));
|
|
90795
|
+
agents.add(stripKnownSwarmPrefix(delegation.to));
|
|
90781
90796
|
}
|
|
90782
90797
|
}
|
|
90783
90798
|
for (const [sessionId, session] of swarmState.agentSessions) {
|
|
@@ -90796,17 +90811,24 @@ function collectCrossSessionDispatchedAgents(phaseReferenceTimestamp, callerSess
|
|
|
90796
90811
|
agents.add(agent);
|
|
90797
90812
|
}
|
|
90798
90813
|
}
|
|
90799
|
-
const
|
|
90800
|
-
|
|
90801
|
-
|
|
90802
|
-
agents.add(stripKnownSwarmPrefix(delegation.from));
|
|
90803
|
-
agents.add(stripKnownSwarmPrefix(delegation.to));
|
|
90804
|
-
}
|
|
90814
|
+
for (const delegation of _getDelegationsSince(sessionId, phaseReferenceTimestamp)) {
|
|
90815
|
+
agents.add(stripKnownSwarmPrefix(delegation.from));
|
|
90816
|
+
agents.add(stripKnownSwarmPrefix(delegation.to));
|
|
90805
90817
|
}
|
|
90806
90818
|
}
|
|
90807
90819
|
}
|
|
90808
90820
|
return { agents, contributorSessionIds };
|
|
90809
90821
|
}
|
|
90822
|
+
function _getDelegationsSince(sessionID, sinceTimestamp) {
|
|
90823
|
+
const chain = swarmState.delegationChains.get(sessionID);
|
|
90824
|
+
if (!chain) {
|
|
90825
|
+
return [];
|
|
90826
|
+
}
|
|
90827
|
+
if (sinceTimestamp === 0) {
|
|
90828
|
+
return chain;
|
|
90829
|
+
}
|
|
90830
|
+
return chain.filter((entry) => entry.timestamp > sinceTimestamp);
|
|
90831
|
+
}
|
|
90810
90832
|
function isValidRetroEntry(entry, phase) {
|
|
90811
90833
|
return entry.type === "retrospective" && "phase_number" in entry && entry.phase_number === phase && "verdict" in entry && entry.verdict === "pass";
|
|
90812
90834
|
}
|
|
@@ -91752,8 +91774,8 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
|
|
|
91752
91774
|
const planRaw = fs84.readFileSync(planPath, "utf-8");
|
|
91753
91775
|
const plan = JSON.parse(planRaw);
|
|
91754
91776
|
const targetPhase = plan.phases.find((p) => p.id === phase);
|
|
91755
|
-
if (targetPhase && targetPhase.tasks.length > 0 &&
|
|
91756
|
-
warnings.push(`Agent dispatch fallback: all ${targetPhase.tasks.length} tasks in phase ${phase} are completed in plan.json. Clearing missing agents: ${agentsMissing.join(", ")}.`);
|
|
91777
|
+
if (targetPhase && targetPhase.tasks.length > 0 && canInferMissingAgentsFromTaskGates(agentsMissing) && await allCompletedTasksHavePassedGateEvidence(dir, targetPhase.tasks)) {
|
|
91778
|
+
warnings.push(`Agent dispatch fallback: all ${targetPhase.tasks.length} tasks in phase ${phase} are completed in plan.json and durable gate evidence passed. Clearing missing agents: ${agentsMissing.join(", ")}.`);
|
|
91757
91779
|
agentsMissing = [];
|
|
91758
91780
|
}
|
|
91759
91781
|
} catch {}
|
|
@@ -103343,6 +103365,13 @@ function matchesTier3Pattern2(files) {
|
|
|
103343
103365
|
}
|
|
103344
103366
|
return false;
|
|
103345
103367
|
}
|
|
103368
|
+
function hasPassedDurableGateEvidence(workingDirectory, taskId) {
|
|
103369
|
+
const evidence = readTaskEvidenceRaw(workingDirectory, taskId);
|
|
103370
|
+
if (!evidence || !Array.isArray(evidence.required_gates) || evidence.required_gates.length === 0) {
|
|
103371
|
+
return false;
|
|
103372
|
+
}
|
|
103373
|
+
return evidence.required_gates.every((gate) => evidence.gates?.[gate] != null);
|
|
103374
|
+
}
|
|
103346
103375
|
function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = false, sessionID) {
|
|
103347
103376
|
try {
|
|
103348
103377
|
let skipStandardTurboBypass = false;
|
|
@@ -103389,12 +103418,11 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
103389
103418
|
try {
|
|
103390
103419
|
const evidence = readTaskEvidenceRaw(resolvedDir, taskId);
|
|
103391
103420
|
if (evidence === null) {} else if (evidence.required_gates && Array.isArray(evidence.required_gates) && evidence.gates) {
|
|
103392
|
-
|
|
103393
|
-
if (allGatesMet) {
|
|
103421
|
+
if (evidence.required_gates.length > 0 && evidence.required_gates.every((gate) => evidence.gates[gate] != null)) {
|
|
103394
103422
|
return { blocked: false, reason: "" };
|
|
103395
103423
|
}
|
|
103396
103424
|
const missingGates = evidence.required_gates.filter((gate) => evidence.gates[gate] == null);
|
|
103397
|
-
evidenceIncompleteReason = `Task ${taskId} is missing required gates: [${missingGates.join(", ")}]. ` + `Required: [${evidence.required_gates.join(", ")}]. ` + `Completed: [${Object.keys(evidence.gates).join(", ")}]. ` + `Delegate the missing gate agents before marking task as completed.`;
|
|
103425
|
+
evidenceIncompleteReason = evidence.required_gates.length === 0 ? `Task ${taskId} has an evidence file with no required gates. Delegate reviewer and test_engineer before marking task as completed.` : `Task ${taskId} is missing required gates: [${missingGates.join(", ")}]. ` + `Required: [${evidence.required_gates.join(", ")}]. ` + `Completed: [${Object.keys(evidence.gates).join(", ")}]. ` + `Delegate the missing gate agents before marking task as completed.`;
|
|
103398
103426
|
}
|
|
103399
103427
|
} catch (error93) {
|
|
103400
103428
|
console.warn(`[gate-evidence] Evidence file for task ${taskId} is corrupt or unreadable:`, error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -103404,7 +103432,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
103404
103432
|
reason: `Evidence file for task ${taskId} is corrupt or unreadable. ` + `Fix the file at .swarm/evidence/${taskId}.json or delete it to fall through to session state.`
|
|
103405
103433
|
};
|
|
103406
103434
|
}
|
|
103407
|
-
if (swarmState.agentSessions.size === 0) {
|
|
103435
|
+
if (swarmState.agentSessions.size === 0 && !evidenceIncompleteReason) {
|
|
103408
103436
|
return { blocked: false, reason: "" };
|
|
103409
103437
|
}
|
|
103410
103438
|
let validSessionCount = 0;
|
|
@@ -103421,7 +103449,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
103421
103449
|
return { blocked: false, reason: "" };
|
|
103422
103450
|
}
|
|
103423
103451
|
}
|
|
103424
|
-
if (validSessionCount === 0) {
|
|
103452
|
+
if (validSessionCount === 0 && !evidenceIncompleteReason) {
|
|
103425
103453
|
return { blocked: false, reason: "" };
|
|
103426
103454
|
}
|
|
103427
103455
|
const stateEntries = [];
|
|
@@ -103438,7 +103466,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
103438
103466
|
const plan = JSON.parse(planRaw);
|
|
103439
103467
|
for (const planPhase of plan.phases ?? []) {
|
|
103440
103468
|
for (const task of planPhase.tasks ?? []) {
|
|
103441
|
-
if (task.id === taskId && task.status === "completed") {
|
|
103469
|
+
if (task.id === taskId && task.status === "completed" && hasPassedDurableGateEvidence(resolvedDir2, taskId)) {
|
|
103442
103470
|
return { blocked: false, reason: "" };
|
|
103443
103471
|
}
|
|
103444
103472
|
}
|
|
@@ -103459,26 +103487,6 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
103459
103487
|
}
|
|
103460
103488
|
}
|
|
103461
103489
|
}
|
|
103462
|
-
if (!hasReviewer && !hasTestEngineer) {
|
|
103463
|
-
for (const [, chain] of swarmState.delegationChains) {
|
|
103464
|
-
let lastCoderIndex = -1;
|
|
103465
|
-
for (let i2 = chain.length - 1;i2 >= 0; i2--) {
|
|
103466
|
-
const target = stripKnownSwarmPrefix(chain[i2].to);
|
|
103467
|
-
if (target === "coder") {
|
|
103468
|
-
lastCoderIndex = i2;
|
|
103469
|
-
break;
|
|
103470
|
-
}
|
|
103471
|
-
}
|
|
103472
|
-
const searchStart = lastCoderIndex === -1 ? 0 : lastCoderIndex + 1;
|
|
103473
|
-
for (let i2 = searchStart;i2 < chain.length; i2++) {
|
|
103474
|
-
const target = stripKnownSwarmPrefix(chain[i2].to);
|
|
103475
|
-
if (target === "reviewer")
|
|
103476
|
-
hasReviewer = true;
|
|
103477
|
-
if (target === "test_engineer")
|
|
103478
|
-
hasTestEngineer = true;
|
|
103479
|
-
}
|
|
103480
|
-
}
|
|
103481
|
-
}
|
|
103482
103490
|
if (hasReviewer && hasTestEngineer) {
|
|
103483
103491
|
return { blocked: false, reason: "" };
|
|
103484
103492
|
}
|
|
@@ -103521,26 +103529,6 @@ function recoverTaskStateFromDelegations(taskId) {
|
|
|
103521
103529
|
}
|
|
103522
103530
|
}
|
|
103523
103531
|
}
|
|
103524
|
-
if (!hasReviewer && !hasTestEngineer) {
|
|
103525
|
-
for (const [, chain] of swarmState.delegationChains) {
|
|
103526
|
-
let lastCoderIndex = -1;
|
|
103527
|
-
for (let i2 = chain.length - 1;i2 >= 0; i2--) {
|
|
103528
|
-
const target = stripKnownSwarmPrefix(chain[i2].to);
|
|
103529
|
-
if (target === "coder") {
|
|
103530
|
-
lastCoderIndex = i2;
|
|
103531
|
-
break;
|
|
103532
|
-
}
|
|
103533
|
-
}
|
|
103534
|
-
const searchStart = lastCoderIndex === -1 ? 0 : lastCoderIndex + 1;
|
|
103535
|
-
for (let i2 = searchStart;i2 < chain.length; i2++) {
|
|
103536
|
-
const target = stripKnownSwarmPrefix(chain[i2].to);
|
|
103537
|
-
if (target === "reviewer")
|
|
103538
|
-
hasReviewer = true;
|
|
103539
|
-
if (target === "test_engineer")
|
|
103540
|
-
hasTestEngineer = true;
|
|
103541
|
-
}
|
|
103542
|
-
}
|
|
103543
|
-
}
|
|
103544
103532
|
if (!hasReviewer && !hasTestEngineer)
|
|
103545
103533
|
return;
|
|
103546
103534
|
for (const [, session] of swarmState.agentSessions) {
|
|
@@ -66,11 +66,10 @@ export declare function checkReviewerGate(taskId: string, workingDirectory?: str
|
|
|
66
66
|
export declare function checkReviewerGateWithScope(taskId: string, workingDirectory?: string, sessionID?: string): Promise<ReviewerGateResult>;
|
|
67
67
|
/**
|
|
68
68
|
* Recovery mechanism: reconcile task state with delegation history.
|
|
69
|
-
* When reviewer/test_engineer delegations occurred but the state
|
|
70
|
-
* was not advanced (e.g., toolAfter didn't fire
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* so that checkReviewerGate can make an accurate decision.
|
|
69
|
+
* When task-scoped reviewer/test_engineer delegations occurred but the state
|
|
70
|
+
* machine was not advanced (e.g., toolAfter didn't fire or subagent_type was
|
|
71
|
+
* missing), this function advances the task state so that checkReviewerGate can
|
|
72
|
+
* make an accurate decision without attributing unrelated delegation activity.
|
|
74
73
|
*
|
|
75
74
|
* @param taskId - The task ID to recover state for
|
|
76
75
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.21.
|
|
3
|
+
"version": "7.21.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",
|