opencode-swarm 7.81.0 → 7.81.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 +13 -2
- package/dist/council/criteria-store.d.ts +1 -1
- package/dist/index.js +66 -12
- package/dist/plan/ledger.d.ts +6 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "opencode-swarm",
|
|
55
|
-
version: "7.81.
|
|
55
|
+
version: "7.81.1",
|
|
56
56
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
57
57
|
main: "dist/index.js",
|
|
58
58
|
types: "dist/index.d.ts",
|
|
@@ -17194,6 +17194,12 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args, acknowledgedB
|
|
|
17194
17194
|
try {
|
|
17195
17195
|
const plan = await loadPlanJsonOnly(directory);
|
|
17196
17196
|
if (plan?.specHash) {
|
|
17197
|
+
if (stalenessData.specHash_plan !== plan.specHash) {
|
|
17198
|
+
return `Spec drift acknowledgment rejected: The spec has changed since the staleness was detected.
|
|
17199
|
+
Current plan specHash: ${plan.specHash}
|
|
17200
|
+
Staleness file specHash: ${stalenessData.specHash_plan}
|
|
17201
|
+
Please re-run the relevant phase to detect current drift status.`;
|
|
17202
|
+
}
|
|
17197
17203
|
currentHash = await computeSpecHash(directory);
|
|
17198
17204
|
plan.specHash = currentHash ?? undefined;
|
|
17199
17205
|
await savePlan(directory, plan);
|
|
@@ -21443,7 +21449,12 @@ async function tryAcquireLock(directory, filePath, agent, taskId) {
|
|
|
21443
21449
|
try {
|
|
21444
21450
|
release = await import_proper_lockfile.default.lock(lockPath, {
|
|
21445
21451
|
stale: LOCK_TIMEOUT_MS,
|
|
21446
|
-
retries: {
|
|
21452
|
+
retries: {
|
|
21453
|
+
retries: 5,
|
|
21454
|
+
minTimeout: 10,
|
|
21455
|
+
maxTimeout: 500,
|
|
21456
|
+
factor: 2
|
|
21457
|
+
},
|
|
21447
21458
|
realpath: false
|
|
21448
21459
|
});
|
|
21449
21460
|
} catch (err) {
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* read back during council evaluation.
|
|
6
6
|
*/
|
|
7
7
|
import type { CouncilCriteria, CouncilCriteriaItem } from './types';
|
|
8
|
-
export declare function writeCriteria(workingDir: string, taskId: string, criteria: CouncilCriteriaItem[]): void
|
|
8
|
+
export declare function writeCriteria(workingDir: string, taskId: string, criteria: CouncilCriteriaItem[]): Promise<void>;
|
|
9
9
|
export declare function readCriteria(workingDir: string, taskId: string): CouncilCriteria | null;
|
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.81.
|
|
72
|
+
version: "7.81.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",
|
|
@@ -20253,6 +20253,12 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args, acknowledgedB
|
|
|
20253
20253
|
try {
|
|
20254
20254
|
const plan = await loadPlanJsonOnly(directory);
|
|
20255
20255
|
if (plan?.specHash) {
|
|
20256
|
+
if (stalenessData.specHash_plan !== plan.specHash) {
|
|
20257
|
+
return `Spec drift acknowledgment rejected: The spec has changed since the staleness was detected.
|
|
20258
|
+
Current plan specHash: ${plan.specHash}
|
|
20259
|
+
Staleness file specHash: ${stalenessData.specHash_plan}
|
|
20260
|
+
Please re-run the relevant phase to detect current drift status.`;
|
|
20261
|
+
}
|
|
20256
20262
|
currentHash = await computeSpecHash(directory);
|
|
20257
20263
|
plan.specHash = currentHash ?? undefined;
|
|
20258
20264
|
await savePlan(directory, plan);
|
|
@@ -21956,7 +21962,12 @@ async function tryAcquireLock(directory, filePath, agent, taskId) {
|
|
|
21956
21962
|
try {
|
|
21957
21963
|
release = await import_proper_lockfile.default.lock(lockPath, {
|
|
21958
21964
|
stale: LOCK_TIMEOUT_MS,
|
|
21959
|
-
retries: {
|
|
21965
|
+
retries: {
|
|
21966
|
+
retries: 5,
|
|
21967
|
+
minTimeout: 10,
|
|
21968
|
+
maxTimeout: 500,
|
|
21969
|
+
factor: 2
|
|
21970
|
+
},
|
|
21960
21971
|
realpath: false
|
|
21961
21972
|
});
|
|
21962
21973
|
} catch (err2) {
|
|
@@ -124323,7 +124334,8 @@ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFi
|
|
|
124323
124334
|
|
|
124324
124335
|
// src/council/criteria-store.ts
|
|
124325
124336
|
init_zod();
|
|
124326
|
-
|
|
124337
|
+
init_task_file();
|
|
124338
|
+
import { existsSync as existsSync82, mkdirSync as mkdirSync35, readFileSync as readFileSync56 } from "node:fs";
|
|
124327
124339
|
import { join as join114 } from "node:path";
|
|
124328
124340
|
var COUNCIL_DIR = ".swarm/council";
|
|
124329
124341
|
var CouncilCriteriaSchema = exports_external.object({
|
|
@@ -124335,7 +124347,7 @@ var CouncilCriteriaSchema = exports_external.object({
|
|
|
124335
124347
|
})),
|
|
124336
124348
|
declaredAt: exports_external.string()
|
|
124337
124349
|
});
|
|
124338
|
-
function writeCriteria(workingDir, taskId, criteria) {
|
|
124350
|
+
async function writeCriteria(workingDir, taskId, criteria) {
|
|
124339
124351
|
const dir = join114(workingDir, COUNCIL_DIR);
|
|
124340
124352
|
mkdirSync35(dir, { recursive: true });
|
|
124341
124353
|
const payload = {
|
|
@@ -124343,7 +124355,7 @@ function writeCriteria(workingDir, taskId, criteria) {
|
|
|
124343
124355
|
criteria,
|
|
124344
124356
|
declaredAt: new Date().toISOString()
|
|
124345
124357
|
};
|
|
124346
|
-
|
|
124358
|
+
await atomicWriteFile(join114(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
|
|
124347
124359
|
}
|
|
124348
124360
|
function readCriteria(workingDir, taskId) {
|
|
124349
124361
|
const filePath = join114(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
|
|
@@ -125232,7 +125244,7 @@ var declare_council_criteria = createSwarmTool({
|
|
|
125232
125244
|
}
|
|
125233
125245
|
const existing = readCriteria(workingDir, input.taskId);
|
|
125234
125246
|
const replaced = existing !== null;
|
|
125235
|
-
writeCriteria(workingDir, input.taskId, input.criteria);
|
|
125247
|
+
await writeCriteria(workingDir, input.taskId, input.criteria);
|
|
125236
125248
|
return JSON.stringify({
|
|
125237
125249
|
success: true,
|
|
125238
125250
|
taskId: input.taskId,
|
|
@@ -133001,7 +133013,7 @@ import * as path157 from "node:path";
|
|
|
133001
133013
|
|
|
133002
133014
|
// src/mutation/engine.ts
|
|
133003
133015
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
133004
|
-
import { unlinkSync as unlinkSync19, writeFileSync as
|
|
133016
|
+
import { unlinkSync as unlinkSync19, writeFileSync as writeFileSync24 } from "node:fs";
|
|
133005
133017
|
import * as path156 from "node:path";
|
|
133006
133018
|
|
|
133007
133019
|
// src/mutation/equivalence.ts
|
|
@@ -133191,7 +133203,7 @@ async function executeMutation(patch, testCommand, testFiles, workingDir) {
|
|
|
133191
133203
|
const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
133192
133204
|
patchFile = path156.join(workingDir, `.mutation_patch_${safeId2}.diff`);
|
|
133193
133205
|
try {
|
|
133194
|
-
|
|
133206
|
+
writeFileSync24(patchFile, patch.patch);
|
|
133195
133207
|
} catch (writeErr) {
|
|
133196
133208
|
error93 = `Failed to write patch file: ${writeErr}`;
|
|
133197
133209
|
outcome = "error";
|
|
@@ -133627,6 +133639,7 @@ init_zod();
|
|
|
133627
133639
|
init_config();
|
|
133628
133640
|
init_schema();
|
|
133629
133641
|
init_manager2();
|
|
133642
|
+
init_task_file();
|
|
133630
133643
|
import * as fs111 from "node:fs";
|
|
133631
133644
|
import * as path167 from "node:path";
|
|
133632
133645
|
|
|
@@ -135821,6 +135834,39 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
135821
135834
|
}
|
|
135822
135835
|
warnings.push(`Knowledge sweep failed for phase ${phase}: ${detail}`);
|
|
135823
135836
|
}
|
|
135837
|
+
const planLockTaskId = `phase-complete-plan-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
135838
|
+
const planFilePath = "plan.json";
|
|
135839
|
+
let planLockResult;
|
|
135840
|
+
try {
|
|
135841
|
+
planLockResult = await tryAcquireLock(dir, planFilePath, agentName, planLockTaskId);
|
|
135842
|
+
} catch (error93) {
|
|
135843
|
+
return JSON.stringify({
|
|
135844
|
+
success: false,
|
|
135845
|
+
phase: args2.phase,
|
|
135846
|
+
status: "incomplete",
|
|
135847
|
+
message: `Failed to acquire lock for plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`,
|
|
135848
|
+
agentsDispatched,
|
|
135849
|
+
agentsMissing,
|
|
135850
|
+
warnings,
|
|
135851
|
+
errors: [error93 instanceof Error ? error93.message : String(error93)],
|
|
135852
|
+
recovery_guidance: "Resolve the filesystem issue (permissions, disk space, or .swarm/locks/ directory) and retry phase_complete."
|
|
135853
|
+
});
|
|
135854
|
+
}
|
|
135855
|
+
if (!planLockResult?.acquired) {
|
|
135856
|
+
return JSON.stringify({
|
|
135857
|
+
success: false,
|
|
135858
|
+
phase: args2.phase,
|
|
135859
|
+
status: "incomplete",
|
|
135860
|
+
message: `Plan write blocked: plan.json is locked by ${planLockResult.existing?.agent ?? "another agent"}`,
|
|
135861
|
+
agentsDispatched,
|
|
135862
|
+
agentsMissing,
|
|
135863
|
+
warnings,
|
|
135864
|
+
errors: [
|
|
135865
|
+
`Concurrent plan write detected — lock held by ${planLockResult.existing?.agent ?? "another agent"} (task: ${planLockResult.existing?.taskId ?? "unknown"})`
|
|
135866
|
+
],
|
|
135867
|
+
recovery_guidance: "Wait a moment and retry phase_complete. The lock will expire automatically if the holding agent fails."
|
|
135868
|
+
});
|
|
135869
|
+
}
|
|
135824
135870
|
try {
|
|
135825
135871
|
const plan = await loadPlan(dir);
|
|
135826
135872
|
if (plan === null) {
|
|
@@ -135849,7 +135895,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
135849
135895
|
const phaseObj = plan2.phases.find((p) => p.id === phase);
|
|
135850
135896
|
if (phaseObj) {
|
|
135851
135897
|
phaseObj.status = "complete";
|
|
135852
|
-
|
|
135898
|
+
await atomicWriteFile(planPath, JSON.stringify(plan2, null, 2));
|
|
135853
135899
|
}
|
|
135854
135900
|
} catch {}
|
|
135855
135901
|
} else if (plan) {
|
|
@@ -135891,9 +135937,17 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
135891
135937
|
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
135892
135938
|
if (phaseObj) {
|
|
135893
135939
|
phaseObj.status = "complete";
|
|
135894
|
-
|
|
135940
|
+
await atomicWriteFile(planPath, JSON.stringify(plan, null, 2));
|
|
135895
135941
|
}
|
|
135896
135942
|
} catch {}
|
|
135943
|
+
} finally {
|
|
135944
|
+
if (planLockResult?.acquired && planLockResult.lock._release) {
|
|
135945
|
+
try {
|
|
135946
|
+
await planLockResult.lock._release();
|
|
135947
|
+
} catch (releaseError) {
|
|
135948
|
+
warn("[phase_complete] Plan lock release failed (non-blocking):", releaseError instanceof Error ? releaseError.message : String(releaseError));
|
|
135949
|
+
}
|
|
135950
|
+
}
|
|
135897
135951
|
}
|
|
135898
135952
|
}
|
|
135899
135953
|
if (complianceWarnings.length > 0) {
|
|
@@ -143588,7 +143642,7 @@ import {
|
|
|
143588
143642
|
readFileSync as readFileSync81,
|
|
143589
143643
|
renameSync as renameSync24,
|
|
143590
143644
|
unlinkSync as unlinkSync21,
|
|
143591
|
-
writeFileSync as
|
|
143645
|
+
writeFileSync as writeFileSync30
|
|
143592
143646
|
} from "node:fs";
|
|
143593
143647
|
import path181 from "node:path";
|
|
143594
143648
|
init_create_tool();
|
|
@@ -143855,7 +143909,7 @@ function writePhaseCouncilEvidence(workingDir, synthesis, provenance) {
|
|
|
143855
143909
|
};
|
|
143856
143910
|
const tempFile = `${evidenceFile}.tmp-${Date.now()}`;
|
|
143857
143911
|
try {
|
|
143858
|
-
|
|
143912
|
+
writeFileSync30(tempFile, JSON.stringify(evidenceBundle, null, 2), "utf-8");
|
|
143859
143913
|
renameSync24(tempFile, evidenceFile);
|
|
143860
143914
|
} finally {
|
|
143861
143915
|
if (existsSync103(tempFile)) {
|
package/dist/plan/ledger.d.ts
CHANGED
|
@@ -82,6 +82,12 @@ declare function getPlanJsonPath(directory: string): string;
|
|
|
82
82
|
* Compute a SHA-256 hash of the plan state.
|
|
83
83
|
* Uses deterministic JSON serialization for consistent hashing.
|
|
84
84
|
*
|
|
85
|
+
* IMPORTANT: Intentionally excludes `specMtime` and `specHash` fields.
|
|
86
|
+
* These fields track changes to spec.md but do not affect plan execution or structure.
|
|
87
|
+
* Including them would cause the plan hash to change whenever spec metadata changes,
|
|
88
|
+
* invalidating cached plan state unnecessarily. Spec changes are tracked separately
|
|
89
|
+
* in the ledger via `spec_updated` and acknowledgment events.
|
|
90
|
+
*
|
|
85
91
|
* @param plan - The plan to hash
|
|
86
92
|
* @returns Hex-encoded SHA-256 hash
|
|
87
93
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.81.
|
|
3
|
+
"version": "7.81.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",
|