opencode-swarm 7.23.0 → 7.24.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/dist/cli/index.js +37 -3
- package/dist/index.js +110 -13
- package/dist/session/snapshot-writer.d.ts +2 -0
- package/dist/tools/update-task-status.d.ts +6 -1
- 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.
|
|
37
|
+
version: "7.24.0",
|
|
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",
|
|
@@ -34292,6 +34292,14 @@ function gitExec(args) {
|
|
|
34292
34292
|
}
|
|
34293
34293
|
return result.stdout;
|
|
34294
34294
|
}
|
|
34295
|
+
function appendRetentionEvent(directory, event) {
|
|
34296
|
+
try {
|
|
34297
|
+
const eventsPath = path9.join(directory, ".swarm", "events.jsonl");
|
|
34298
|
+
const line = `${JSON.stringify({ ...event, timestamp: new Date().toISOString() })}
|
|
34299
|
+
`;
|
|
34300
|
+
fs6.appendFileSync(eventsPath, line);
|
|
34301
|
+
} catch {}
|
|
34302
|
+
}
|
|
34295
34303
|
function getCurrentSha() {
|
|
34296
34304
|
const output = gitExec(["rev-parse", "HEAD"]);
|
|
34297
34305
|
return output.trim();
|
|
@@ -34343,6 +34351,17 @@ function handleSave(label, directory) {
|
|
|
34343
34351
|
sha: newSha,
|
|
34344
34352
|
timestamp
|
|
34345
34353
|
});
|
|
34354
|
+
if (log2.checkpoints.length > maxCheckpoints) {
|
|
34355
|
+
const evicted = log2.checkpoints.splice(0, log2.checkpoints.length - maxCheckpoints);
|
|
34356
|
+
try {
|
|
34357
|
+
appendRetentionEvent(directory, {
|
|
34358
|
+
event: "checkpoint_retention_applied",
|
|
34359
|
+
evicted_labels: evicted.map((e) => e.label),
|
|
34360
|
+
evicted_count: evicted.length,
|
|
34361
|
+
remaining_count: log2.checkpoints.length
|
|
34362
|
+
});
|
|
34363
|
+
} catch {}
|
|
34364
|
+
}
|
|
34346
34365
|
writeCheckpointLog(log2, directory);
|
|
34347
34366
|
return JSON.stringify({
|
|
34348
34367
|
action: "save",
|
|
@@ -43447,7 +43466,7 @@ var init_handoff_service = __esm(() => {
|
|
|
43447
43466
|
});
|
|
43448
43467
|
|
|
43449
43468
|
// src/session/snapshot-writer.ts
|
|
43450
|
-
import { mkdirSync as mkdirSync10, renameSync as renameSync6 } from "fs";
|
|
43469
|
+
import { closeSync as closeSync3, fsyncSync as fsyncSync2, mkdirSync as mkdirSync10, openSync as openSync3, renameSync as renameSync6 } from "fs";
|
|
43451
43470
|
import * as path27 from "path";
|
|
43452
43471
|
function serializeAgentSession(s) {
|
|
43453
43472
|
const gateLog = {};
|
|
@@ -43464,6 +43483,12 @@ function serializeAgentSession(s) {
|
|
|
43464
43483
|
const catastrophicPhaseWarnings = Array.from(s.catastrophicPhaseWarnings ?? new Set);
|
|
43465
43484
|
const phaseAgentsDispatched = Array.from(s.phaseAgentsDispatched ?? new Set);
|
|
43466
43485
|
const lastCompletedPhaseAgentsDispatched = Array.from(s.lastCompletedPhaseAgentsDispatched ?? new Set);
|
|
43486
|
+
const stageBCompletion = {};
|
|
43487
|
+
if (s.stageBCompletion) {
|
|
43488
|
+
for (const [taskId, agents] of s.stageBCompletion) {
|
|
43489
|
+
stageBCompletion[taskId] = Array.from(agents);
|
|
43490
|
+
}
|
|
43491
|
+
}
|
|
43467
43492
|
const windows = {};
|
|
43468
43493
|
const rawWindows = s.windows ?? {};
|
|
43469
43494
|
for (const [key, win] of Object.entries(rawWindows)) {
|
|
@@ -43520,7 +43545,8 @@ function serializeAgentSession(s) {
|
|
|
43520
43545
|
fullAutoInteractionCount: s.fullAutoInteractionCount ?? 0,
|
|
43521
43546
|
fullAutoDeadlockCount: s.fullAutoDeadlockCount ?? 0,
|
|
43522
43547
|
fullAutoLastQuestionHash: s.fullAutoLastQuestionHash ?? null,
|
|
43523
|
-
sessionRehydratedAt: s.sessionRehydratedAt ?? 0
|
|
43548
|
+
sessionRehydratedAt: s.sessionRehydratedAt ?? 0,
|
|
43549
|
+
...Object.keys(stageBCompletion).length > 0 && { stageBCompletion }
|
|
43524
43550
|
};
|
|
43525
43551
|
}
|
|
43526
43552
|
async function writeSnapshot(directory, state) {
|
|
@@ -43542,6 +43568,14 @@ async function writeSnapshot(directory, state) {
|
|
|
43542
43568
|
mkdirSync10(dir, { recursive: true });
|
|
43543
43569
|
const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
43544
43570
|
await bunWrite(tempPath, content);
|
|
43571
|
+
try {
|
|
43572
|
+
const fd = openSync3(tempPath, "r+");
|
|
43573
|
+
try {
|
|
43574
|
+
fsyncSync2(fd);
|
|
43575
|
+
} finally {
|
|
43576
|
+
closeSync3(fd);
|
|
43577
|
+
}
|
|
43578
|
+
} catch {}
|
|
43545
43579
|
renameSync6(tempPath, resolvedPath);
|
|
43546
43580
|
} catch (error93) {
|
|
43547
43581
|
log("[snapshot-writer] write failed", {
|
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.
|
|
36
|
+
version: "7.24.0",
|
|
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",
|
|
@@ -41417,6 +41417,14 @@ function gitExec(args2) {
|
|
|
41417
41417
|
}
|
|
41418
41418
|
return result.stdout;
|
|
41419
41419
|
}
|
|
41420
|
+
function appendRetentionEvent(directory, event) {
|
|
41421
|
+
try {
|
|
41422
|
+
const eventsPath = path14.join(directory, ".swarm", "events.jsonl");
|
|
41423
|
+
const line = `${JSON.stringify({ ...event, timestamp: new Date().toISOString() })}
|
|
41424
|
+
`;
|
|
41425
|
+
fs11.appendFileSync(eventsPath, line);
|
|
41426
|
+
} catch {}
|
|
41427
|
+
}
|
|
41420
41428
|
function getCurrentSha() {
|
|
41421
41429
|
const output = gitExec(["rev-parse", "HEAD"]);
|
|
41422
41430
|
return output.trim();
|
|
@@ -41468,6 +41476,17 @@ function handleSave(label, directory) {
|
|
|
41468
41476
|
sha: newSha,
|
|
41469
41477
|
timestamp
|
|
41470
41478
|
});
|
|
41479
|
+
if (log2.checkpoints.length > maxCheckpoints) {
|
|
41480
|
+
const evicted = log2.checkpoints.splice(0, log2.checkpoints.length - maxCheckpoints);
|
|
41481
|
+
try {
|
|
41482
|
+
appendRetentionEvent(directory, {
|
|
41483
|
+
event: "checkpoint_retention_applied",
|
|
41484
|
+
evicted_labels: evicted.map((e) => e.label),
|
|
41485
|
+
evicted_count: evicted.length,
|
|
41486
|
+
remaining_count: log2.checkpoints.length
|
|
41487
|
+
});
|
|
41488
|
+
} catch {}
|
|
41489
|
+
}
|
|
41471
41490
|
writeCheckpointLog(log2, directory);
|
|
41472
41491
|
return JSON.stringify({
|
|
41473
41492
|
action: "save",
|
|
@@ -52386,7 +52405,7 @@ var init_handoff_service = __esm(() => {
|
|
|
52386
52405
|
});
|
|
52387
52406
|
|
|
52388
52407
|
// src/session/snapshot-writer.ts
|
|
52389
|
-
import { mkdirSync as mkdirSync13, renameSync as renameSync9 } from "node:fs";
|
|
52408
|
+
import { closeSync as closeSync3, fsyncSync as fsyncSync2, mkdirSync as mkdirSync13, openSync as openSync3, renameSync as renameSync9 } from "node:fs";
|
|
52390
52409
|
import * as path33 from "node:path";
|
|
52391
52410
|
function serializeAgentSession(s) {
|
|
52392
52411
|
const gateLog = {};
|
|
@@ -52403,6 +52422,12 @@ function serializeAgentSession(s) {
|
|
|
52403
52422
|
const catastrophicPhaseWarnings = Array.from(s.catastrophicPhaseWarnings ?? new Set);
|
|
52404
52423
|
const phaseAgentsDispatched = Array.from(s.phaseAgentsDispatched ?? new Set);
|
|
52405
52424
|
const lastCompletedPhaseAgentsDispatched = Array.from(s.lastCompletedPhaseAgentsDispatched ?? new Set);
|
|
52425
|
+
const stageBCompletion = {};
|
|
52426
|
+
if (s.stageBCompletion) {
|
|
52427
|
+
for (const [taskId, agents] of s.stageBCompletion) {
|
|
52428
|
+
stageBCompletion[taskId] = Array.from(agents);
|
|
52429
|
+
}
|
|
52430
|
+
}
|
|
52406
52431
|
const windows = {};
|
|
52407
52432
|
const rawWindows = s.windows ?? {};
|
|
52408
52433
|
for (const [key, win] of Object.entries(rawWindows)) {
|
|
@@ -52459,7 +52484,8 @@ function serializeAgentSession(s) {
|
|
|
52459
52484
|
fullAutoInteractionCount: s.fullAutoInteractionCount ?? 0,
|
|
52460
52485
|
fullAutoDeadlockCount: s.fullAutoDeadlockCount ?? 0,
|
|
52461
52486
|
fullAutoLastQuestionHash: s.fullAutoLastQuestionHash ?? null,
|
|
52462
|
-
sessionRehydratedAt: s.sessionRehydratedAt ?? 0
|
|
52487
|
+
sessionRehydratedAt: s.sessionRehydratedAt ?? 0,
|
|
52488
|
+
...Object.keys(stageBCompletion).length > 0 && { stageBCompletion }
|
|
52463
52489
|
};
|
|
52464
52490
|
}
|
|
52465
52491
|
async function writeSnapshot(directory, state) {
|
|
@@ -52481,6 +52507,14 @@ async function writeSnapshot(directory, state) {
|
|
|
52481
52507
|
mkdirSync13(dir, { recursive: true });
|
|
52482
52508
|
const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
52483
52509
|
await bunWrite(tempPath, content);
|
|
52510
|
+
try {
|
|
52511
|
+
const fd = openSync3(tempPath, "r+");
|
|
52512
|
+
try {
|
|
52513
|
+
fsyncSync2(fd);
|
|
52514
|
+
} finally {
|
|
52515
|
+
closeSync3(fd);
|
|
52516
|
+
}
|
|
52517
|
+
} catch {}
|
|
52484
52518
|
renameSync9(tempPath, resolvedPath);
|
|
52485
52519
|
} catch (error93) {
|
|
52486
52520
|
log("[snapshot-writer] write failed", {
|
|
@@ -62618,7 +62652,17 @@ If the user answered the gate question, immediately follow up with ONE more ques
|
|
|
62618
62652
|
- locked: true
|
|
62619
62653
|
- recorded_at: <ISO timestamp>
|
|
62620
62654
|
\`\`\`
|
|
62621
|
-
If the user accepts the default (1), skip writing this section entirely — serial execution is the default and needs no config
|
|
62655
|
+
If the user accepts the default (1), skip writing this section entirely — serial execution is the default and needs no config.
|
|
62656
|
+
|
|
62657
|
+
After asking the parallelization question (regardless of whether the user chose serial or parallel), immediately follow up with ONE more question: "Commit frequency for completed tasks? (default: phase-level only; optional per-task checkpoint commit after each task completion)".
|
|
62658
|
+
|
|
62659
|
+
If the user chooses per-task commits, write this section to \`.swarm/context.md\`:
|
|
62660
|
+
\`\`\`
|
|
62661
|
+
## Task Completion Commit Policy
|
|
62662
|
+
- commit_after_each_completed_task: true
|
|
62663
|
+
- recorded_at: <ISO timestamp>
|
|
62664
|
+
\`\`\`
|
|
62665
|
+
If the user keeps the default phase-level behavior, do not write this section.`;
|
|
62622
62666
|
}
|
|
62623
62667
|
function buildAvailableToolsList(council) {
|
|
62624
62668
|
const tools = AGENT_TOOL_MAP.architect ?? [];
|
|
@@ -64107,7 +64151,10 @@ save_plan({
|
|
|
64107
64151
|
After \`save_plan\` succeeds, read \`.swarm/context.md\`:
|
|
64108
64152
|
- If a \`## Pending QA Gate Selection\` section exists: parse the gate values, call \`set_qa_gates\` with those flags, confirm with the user ("QA gates applied: <list>"), then remove the section from context.md.
|
|
64109
64153
|
- If a \`## Pending Parallelization Config\` section also exists: parse the values and call \`save_plan\` again with \`execution_profile\` set to \`{ parallelization_enabled: <parsed>, max_concurrent_tasks: <parsed>, council_parallel: false, locked: true }\`. Then remove the section from context.md. If the plan already had \`execution_profile.locked: true\`, skip this step — the profile is already locked and immutable.
|
|
64154
|
+
- If a \`## Task Completion Commit Policy\` section exists: preserve it in \`.swarm/context.md\` (do NOT remove). This section is execution-time guidance for optional per-task checkpoint commits after \`update_task_status(status="completed")\`.
|
|
64110
64155
|
- If no pending section exists: {{QA_GATE_DIALOGUE_PLAN}}
|
|
64156
|
+
- If a \`## Task Completion Commit Policy\` section already exists in context.md, honor it as execution-time guidance (do NOT remove).
|
|
64157
|
+
- If no \`## Task Completion Commit Policy\` section exists AND the \`{{QA_GATE_DIALOGUE_PLAN}}\` template was not rendered (pending sections were pre-written), ask the commit-frequency question now. Write the section to context.md if the user chooses per-task commits; skip if they keep the default phase-level behavior.
|
|
64111
64158
|
<!-- BEHAVIORAL_GUIDANCE_START -->
|
|
64112
64159
|
INLINE GATE SELECTION — no pending section found in context.md. You MUST ask now.
|
|
64113
64160
|
✗ "I'll call set_qa_gates with defaults and move on"
|
|
@@ -64392,7 +64439,14 @@ This step supplements (not replaces) the existing regression-sweep and test-drif
|
|
|
64392
64439
|
Any blank "value: ___" field = gate was not run = task is NOT complete.
|
|
64393
64440
|
Filling this checklist from memory ("I think I ran it") is INVALID. Each value must come from actual tool/agent output in this session.
|
|
64394
64441
|
|
|
64395
|
-
|
|
64442
|
+
5p. Call update_task_status with status "completed".
|
|
64443
|
+
5q. OPTIONAL TASK-COMPLETION COMMIT POLICY: read \`.swarm/context.md\`.
|
|
64444
|
+
- If \`## Task Completion Commit Policy\` contains \`commit_after_each_completed_task: true\`, immediately call:
|
|
64445
|
+
\`checkpoint save task-<task-id>-complete\`
|
|
64446
|
+
- If the section is absent or false, skip this step.
|
|
64447
|
+
- This optional commit policy NEVER bypasses PRE-COMMIT RULE checks above.
|
|
64448
|
+
- If checkpoint save fails with "duplicate label", the task was already checkpointed from a prior completion or retry. Silently skip — the existing checkpoint is valid.
|
|
64449
|
+
5r. Proceed to next task.
|
|
64396
64450
|
|
|
64397
64451
|
## ⛔ RETROSPECTIVE GATE
|
|
64398
64452
|
|
|
@@ -83611,8 +83665,8 @@ ${formattedIndex}
|
|
|
83611
83665
|
} else {
|
|
83612
83666
|
if (existingContent.length > 0 && !existingContent.endsWith(`
|
|
83613
83667
|
`)) {
|
|
83614
|
-
updatedContent = existingContent
|
|
83615
|
-
|
|
83668
|
+
updatedContent = `${existingContent}
|
|
83669
|
+
${newSection}`;
|
|
83616
83670
|
} else {
|
|
83617
83671
|
updatedContent = existingContent + newSection;
|
|
83618
83672
|
}
|
|
@@ -85196,6 +85250,12 @@ function deserializeAgentSession(s) {
|
|
|
85196
85250
|
const catastrophicPhaseWarnings = new Set(s.catastrophicPhaseWarnings ?? []);
|
|
85197
85251
|
const phaseAgentsDispatched = new Set(s.phaseAgentsDispatched ?? []);
|
|
85198
85252
|
const lastCompletedPhaseAgentsDispatched = new Set(s.lastCompletedPhaseAgentsDispatched ?? []);
|
|
85253
|
+
const stageBCompletion = new Map;
|
|
85254
|
+
if (s.stageBCompletion) {
|
|
85255
|
+
for (const [taskId, agents] of Object.entries(s.stageBCompletion)) {
|
|
85256
|
+
stageBCompletion.set(taskId, new Set(agents));
|
|
85257
|
+
}
|
|
85258
|
+
}
|
|
85199
85259
|
const windows = {};
|
|
85200
85260
|
for (const [key, win] of Object.entries(s.windows ?? {})) {
|
|
85201
85261
|
windows[key] = {
|
|
@@ -85250,7 +85310,8 @@ function deserializeAgentSession(s) {
|
|
|
85250
85310
|
prmLastPatternDetected: null,
|
|
85251
85311
|
prmTrajectoryStep: 0,
|
|
85252
85312
|
prmHardStopPending: false,
|
|
85253
|
-
sessionRehydratedAt: s.sessionRehydratedAt ?? 0
|
|
85313
|
+
sessionRehydratedAt: s.sessionRehydratedAt ?? 0,
|
|
85314
|
+
stageBCompletion
|
|
85254
85315
|
};
|
|
85255
85316
|
}
|
|
85256
85317
|
async function readSnapshot(directory) {
|
|
@@ -87087,7 +87148,7 @@ ${body2}`);
|
|
|
87087
87148
|
|
|
87088
87149
|
// src/council/council-evidence-writer.ts
|
|
87089
87150
|
import {
|
|
87090
|
-
appendFileSync as
|
|
87151
|
+
appendFileSync as appendFileSync12,
|
|
87091
87152
|
existsSync as existsSync53,
|
|
87092
87153
|
mkdirSync as mkdirSync24,
|
|
87093
87154
|
readFileSync as readFileSync44,
|
|
@@ -87171,7 +87232,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
87171
87232
|
timestamp: synthesis.timestamp,
|
|
87172
87233
|
vetoedBy: synthesis.vetoedBy
|
|
87173
87234
|
});
|
|
87174
|
-
|
|
87235
|
+
appendFileSync12(join83(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
|
|
87175
87236
|
`);
|
|
87176
87237
|
} catch (auditError) {
|
|
87177
87238
|
console.warn(`writeCouncilEvidence: failed to append round-history audit log: ${auditError instanceof Error ? auditError.message : String(auditError)}`);
|
|
@@ -103946,7 +104007,27 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
103946
104007
|
}
|
|
103947
104008
|
}
|
|
103948
104009
|
const currentStateStr = stateEntries.length > 0 ? stateEntries.join(", ") : "no active sessions";
|
|
103949
|
-
const
|
|
104010
|
+
const chainEntries = [];
|
|
104011
|
+
for (const [sessionId, chain] of swarmState.delegationChains) {
|
|
104012
|
+
const session = swarmState.agentSessions.get(sessionId);
|
|
104013
|
+
if (session && (session.currentTaskId === taskId || session.lastCoderDelegationTaskId === taskId)) {
|
|
104014
|
+
const targets = chain.map((d) => stripKnownSwarmPrefix(d.to));
|
|
104015
|
+
chainEntries.push(`${sessionId}: [${targets.join(", ")}]`);
|
|
104016
|
+
}
|
|
104017
|
+
}
|
|
104018
|
+
const chainSummary = chainEntries.length > 0 ? chainEntries.join("; ") : "no chains for this task";
|
|
104019
|
+
const rehydratedSessionCount = [
|
|
104020
|
+
...swarmState.agentSessions.values()
|
|
104021
|
+
].filter((s) => s.sessionRehydratedAt > 0).length;
|
|
104022
|
+
const finalReason = [
|
|
104023
|
+
`Task ${taskId} has not passed QA gates.`,
|
|
104024
|
+
` Session states: [${currentStateStr}].`,
|
|
104025
|
+
` Delegation chains: [${chainSummary}].`,
|
|
104026
|
+
` Evidence: [${evidenceIncompleteReason ?? "no evidence file found"}].`,
|
|
104027
|
+
` Rehydrated sessions: ${rehydratedSessionCount}.`,
|
|
104028
|
+
` Missing required state: tests_run or complete.`
|
|
104029
|
+
].join(`
|
|
104030
|
+
`);
|
|
103950
104031
|
telemetry.gateFailed("", "qa_gate", taskId, evidenceIncompleteReason ? `Missing gates: evidence incomplete` : `Missing state: tests_run or complete`);
|
|
103951
104032
|
return {
|
|
103952
104033
|
blocked: true,
|
|
@@ -103968,7 +104049,7 @@ async function checkReviewerGateWithScope(taskId, workingDirectory, sessionID) {
|
|
|
103968
104049
|
${scopeWarning}` : scopeWarning
|
|
103969
104050
|
};
|
|
103970
104051
|
}
|
|
103971
|
-
function recoverTaskStateFromDelegations(taskId) {
|
|
104052
|
+
function recoverTaskStateFromDelegations(taskId, directory) {
|
|
103972
104053
|
let hasReviewer = false;
|
|
103973
104054
|
let hasTestEngineer = false;
|
|
103974
104055
|
for (const [sessionId, chain] of swarmState.delegationChains) {
|
|
@@ -103983,8 +104064,24 @@ function recoverTaskStateFromDelegations(taskId) {
|
|
|
103983
104064
|
}
|
|
103984
104065
|
}
|
|
103985
104066
|
}
|
|
104067
|
+
if ((!hasReviewer || !hasTestEngineer) && directory) {
|
|
104068
|
+
try {
|
|
104069
|
+
const evidence = readTaskEvidenceRaw(directory, taskId);
|
|
104070
|
+
if (evidence && evidence.gates && Array.isArray(evidence.required_gates)) {
|
|
104071
|
+
if (evidence.gates.reviewer != null)
|
|
104072
|
+
hasReviewer = true;
|
|
104073
|
+
if (evidence.gates.test_engineer != null)
|
|
104074
|
+
hasTestEngineer = true;
|
|
104075
|
+
}
|
|
104076
|
+
} catch {}
|
|
104077
|
+
}
|
|
103986
104078
|
if (!hasReviewer && !hasTestEngineer)
|
|
103987
104079
|
return;
|
|
104080
|
+
if (swarmState.agentSessions.size === 0) {
|
|
104081
|
+
try {
|
|
104082
|
+
startAgentSession("recovery-session", "architect");
|
|
104083
|
+
} catch {}
|
|
104084
|
+
}
|
|
103988
104085
|
for (const [, session] of swarmState.agentSessions) {
|
|
103989
104086
|
if (!(session.taskWorkflowStates instanceof Map))
|
|
103990
104087
|
continue;
|
|
@@ -104128,7 +104225,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
104128
104225
|
} catch {}
|
|
104129
104226
|
}
|
|
104130
104227
|
if (args2.status === "completed") {
|
|
104131
|
-
recoverTaskStateFromDelegations(args2.task_id);
|
|
104228
|
+
recoverTaskStateFromDelegations(args2.task_id, directory);
|
|
104132
104229
|
let phaseRequiresReviewer = true;
|
|
104133
104230
|
try {
|
|
104134
104231
|
const planPath = path137.join(directory, ".swarm", "plan.json");
|
|
@@ -59,6 +59,8 @@ export interface SerializedAgentSession {
|
|
|
59
59
|
fullAutoLastQuestionHash?: string | null;
|
|
60
60
|
/** Timestamp when session was rehydrated from snapshot (0 if never rehydrated) */
|
|
61
61
|
sessionRehydratedAt?: number;
|
|
62
|
+
/** Stage B completion tracking: per-task set of completed Stage B agents. Optional for backward compat with old snapshots. */
|
|
63
|
+
stageBCompletion?: Record<string, string[]>;
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
64
66
|
* Minimal interface for serialized InvocationWindow
|
|
@@ -71,9 +71,14 @@ export declare function checkReviewerGateWithScope(taskId: string, workingDirect
|
|
|
71
71
|
* missing), this function advances the task state so that checkReviewerGate can
|
|
72
72
|
* make an accurate decision without attributing unrelated delegation activity.
|
|
73
73
|
*
|
|
74
|
+
* Falls back to reading durable evidence files when delegation chains are empty
|
|
75
|
+
* (e.g., after a crash or session restart without snapshot). This ensures
|
|
76
|
+
* recovery works even when no in-memory delegation history exists.
|
|
77
|
+
*
|
|
74
78
|
* @param taskId - The task ID to recover state for
|
|
79
|
+
* @param directory - Optional project directory for evidence file fallback
|
|
75
80
|
*/
|
|
76
|
-
export declare function recoverTaskStateFromDelegations(taskId: string): void;
|
|
81
|
+
export declare function recoverTaskStateFromDelegations(taskId: string, directory?: string): void;
|
|
77
82
|
/**
|
|
78
83
|
* Result of the council-gate check used when transitioning to 'completed'.
|
|
79
84
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.24.0",
|
|
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",
|