opencode-swarm 7.78.4 → 7.78.5
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 +173 -102
- package/dist/turbo/lean/runner.d.ts +36 -7
- package/dist/turbo/lean/state-lock.d.ts +59 -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.78.
|
|
55
|
+
version: "7.78.5",
|
|
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",
|
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.78.
|
|
72
|
+
version: "7.78.5",
|
|
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",
|
|
@@ -64383,7 +64383,7 @@ async function autoRetireSkills(directory, curatorKnowledgePath, excludeSlugs) {
|
|
|
64383
64383
|
return true;
|
|
64384
64384
|
return false;
|
|
64385
64385
|
});
|
|
64386
|
-
const violations = skillUsage.filter((e) => e.complianceVerdict === "
|
|
64386
|
+
const violations = skillUsage.filter((e) => e.complianceVerdict === "violated").length;
|
|
64387
64387
|
const violationRate = skillUsage.length > 0 ? violations / skillUsage.length : 0;
|
|
64388
64388
|
let allArchived = false;
|
|
64389
64389
|
try {
|
|
@@ -65044,7 +65044,7 @@ ${phaseDigest.summary}`,
|
|
|
65044
65044
|
});
|
|
65045
65045
|
if (skillUsage.length === 0)
|
|
65046
65046
|
continue;
|
|
65047
|
-
const violations = skillUsage.filter((e) => e.complianceVerdict === "
|
|
65047
|
+
const violations = skillUsage.filter((e) => e.complianceVerdict === "violated").length;
|
|
65048
65048
|
const violationRate = violations / skillUsage.length;
|
|
65049
65049
|
if (violationRate > REVISION_VIOLATION_THRESHOLD && violationRate <= 0.3) {
|
|
65050
65050
|
const content = await _internals28.readFileAsync(active.path, "utf-8");
|
|
@@ -65052,7 +65052,7 @@ ${phaseDigest.summary}`,
|
|
|
65052
65052
|
if (fm && fm.skillOrigin === "promoted_external")
|
|
65053
65053
|
continue;
|
|
65054
65054
|
const currentVersion = fm?.version ?? 1;
|
|
65055
|
-
const violationContexts = skillUsage.filter((e) => e.complianceVerdict === "
|
|
65055
|
+
const violationContexts = skillUsage.filter((e) => e.complianceVerdict === "violated").slice(-10).map((e) => ({
|
|
65056
65056
|
taskId: e.taskID,
|
|
65057
65057
|
agent: e.agentName,
|
|
65058
65058
|
verdict: e.complianceVerdict,
|
|
@@ -103812,7 +103812,7 @@ var init_curator_drift = __esm(() => {
|
|
|
103812
103812
|
var exports_design_doc_drift = {};
|
|
103813
103813
|
__export(exports_design_doc_drift, {
|
|
103814
103814
|
runDesignDocDriftCheck: () => runDesignDocDriftCheck,
|
|
103815
|
-
_internals: () =>
|
|
103815
|
+
_internals: () => _internals95
|
|
103816
103816
|
});
|
|
103817
103817
|
import * as fs110 from "node:fs";
|
|
103818
103818
|
import * as path165 from "node:path";
|
|
@@ -103945,7 +103945,7 @@ async function runDesignDocDriftCheck(directory, phase, outDir) {
|
|
|
103945
103945
|
return null;
|
|
103946
103946
|
}
|
|
103947
103947
|
}
|
|
103948
|
-
var DOC_DRIFT_REPORT_PREFIX = "doc-drift-phase-", MAX_TRACEABILITY_BYTES, DESIGN_DOC_FILES, TRACEABILITY_REL,
|
|
103948
|
+
var DOC_DRIFT_REPORT_PREFIX = "doc-drift-phase-", MAX_TRACEABILITY_BYTES, DESIGN_DOC_FILES, TRACEABILITY_REL, _internals95;
|
|
103949
103949
|
var init_design_doc_drift = __esm(() => {
|
|
103950
103950
|
init_event_bus();
|
|
103951
103951
|
init_effective_spec();
|
|
@@ -103960,7 +103960,7 @@ var init_design_doc_drift = __esm(() => {
|
|
|
103960
103960
|
"idiom-notes": path165.join("reference", "idiom-notes.md")
|
|
103961
103961
|
};
|
|
103962
103962
|
TRACEABILITY_REL = path165.join("reference", "traceability.json");
|
|
103963
|
-
|
|
103963
|
+
_internals95 = {
|
|
103964
103964
|
mtimeMsOrNull,
|
|
103965
103965
|
resolveAnchorWithin,
|
|
103966
103966
|
DESIGN_DOC_FILES
|
|
@@ -103971,7 +103971,7 @@ var init_design_doc_drift = __esm(() => {
|
|
|
103971
103971
|
var exports_project_context = {};
|
|
103972
103972
|
__export(exports_project_context, {
|
|
103973
103973
|
buildProjectContext: () => buildProjectContext,
|
|
103974
|
-
_internals: () =>
|
|
103974
|
+
_internals: () => _internals108,
|
|
103975
103975
|
LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
|
|
103976
103976
|
});
|
|
103977
103977
|
import * as fs134 from "node:fs";
|
|
@@ -104055,7 +104055,7 @@ function selectLintCommand(backend, directory) {
|
|
|
104055
104055
|
return null;
|
|
104056
104056
|
}
|
|
104057
104057
|
async function buildProjectContext(directory) {
|
|
104058
|
-
const backend = await
|
|
104058
|
+
const backend = await _internals108.pickBackend(directory);
|
|
104059
104059
|
if (!backend)
|
|
104060
104060
|
return null;
|
|
104061
104061
|
const ctx = emptyProjectContext();
|
|
@@ -104094,17 +104094,17 @@ async function buildProjectContext(directory) {
|
|
|
104094
104094
|
if (backend.prompts.reviewerChecklist.length > 0) {
|
|
104095
104095
|
ctx.REVIEWER_CHECKLIST = bulletList(backend.prompts.reviewerChecklist);
|
|
104096
104096
|
}
|
|
104097
|
-
const profiles =
|
|
104097
|
+
const profiles = _internals108.pickedProfiles(directory);
|
|
104098
104098
|
if (profiles.length > 1) {
|
|
104099
104099
|
ctx.PROJECT_CONTEXT_SECONDARY_LANGUAGES = profiles.slice(1).map((p) => p.id).join(", ");
|
|
104100
104100
|
}
|
|
104101
104101
|
return ctx;
|
|
104102
104102
|
}
|
|
104103
|
-
var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300,
|
|
104103
|
+
var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals108;
|
|
104104
104104
|
var init_project_context = __esm(() => {
|
|
104105
104105
|
init_dispatch();
|
|
104106
104106
|
init_framework_detector();
|
|
104107
|
-
|
|
104107
|
+
_internals108 = {
|
|
104108
104108
|
pickBackend,
|
|
104109
104109
|
pickedProfiles
|
|
104110
104110
|
};
|
|
@@ -130481,6 +130481,65 @@ function getMergeStrategy2(config3) {
|
|
|
130481
130481
|
// src/turbo/lean/runner.ts
|
|
130482
130482
|
init_state3();
|
|
130483
130483
|
|
|
130484
|
+
// src/turbo/lean/state-lock.ts
|
|
130485
|
+
init_file_locks();
|
|
130486
|
+
var _internals89 = { tryAcquireLock };
|
|
130487
|
+
|
|
130488
|
+
class TurboStateLockTimeoutError extends Error {
|
|
130489
|
+
directory;
|
|
130490
|
+
sessionID;
|
|
130491
|
+
constructor(directory, sessionID, timeoutMs) {
|
|
130492
|
+
super(`Turbo state lock timeout after ${timeoutMs}ms for session ${sessionID}`);
|
|
130493
|
+
this.name = "TurboStateLockTimeoutError";
|
|
130494
|
+
this.directory = directory;
|
|
130495
|
+
this.sessionID = sessionID;
|
|
130496
|
+
}
|
|
130497
|
+
}
|
|
130498
|
+
var BACKOFF_START_MS2 = 50;
|
|
130499
|
+
var BACKOFF_MAX_MS2 = 2000;
|
|
130500
|
+
var BACKOFF_JITTER_RATIO2 = 0.25;
|
|
130501
|
+
function backoffMs2(attempt) {
|
|
130502
|
+
const base = Math.min(BACKOFF_START_MS2 * 2 ** attempt, BACKOFF_MAX_MS2);
|
|
130503
|
+
const jitter = base * BACKOFF_JITTER_RATIO2 * (Math.random() * 2 - 1);
|
|
130504
|
+
return Math.max(1, Math.round(base + jitter));
|
|
130505
|
+
}
|
|
130506
|
+
async function withTurboStateLock(directory, sessionID, fn2, timeoutMs = 30000) {
|
|
130507
|
+
const lockPath = ".swarm/turbo-state.json";
|
|
130508
|
+
const agent = "lean-turbo-runner";
|
|
130509
|
+
const deadline = Date.now() + timeoutMs;
|
|
130510
|
+
let attempt = 0;
|
|
130511
|
+
while (true) {
|
|
130512
|
+
let result;
|
|
130513
|
+
try {
|
|
130514
|
+
result = await _internals89.tryAcquireLock(directory, lockPath, agent, sessionID);
|
|
130515
|
+
} catch (acquireErr) {
|
|
130516
|
+
console.warn(`[lean-turbo] state lock acquisition error for ${sessionID} (${lockPath}), will retry: ${acquireErr instanceof Error ? acquireErr.message : String(acquireErr)}`);
|
|
130517
|
+
}
|
|
130518
|
+
if (result && result.acquired) {
|
|
130519
|
+
const lock = result.lock;
|
|
130520
|
+
try {
|
|
130521
|
+
return await fn2();
|
|
130522
|
+
} finally {
|
|
130523
|
+
if (lock._release) {
|
|
130524
|
+
try {
|
|
130525
|
+
await lock._release();
|
|
130526
|
+
} catch (releaseErr) {
|
|
130527
|
+
console.warn(`[lean-turbo] state lock release failed for ${sessionID} (${lockPath}): ${releaseErr instanceof Error ? releaseErr.message : String(releaseErr)}`);
|
|
130528
|
+
}
|
|
130529
|
+
}
|
|
130530
|
+
}
|
|
130531
|
+
}
|
|
130532
|
+
if (Date.now() >= deadline) {
|
|
130533
|
+
throw new TurboStateLockTimeoutError(directory, sessionID, timeoutMs);
|
|
130534
|
+
}
|
|
130535
|
+
const delay2 = Math.min(backoffMs2(attempt), deadline - Date.now());
|
|
130536
|
+
if (delay2 > 0) {
|
|
130537
|
+
await new Promise((resolve60) => setTimeout(resolve60, delay2));
|
|
130538
|
+
}
|
|
130539
|
+
attempt++;
|
|
130540
|
+
}
|
|
130541
|
+
}
|
|
130542
|
+
|
|
130484
130543
|
// src/turbo/lean/worktree.ts
|
|
130485
130544
|
init_core3();
|
|
130486
130545
|
async function provisionWorktree2(directory, laneId, sessionId, config3) {
|
|
@@ -131062,21 +131121,47 @@ class LeanTurboRunner {
|
|
|
131062
131121
|
return agent;
|
|
131063
131122
|
}
|
|
131064
131123
|
async _writeLaneEvidenceSafely(lane, status, extras) {
|
|
131065
|
-
|
|
131066
|
-
|
|
131067
|
-
|
|
131068
|
-
|
|
131069
|
-
|
|
131070
|
-
|
|
131071
|
-
|
|
131072
|
-
|
|
131073
|
-
|
|
131074
|
-
|
|
131075
|
-
|
|
131076
|
-
|
|
131124
|
+
const maxAttempts = 3;
|
|
131125
|
+
const baseDelayMs = 100;
|
|
131126
|
+
for (let attempt = 0;attempt < maxAttempts; attempt++) {
|
|
131127
|
+
try {
|
|
131128
|
+
const evidence = {
|
|
131129
|
+
laneId: lane.laneId,
|
|
131130
|
+
taskIds: lane.taskIds,
|
|
131131
|
+
files: lane.files,
|
|
131132
|
+
status,
|
|
131133
|
+
startedAt: lane.startedAt,
|
|
131134
|
+
...extras
|
|
131135
|
+
};
|
|
131136
|
+
const runState = LeanTurboRunner._internals.loadLeanTurboRunState(this._directory, this._sessionID);
|
|
131137
|
+
const phase = runState?.phase;
|
|
131138
|
+
if (phase === undefined) {
|
|
131139
|
+
console.warn(`[lean-turbo] evidence write skipped for lane ${lane.laneId}: phase not set in run state`);
|
|
131140
|
+
return;
|
|
131141
|
+
}
|
|
131077
131142
|
await LeanTurboRunner._internals.writeLaneEvidence(this._directory, phase, evidence);
|
|
131143
|
+
return;
|
|
131144
|
+
} catch (error93) {
|
|
131145
|
+
const errCode = error93 instanceof Error ? error93.code ?? "" : "";
|
|
131146
|
+
const isTransient = errCode.length > 0 && [
|
|
131147
|
+
"ENOENT",
|
|
131148
|
+
"EBUSY",
|
|
131149
|
+
"EPERM",
|
|
131150
|
+
"EIO",
|
|
131151
|
+
"EAGAIN",
|
|
131152
|
+
"ETIMEDOUT",
|
|
131153
|
+
"ENOSPC"
|
|
131154
|
+
].includes(errCode);
|
|
131155
|
+
if (attempt < maxAttempts - 1 && isTransient) {
|
|
131156
|
+
const delayMs = baseDelayMs * 2 ** attempt;
|
|
131157
|
+
await new Promise((resolve60) => setTimeout(resolve60, delayMs));
|
|
131158
|
+
continue;
|
|
131159
|
+
}
|
|
131160
|
+
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
131161
|
+
console.warn(`[lean-turbo] evidence write failed for lane ${lane.laneId}: ${msg}`);
|
|
131162
|
+
return;
|
|
131078
131163
|
}
|
|
131079
|
-
}
|
|
131164
|
+
}
|
|
131080
131165
|
}
|
|
131081
131166
|
_buildLanePrompt(lane) {
|
|
131082
131167
|
const taskList = lane.taskIds.map((id) => ` - ${id}`).join(`
|
|
@@ -131095,23 +131180,9 @@ ${fileList}
|
|
|
131095
131180
|
` + `When all tasks are complete, signal completion.`;
|
|
131096
131181
|
}
|
|
131097
131182
|
async _withStateLock(fn2) {
|
|
131098
|
-
const
|
|
131099
|
-
|
|
131100
|
-
|
|
131101
|
-
timeoutId = setTimeout(() => {
|
|
131102
|
-
reject(new Error(`_withStateLock timed out after ${timeoutMs}ms — state update will not block subsequent operations`));
|
|
131103
|
-
}, timeoutMs);
|
|
131104
|
-
});
|
|
131105
|
-
const chain = this._stateLock.then(fn2).finally(() => {
|
|
131106
|
-
if (timeoutId)
|
|
131107
|
-
clearTimeout(timeoutId);
|
|
131108
|
-
});
|
|
131109
|
-
const promise3 = Promise.race([chain, withTimeout3]).finally(() => {
|
|
131110
|
-
if (timeoutId)
|
|
131111
|
-
clearTimeout(timeoutId);
|
|
131112
|
-
});
|
|
131113
|
-
this._stateLock = promise3.catch(() => {});
|
|
131114
|
-
return promise3;
|
|
131183
|
+
const chain = this._stateLock.then(() => withTurboStateLock(this._directory, this._sessionID, fn2, 1e4));
|
|
131184
|
+
this._stateLock = chain.catch(() => {});
|
|
131185
|
+
return chain;
|
|
131115
131186
|
}
|
|
131116
131187
|
async _updateDurableState(lanePlan) {
|
|
131117
131188
|
try {
|
|
@@ -131186,7 +131257,7 @@ ${fileList}
|
|
|
131186
131257
|
|
|
131187
131258
|
// src/tools/lean-turbo-run-phase.ts
|
|
131188
131259
|
init_create_tool();
|
|
131189
|
-
var
|
|
131260
|
+
var _internals90 = {
|
|
131190
131261
|
LeanTurboRunner,
|
|
131191
131262
|
loadPluginConfigWithMeta
|
|
131192
131263
|
};
|
|
@@ -131196,9 +131267,9 @@ async function executeLeanTurboRunPhase(args2) {
|
|
|
131196
131267
|
let runError = null;
|
|
131197
131268
|
let runner = null;
|
|
131198
131269
|
try {
|
|
131199
|
-
const { config: config3 } =
|
|
131270
|
+
const { config: config3 } = _internals90.loadPluginConfigWithMeta(directory);
|
|
131200
131271
|
const leanConfig = config3.turbo?.strategy === "lean" ? config3.turbo.lean : undefined;
|
|
131201
|
-
runner = new
|
|
131272
|
+
runner = new _internals90.LeanTurboRunner({
|
|
131202
131273
|
directory,
|
|
131203
131274
|
sessionID,
|
|
131204
131275
|
opencodeClient: swarmState.opencodeClient ?? null,
|
|
@@ -131534,7 +131605,7 @@ function isStaticallyEquivalent(originalCode, mutatedCode) {
|
|
|
131534
131605
|
const strippedMutated = stripCode(mutatedCode);
|
|
131535
131606
|
return strippedOriginal === strippedMutated;
|
|
131536
131607
|
}
|
|
131537
|
-
var
|
|
131608
|
+
var _internals91 = {
|
|
131538
131609
|
isStaticallyEquivalent,
|
|
131539
131610
|
checkEquivalence,
|
|
131540
131611
|
batchCheckEquivalence
|
|
@@ -131574,7 +131645,7 @@ async function batchCheckEquivalence(patches, llmJudge) {
|
|
|
131574
131645
|
const results = [];
|
|
131575
131646
|
for (const { patch, originalCode, mutatedCode } of patches) {
|
|
131576
131647
|
try {
|
|
131577
|
-
const result = await
|
|
131648
|
+
const result = await _internals91.checkEquivalence(patch, originalCode, mutatedCode, llmJudge);
|
|
131578
131649
|
results.push(result);
|
|
131579
131650
|
} catch (err2) {
|
|
131580
131651
|
results.push({
|
|
@@ -131634,7 +131705,7 @@ function validateTestCommand(testCommand) {
|
|
|
131634
131705
|
var MUTATION_TIMEOUT_MS = 30000;
|
|
131635
131706
|
var TOTAL_BUDGET_MS = 300000;
|
|
131636
131707
|
var GIT_APPLY_TIMEOUT_MS = 5000;
|
|
131637
|
-
var
|
|
131708
|
+
var _internals92 = {
|
|
131638
131709
|
executeMutation,
|
|
131639
131710
|
computeReport,
|
|
131640
131711
|
executeMutationSuite,
|
|
@@ -131666,7 +131737,7 @@ async function executeMutation(patch, testCommand, testFiles, workingDir) {
|
|
|
131666
131737
|
};
|
|
131667
131738
|
}
|
|
131668
131739
|
try {
|
|
131669
|
-
const applyResult =
|
|
131740
|
+
const applyResult = _internals92.spawnSync("git", ["apply", "--", patchFile], {
|
|
131670
131741
|
cwd: workingDir,
|
|
131671
131742
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
131672
131743
|
stdio: "pipe"
|
|
@@ -131697,7 +131768,7 @@ async function executeMutation(patch, testCommand, testFiles, workingDir) {
|
|
|
131697
131768
|
try {
|
|
131698
131769
|
const safeTestFiles = testFiles.filter((f) => !f.startsWith("-"));
|
|
131699
131770
|
const testArgs = safeTestFiles.length > 0 ? [...testCommand.slice(1), ...safeTestFiles] : testCommand.slice(1);
|
|
131700
|
-
const spawnResult =
|
|
131771
|
+
const spawnResult = _internals92.spawnSync(testCommand[0], testArgs, {
|
|
131701
131772
|
cwd: workingDir,
|
|
131702
131773
|
timeout: MUTATION_TIMEOUT_MS,
|
|
131703
131774
|
stdio: "pipe"
|
|
@@ -131730,7 +131801,7 @@ async function executeMutation(patch, testCommand, testFiles, workingDir) {
|
|
|
131730
131801
|
} finally {
|
|
131731
131802
|
if (patchFile) {
|
|
131732
131803
|
try {
|
|
131733
|
-
const revertResult =
|
|
131804
|
+
const revertResult = _internals92.spawnSync("git", ["apply", "-R", "--", patchFile], {
|
|
131734
131805
|
cwd: workingDir,
|
|
131735
131806
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
131736
131807
|
stdio: "pipe"
|
|
@@ -131927,7 +131998,7 @@ async function executeMutationSuite(patches, testCommand, testFiles, workingDir,
|
|
|
131927
131998
|
}
|
|
131928
131999
|
|
|
131929
132000
|
// src/mutation/gate.ts
|
|
131930
|
-
var
|
|
132001
|
+
var _internals93 = {
|
|
131931
132002
|
evaluateMutationGate,
|
|
131932
132003
|
buildTestImprovementPrompt,
|
|
131933
132004
|
buildMessage
|
|
@@ -131948,8 +132019,8 @@ function evaluateMutationGate(report, passThreshold = PASS_THRESHOLD, warnThresh
|
|
|
131948
132019
|
} else {
|
|
131949
132020
|
verdict = "fail";
|
|
131950
132021
|
}
|
|
131951
|
-
const testImprovementPrompt =
|
|
131952
|
-
const message =
|
|
132022
|
+
const testImprovementPrompt = _internals93.buildTestImprovementPrompt(report, passThreshold, verdict);
|
|
132023
|
+
const message = _internals93.buildMessage(verdict, adjustedKillRate, report.killed, report.totalMutants, report.equivalent, warnThreshold);
|
|
131953
132024
|
return {
|
|
131954
132025
|
verdict,
|
|
131955
132026
|
killRate: report.killRate,
|
|
@@ -132466,7 +132537,7 @@ function listLaneEvidenceSync(directory, phase) {
|
|
|
132466
132537
|
}
|
|
132467
132538
|
return laneIds;
|
|
132468
132539
|
}
|
|
132469
|
-
var
|
|
132540
|
+
var _internals94 = {
|
|
132470
132541
|
listActiveLocks,
|
|
132471
132542
|
readPersisted: readPersisted2,
|
|
132472
132543
|
readPlanJson: defaultReadPlanJson,
|
|
@@ -132527,7 +132598,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
132527
132598
|
reason: "Lean Turbo state unreadable or missing"
|
|
132528
132599
|
};
|
|
132529
132600
|
}
|
|
132530
|
-
const persisted =
|
|
132601
|
+
const persisted = _internals94.readPersisted(directory);
|
|
132531
132602
|
if (!persisted) {
|
|
132532
132603
|
return {
|
|
132533
132604
|
ok: false,
|
|
@@ -132591,7 +132662,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
132591
132662
|
}
|
|
132592
132663
|
}
|
|
132593
132664
|
if (runState.lanes.length > 0) {
|
|
132594
|
-
const evidenceLaneIds = new Set(
|
|
132665
|
+
const evidenceLaneIds = new Set(_internals94.listLaneEvidenceSync(directory, phase));
|
|
132595
132666
|
for (const lane of runState.lanes) {
|
|
132596
132667
|
if ((lane.status === "completed" || lane.status === "failed") && !evidenceLaneIds.has(lane.laneId)) {
|
|
132597
132668
|
return {
|
|
@@ -132601,7 +132672,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
132601
132672
|
}
|
|
132602
132673
|
}
|
|
132603
132674
|
}
|
|
132604
|
-
const activeLocks =
|
|
132675
|
+
const activeLocks = _internals94.listActiveLocks(directory);
|
|
132605
132676
|
const phaseLaneIds = new Set(laneIds);
|
|
132606
132677
|
for (const lock of activeLocks) {
|
|
132607
132678
|
if (lock.laneId && phaseLaneIds.has(lock.laneId)) {
|
|
@@ -132621,7 +132692,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
132621
132692
|
}
|
|
132622
132693
|
const serialDegradedTasks = runState.degradedTasks.filter((dt) => !laneTaskIds.has(dt.taskId));
|
|
132623
132694
|
if (serialDegradedTasks.length > 0) {
|
|
132624
|
-
const plan =
|
|
132695
|
+
const plan = _internals94.readPlanJson(directory);
|
|
132625
132696
|
if (!plan) {
|
|
132626
132697
|
return {
|
|
132627
132698
|
ok: false,
|
|
@@ -132665,7 +132736,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
132665
132736
|
}
|
|
132666
132737
|
const serializedTasks = runState.serializedTasks;
|
|
132667
132738
|
if (Array.isArray(serializedTasks) && serializedTasks.length > 0) {
|
|
132668
|
-
const plan =
|
|
132739
|
+
const plan = _internals94.readPlanJson(directory);
|
|
132669
132740
|
if (!plan) {
|
|
132670
132741
|
return {
|
|
132671
132742
|
ok: false,
|
|
@@ -132724,7 +132795,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
132724
132795
|
}
|
|
132725
132796
|
let reviewerVerdict = runState.lastReviewerVerdict;
|
|
132726
132797
|
if (!reviewerVerdict) {
|
|
132727
|
-
const evidence =
|
|
132798
|
+
const evidence = _internals94.readReviewerEvidence(directory, phase);
|
|
132728
132799
|
reviewerVerdict = evidence?.verdict ?? undefined;
|
|
132729
132800
|
}
|
|
132730
132801
|
if (mergedConfig.phase_reviewer) {
|
|
@@ -132737,7 +132808,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
132737
132808
|
}
|
|
132738
132809
|
let criticVerdict = runState.lastCriticVerdict;
|
|
132739
132810
|
if (!criticVerdict) {
|
|
132740
|
-
const evidence =
|
|
132811
|
+
const evidence = _internals94.readCriticEvidence(directory, phase);
|
|
132741
132812
|
criticVerdict = evidence?.verdict ?? undefined;
|
|
132742
132813
|
}
|
|
132743
132814
|
if (mergedConfig.phase_critic) {
|
|
@@ -133920,7 +133991,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
133920
133991
|
phase_critic: leanConfig.phase_critic,
|
|
133921
133992
|
integrated_diff_required: leanConfig.integrated_diff_required
|
|
133922
133993
|
} : undefined;
|
|
133923
|
-
const leanCheck =
|
|
133994
|
+
const leanCheck = _internals94.verifyLeanTurboPhaseReady(dir, phase, sessionID, leanPhaseReadyConfig);
|
|
133924
133995
|
if (!leanCheck.ok) {
|
|
133925
133996
|
return JSON.stringify({
|
|
133926
133997
|
success: false,
|
|
@@ -136272,11 +136343,11 @@ var quality_budget = createSwarmTool({
|
|
|
136272
136343
|
}).optional().describe("Quality budget thresholds")
|
|
136273
136344
|
},
|
|
136274
136345
|
async execute(args2, directory) {
|
|
136275
|
-
const result = await
|
|
136346
|
+
const result = await _internals96.qualityBudget(args2, directory);
|
|
136276
136347
|
return JSON.stringify(result);
|
|
136277
136348
|
}
|
|
136278
136349
|
});
|
|
136279
|
-
var
|
|
136350
|
+
var _internals96 = {
|
|
136280
136351
|
qualityBudget
|
|
136281
136352
|
};
|
|
136282
136353
|
|
|
@@ -137001,7 +137072,7 @@ var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
|
|
|
137001
137072
|
var DEFAULT_TIMEOUT_MS4 = 30000;
|
|
137002
137073
|
var MAX_OUTPUT_BYTES8 = 10 * 1024 * 1024;
|
|
137003
137074
|
var KILL_GRACE_MS = 2000;
|
|
137004
|
-
var
|
|
137075
|
+
var _internals97 = {
|
|
137005
137076
|
isSemgrepAvailable,
|
|
137006
137077
|
checkSemgrepAvailable,
|
|
137007
137078
|
resetSemgrepCache,
|
|
@@ -137027,7 +137098,7 @@ function isSemgrepAvailable() {
|
|
|
137027
137098
|
}
|
|
137028
137099
|
}
|
|
137029
137100
|
async function checkSemgrepAvailable() {
|
|
137030
|
-
return
|
|
137101
|
+
return _internals97.isSemgrepAvailable();
|
|
137031
137102
|
}
|
|
137032
137103
|
function resetSemgrepCache() {
|
|
137033
137104
|
semgrepAvailableCache = null;
|
|
@@ -137184,12 +137255,12 @@ async function runSemgrep(options) {
|
|
|
137184
137255
|
const timeoutMs = options.timeoutMs || DEFAULT_TIMEOUT_MS4;
|
|
137185
137256
|
if (files.length === 0) {
|
|
137186
137257
|
return {
|
|
137187
|
-
available:
|
|
137258
|
+
available: _internals97.isSemgrepAvailable(),
|
|
137188
137259
|
findings: [],
|
|
137189
137260
|
engine: "tier_a"
|
|
137190
137261
|
};
|
|
137191
137262
|
}
|
|
137192
|
-
if (!
|
|
137263
|
+
if (!_internals97.isSemgrepAvailable()) {
|
|
137193
137264
|
return {
|
|
137194
137265
|
available: false,
|
|
137195
137266
|
findings: [],
|
|
@@ -137356,7 +137427,7 @@ function assignOccurrenceIndices(findings, directory) {
|
|
|
137356
137427
|
}
|
|
137357
137428
|
const occIdx = countMap.get(baseKey) ?? 0;
|
|
137358
137429
|
countMap.set(baseKey, occIdx + 1);
|
|
137359
|
-
const fp =
|
|
137430
|
+
const fp = _internals98.fingerprintFinding(finding, directory, occIdx);
|
|
137360
137431
|
return {
|
|
137361
137432
|
finding,
|
|
137362
137433
|
index: occIdx,
|
|
@@ -137425,7 +137496,7 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
|
|
|
137425
137496
|
}
|
|
137426
137497
|
} catch {}
|
|
137427
137498
|
const scannedRelFiles = new Set(scannedFiles.map((f) => normalizeFindingPath(directory, f)));
|
|
137428
|
-
const indexed =
|
|
137499
|
+
const indexed = _internals98.assignOccurrenceIndices(findings, directory);
|
|
137429
137500
|
if (existing && !opts?.force) {
|
|
137430
137501
|
const prunedFingerprints = existing.fingerprints.filter((fp) => {
|
|
137431
137502
|
const relFile = fp.slice(0, fp.indexOf("|"));
|
|
@@ -137565,7 +137636,7 @@ function loadBaseline(directory, phase) {
|
|
|
137565
137636
|
};
|
|
137566
137637
|
}
|
|
137567
137638
|
}
|
|
137568
|
-
var
|
|
137639
|
+
var _internals98 = {
|
|
137569
137640
|
fingerprintFinding,
|
|
137570
137641
|
assignOccurrenceIndices,
|
|
137571
137642
|
captureOrMergeBaseline,
|
|
@@ -137975,11 +138046,11 @@ var sast_scan = createSwarmTool({
|
|
|
137975
138046
|
capture_baseline: safeArgs.capture_baseline,
|
|
137976
138047
|
phase: safeArgs.phase
|
|
137977
138048
|
};
|
|
137978
|
-
const result = await
|
|
138049
|
+
const result = await _internals99.sastScan(input, directory);
|
|
137979
138050
|
return JSON.stringify(result, null, 2);
|
|
137980
138051
|
}
|
|
137981
138052
|
});
|
|
137982
|
-
var
|
|
138053
|
+
var _internals99 = {
|
|
137983
138054
|
sastScan,
|
|
137984
138055
|
sast_scan
|
|
137985
138056
|
};
|
|
@@ -143014,7 +143085,7 @@ var swarm_memory_propose = createSwarmTool({
|
|
|
143014
143085
|
evidenceRefs: exports_external.array(exports_external.string().min(1).max(500)).max(20).optional().describe("Evidence refs such as files, commits, test outputs, or URLs")
|
|
143015
143086
|
},
|
|
143016
143087
|
execute: async (args2, directory, ctx) => {
|
|
143017
|
-
const { config: config3 } =
|
|
143088
|
+
const { config: config3 } = _internals100.loadPluginConfigWithMeta(directory);
|
|
143018
143089
|
if (config3.memory?.enabled !== true) {
|
|
143019
143090
|
return JSON.stringify({
|
|
143020
143091
|
success: false,
|
|
@@ -143030,7 +143101,7 @@ var swarm_memory_propose = createSwarmTool({
|
|
|
143030
143101
|
});
|
|
143031
143102
|
}
|
|
143032
143103
|
const agent = getContextAgent3(ctx);
|
|
143033
|
-
const gateway =
|
|
143104
|
+
const gateway = _internals100.createMemoryGateway({
|
|
143034
143105
|
directory,
|
|
143035
143106
|
sessionID: ctx?.sessionID,
|
|
143036
143107
|
agentRole: agent,
|
|
@@ -143055,7 +143126,7 @@ var swarm_memory_propose = createSwarmTool({
|
|
|
143055
143126
|
}
|
|
143056
143127
|
}
|
|
143057
143128
|
});
|
|
143058
|
-
var
|
|
143129
|
+
var _internals100 = {
|
|
143059
143130
|
loadPluginConfigWithMeta,
|
|
143060
143131
|
createMemoryGateway
|
|
143061
143132
|
};
|
|
@@ -143093,7 +143164,7 @@ var swarm_memory_recall = createSwarmTool({
|
|
|
143093
143164
|
maxItems: exports_external.number().int().min(1).max(20).optional().describe("Maximum memories to return")
|
|
143094
143165
|
},
|
|
143095
143166
|
execute: async (args2, directory, ctx) => {
|
|
143096
|
-
const { config: config3 } =
|
|
143167
|
+
const { config: config3 } = _internals101.loadPluginConfigWithMeta(directory);
|
|
143097
143168
|
if (config3.memory?.enabled !== true) {
|
|
143098
143169
|
return JSON.stringify({
|
|
143099
143170
|
success: false,
|
|
@@ -143109,7 +143180,7 @@ var swarm_memory_recall = createSwarmTool({
|
|
|
143109
143180
|
});
|
|
143110
143181
|
}
|
|
143111
143182
|
const agent = getContextAgent4(ctx);
|
|
143112
|
-
const gateway =
|
|
143183
|
+
const gateway = _internals101.createMemoryGateway({
|
|
143113
143184
|
directory,
|
|
143114
143185
|
sessionID: ctx?.sessionID,
|
|
143115
143186
|
agentRole: agent,
|
|
@@ -143142,7 +143213,7 @@ var RecallArgsSchema = exports_external.object({
|
|
|
143142
143213
|
kinds: exports_external.array(exports_external.enum(MEMORY_KINDS2)).optional(),
|
|
143143
143214
|
maxItems: exports_external.number().int().min(1).max(20).optional()
|
|
143144
143215
|
});
|
|
143145
|
-
var
|
|
143216
|
+
var _internals101 = {
|
|
143146
143217
|
loadPluginConfigWithMeta,
|
|
143147
143218
|
createMemoryGateway
|
|
143148
143219
|
};
|
|
@@ -143668,7 +143739,7 @@ import * as path185 from "node:path";
|
|
|
143668
143739
|
init_bun_compat();
|
|
143669
143740
|
import * as fs126 from "node:fs";
|
|
143670
143741
|
import * as path184 from "node:path";
|
|
143671
|
-
var
|
|
143742
|
+
var _internals102 = { bunSpawn };
|
|
143672
143743
|
var _swarmGitExcludedChecked = false;
|
|
143673
143744
|
function fileCoversSwarm(content) {
|
|
143674
143745
|
for (const rawLine of content.split(`
|
|
@@ -143701,7 +143772,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
143701
143772
|
checkIgnoreExitCode
|
|
143702
143773
|
] = await Promise.all([
|
|
143703
143774
|
(async () => {
|
|
143704
|
-
const proc =
|
|
143775
|
+
const proc = _internals102.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
|
|
143705
143776
|
try {
|
|
143706
143777
|
return await Promise.all([proc.exited, proc.stdout.text()]);
|
|
143707
143778
|
} finally {
|
|
@@ -143711,7 +143782,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
143711
143782
|
}
|
|
143712
143783
|
})(),
|
|
143713
143784
|
(async () => {
|
|
143714
|
-
const proc =
|
|
143785
|
+
const proc = _internals102.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
|
|
143715
143786
|
try {
|
|
143716
143787
|
return await Promise.all([proc.exited, proc.stdout.text()]);
|
|
143717
143788
|
} finally {
|
|
@@ -143721,7 +143792,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
143721
143792
|
}
|
|
143722
143793
|
})(),
|
|
143723
143794
|
(async () => {
|
|
143724
|
-
const proc =
|
|
143795
|
+
const proc = _internals102.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
|
|
143725
143796
|
try {
|
|
143726
143797
|
return await proc.exited;
|
|
143727
143798
|
} finally {
|
|
@@ -143760,7 +143831,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
143760
143831
|
}
|
|
143761
143832
|
} catch {}
|
|
143762
143833
|
}
|
|
143763
|
-
const trackedProc =
|
|
143834
|
+
const trackedProc = _internals102.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
|
|
143764
143835
|
let trackedExitCode;
|
|
143765
143836
|
let trackedOutput;
|
|
143766
143837
|
try {
|
|
@@ -143785,7 +143856,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
143785
143856
|
}
|
|
143786
143857
|
|
|
143787
143858
|
// src/hooks/diff-scope.ts
|
|
143788
|
-
var
|
|
143859
|
+
var _internals103 = { bunSpawn };
|
|
143789
143860
|
function getDeclaredScope(taskId, directory) {
|
|
143790
143861
|
try {
|
|
143791
143862
|
const planPath = path185.join(directory, ".swarm", "plan.json");
|
|
@@ -143820,7 +143891,7 @@ var GIT_DIFF_SPAWN_OPTIONS = {
|
|
|
143820
143891
|
};
|
|
143821
143892
|
async function getChangedFiles2(directory) {
|
|
143822
143893
|
try {
|
|
143823
|
-
const proc =
|
|
143894
|
+
const proc = _internals103.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
|
|
143824
143895
|
cwd: directory,
|
|
143825
143896
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
143826
143897
|
});
|
|
@@ -143837,7 +143908,7 @@ async function getChangedFiles2(directory) {
|
|
|
143837
143908
|
return stdout.trim().split(`
|
|
143838
143909
|
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
143839
143910
|
}
|
|
143840
|
-
const proc2 =
|
|
143911
|
+
const proc2 = _internals103.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
|
|
143841
143912
|
cwd: directory,
|
|
143842
143913
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
143843
143914
|
});
|
|
@@ -143895,7 +143966,7 @@ init_telemetry();
|
|
|
143895
143966
|
init_file_locks();
|
|
143896
143967
|
import * as fs128 from "node:fs";
|
|
143897
143968
|
import * as path186 from "node:path";
|
|
143898
|
-
var
|
|
143969
|
+
var _internals104 = {
|
|
143899
143970
|
listActiveLocks,
|
|
143900
143971
|
verifyLeanTurboTaskCompletion
|
|
143901
143972
|
};
|
|
@@ -144037,7 +144108,7 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
|
144037
144108
|
}
|
|
144038
144109
|
};
|
|
144039
144110
|
}
|
|
144040
|
-
const activeLocks =
|
|
144111
|
+
const activeLocks = _internals104.listActiveLocks(directory);
|
|
144041
144112
|
const laneLocks = activeLocks.filter((lock) => lock.laneId === lane.laneId);
|
|
144042
144113
|
if (laneLocks.length > 0) {
|
|
144043
144114
|
return {
|
|
@@ -144104,7 +144175,7 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
|
144104
144175
|
init_task_id();
|
|
144105
144176
|
init_create_tool();
|
|
144106
144177
|
init_resolve_working_directory();
|
|
144107
|
-
var
|
|
144178
|
+
var _internals105 = {
|
|
144108
144179
|
tryAcquireLock,
|
|
144109
144180
|
updateTaskStatus,
|
|
144110
144181
|
resolveWorkingDirectory
|
|
@@ -144201,7 +144272,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
144201
144272
|
}
|
|
144202
144273
|
let resolvedDir;
|
|
144203
144274
|
if (fallbackDir) {
|
|
144204
|
-
const resolveResult =
|
|
144275
|
+
const resolveResult = _internals105.resolveWorkingDirectory(workingDirectory, fallbackDir);
|
|
144205
144276
|
if (resolveResult.success) {
|
|
144206
144277
|
resolvedDir = resolveResult.directory;
|
|
144207
144278
|
} else {
|
|
@@ -144548,7 +144619,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
144548
144619
|
}
|
|
144549
144620
|
}
|
|
144550
144621
|
let directory;
|
|
144551
|
-
const resolveResult =
|
|
144622
|
+
const resolveResult = _internals105.resolveWorkingDirectory(args2.working_directory, fallbackDir);
|
|
144552
144623
|
if (!resolveResult.success) {
|
|
144553
144624
|
return {
|
|
144554
144625
|
success: false,
|
|
@@ -144641,7 +144712,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
144641
144712
|
}
|
|
144642
144713
|
let lockResult;
|
|
144643
144714
|
try {
|
|
144644
|
-
lockResult = await
|
|
144715
|
+
lockResult = await _internals105.tryAcquireLock(directory, planFilePath, agentName, lockTaskId);
|
|
144645
144716
|
} catch (error93) {
|
|
144646
144717
|
return {
|
|
144647
144718
|
success: false,
|
|
@@ -144660,7 +144731,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
144660
144731
|
};
|
|
144661
144732
|
}
|
|
144662
144733
|
try {
|
|
144663
|
-
const updatedPlan = await
|
|
144734
|
+
const updatedPlan = await _internals105.updateTaskStatus(directory, args2.task_id, args2.status);
|
|
144664
144735
|
if (args2.status === "completed") {
|
|
144665
144736
|
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
144666
144737
|
if (!(session.taskWorkflowStates instanceof Map)) {
|
|
@@ -145315,7 +145386,7 @@ var web_fetch = createSwarmTool({
|
|
|
145315
145386
|
};
|
|
145316
145387
|
return JSON.stringify(fail, null, 2);
|
|
145317
145388
|
}
|
|
145318
|
-
const config3 =
|
|
145389
|
+
const config3 = _internals106.loadPluginConfig(dirResult.directory);
|
|
145319
145390
|
const generalConfig = config3.council?.general;
|
|
145320
145391
|
if (!generalConfig || generalConfig.enabled !== true) {
|
|
145321
145392
|
const fail = {
|
|
@@ -145325,7 +145396,7 @@ var web_fetch = createSwarmTool({
|
|
|
145325
145396
|
};
|
|
145326
145397
|
return JSON.stringify(fail, null, 2);
|
|
145327
145398
|
}
|
|
145328
|
-
const validated = await validateFetchUrl(parsed.data.url,
|
|
145399
|
+
const validated = await validateFetchUrl(parsed.data.url, _internals106.dnsLookup);
|
|
145329
145400
|
if (!validated.ok) {
|
|
145330
145401
|
const fail = {
|
|
145331
145402
|
success: false,
|
|
@@ -145336,7 +145407,7 @@ var web_fetch = createSwarmTool({
|
|
|
145336
145407
|
}
|
|
145337
145408
|
const maxBytes = parsed.data.max_bytes ?? DEFAULT_MAX_BYTES;
|
|
145338
145409
|
const timeoutMs = parsed.data.timeout_ms ?? DEFAULT_TIMEOUT_MS5;
|
|
145339
|
-
const result = await boundedFetch({ url: validated.url, address: validated.address }, maxBytes, timeoutMs,
|
|
145410
|
+
const result = await boundedFetch({ url: validated.url, address: validated.address }, maxBytes, timeoutMs, _internals106);
|
|
145340
145411
|
if (!result.ok) {
|
|
145341
145412
|
const fail = {
|
|
145342
145413
|
success: false,
|
|
@@ -145371,7 +145442,7 @@ var web_fetch = createSwarmTool({
|
|
|
145371
145442
|
});
|
|
145372
145443
|
async function captureFetchEvidence(directory, url3, title, text) {
|
|
145373
145444
|
try {
|
|
145374
|
-
const written = await
|
|
145445
|
+
const written = await _internals106.writeEvidenceDocuments(directory, [
|
|
145375
145446
|
{
|
|
145376
145447
|
sourceType: "crawl",
|
|
145377
145448
|
url: url3,
|
|
@@ -145392,7 +145463,7 @@ async function captureFetchEvidence(directory, url3, title, text) {
|
|
|
145392
145463
|
};
|
|
145393
145464
|
}
|
|
145394
145465
|
}
|
|
145395
|
-
var
|
|
145466
|
+
var _internals106 = {
|
|
145396
145467
|
httpRequest: performHttpRequest,
|
|
145397
145468
|
dnsLookup: lookup,
|
|
145398
145469
|
loadPluginConfig,
|
|
@@ -145706,7 +145777,7 @@ var web_search = createSwarmTool({
|
|
|
145706
145777
|
});
|
|
145707
145778
|
async function captureSearchEvidence(directory, query, results) {
|
|
145708
145779
|
try {
|
|
145709
|
-
const written = await
|
|
145780
|
+
const written = await _internals107.writeEvidenceDocuments(directory, results.map((result) => ({
|
|
145710
145781
|
sourceType: "web_search",
|
|
145711
145782
|
query,
|
|
145712
145783
|
title: result.title,
|
|
@@ -145734,7 +145805,7 @@ async function captureSearchEvidence(directory, query, results) {
|
|
|
145734
145805
|
};
|
|
145735
145806
|
}
|
|
145736
145807
|
}
|
|
145737
|
-
var
|
|
145808
|
+
var _internals107 = {
|
|
145738
145809
|
writeEvidenceDocuments
|
|
145739
145810
|
};
|
|
145740
145811
|
|
|
@@ -122,6 +122,19 @@ export interface LaneResult {
|
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
124
|
* Result of a full phase run.
|
|
125
|
+
*
|
|
126
|
+
* ## Serial Tasks Contract
|
|
127
|
+
*
|
|
128
|
+
* `serializedTasks` contains task IDs excluded from parallel lanes due to lock conflicts
|
|
129
|
+
* (Issue #1 - Serial Task Orphan Risk).
|
|
130
|
+
*
|
|
131
|
+
* **Caller Responsibility**: The orchestrator MUST complete these tasks via standard
|
|
132
|
+
* serial flow (normal task dispatch). The runner does NOT dispatch serializedTasks.
|
|
133
|
+
*
|
|
134
|
+
* **Verification**: phase-ready (step 6b) verifies serializedTasks by checking that
|
|
135
|
+
* each task ID has status: completed in plan.json. If serializedTasks contain task IDs
|
|
136
|
+
* but those tasks are not marked completed in plan.json, phase-ready returns ok: false,
|
|
137
|
+
* blocking phase advancement.
|
|
125
138
|
*/
|
|
126
139
|
export interface LeanTurboPhaseResult {
|
|
127
140
|
/** Whether the phase ran (at least one lane attempted) */
|
|
@@ -132,7 +145,11 @@ export interface LeanTurboPhaseResult {
|
|
|
132
145
|
lanes: LaneResult[];
|
|
133
146
|
/** Task IDs that were degraded (risk conditions) */
|
|
134
147
|
degradedTasks: string[];
|
|
135
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Task IDs excluded from parallel lanes due to lock conflicts.
|
|
150
|
+
* Caller must complete these via standard serial flow before phase can advance.
|
|
151
|
+
* (Issue #1: Serial Task Orphan Risk - Fixed with integration test)
|
|
152
|
+
*/
|
|
136
153
|
serializedTasks: string[];
|
|
137
154
|
/** Lanes whose coder completed but merge-back to primary branch failed */
|
|
138
155
|
mergeBackFailures?: MergeBackFailureInfo[];
|
|
@@ -326,8 +343,11 @@ export declare class LeanTurboRunner {
|
|
|
326
343
|
*/
|
|
327
344
|
private _selectNextAgent;
|
|
328
345
|
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
346
|
+
* Write lane evidence with retry logic for transient disk errors.
|
|
347
|
+
*
|
|
348
|
+
* Evidence is required by phase-ready (step 4b) to verify lane completion.
|
|
349
|
+
* Transient I/O errors are retried with exponential backoff; permanent errors
|
|
350
|
+
* are logged and dropped (evidence failure is non-fatal for runner operation).
|
|
331
351
|
*/
|
|
332
352
|
private _writeLaneEvidenceSafely;
|
|
333
353
|
/**
|
|
@@ -335,11 +355,20 @@ export declare class LeanTurboRunner {
|
|
|
335
355
|
*/
|
|
336
356
|
private _buildLanePrompt;
|
|
337
357
|
/**
|
|
338
|
-
* Serializes access to durable state via a promise chain.
|
|
339
|
-
*
|
|
358
|
+
* Serializes access to durable state via a promise chain AND file-based lock.
|
|
359
|
+
*
|
|
360
|
+
* - Promise chain: serializes writes within a single runner instance
|
|
361
|
+
* - File-based lock: coordinates between multiple runners with the same sessionID
|
|
362
|
+
*
|
|
363
|
+
* Timeout budget starts when this call reaches the front of the queue (execution
|
|
364
|
+
* time), not when it is enqueued. withTurboStateLock computes its deadline
|
|
365
|
+
* internally on entry, so each caller gets a full 10-second window regardless
|
|
366
|
+
* of how long it waited behind prior entries.
|
|
340
367
|
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
368
|
+
* Timeout coverage: withTurboStateLock is tested directly in state-lock.test.ts
|
|
369
|
+
* (test: "throws TurboStateLockTimeoutError when lock is held by another caller").
|
|
370
|
+
* This wrapper delegates to it in a single line with no additional timeout logic,
|
|
371
|
+
* so a separate wrapper-level timeout test would duplicate that coverage.
|
|
343
372
|
*/
|
|
344
373
|
private _withStateLock;
|
|
345
374
|
/**
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Lean Turbo state write-lock helper.
|
|
3
|
+
*
|
|
4
|
+
* ## Issue #2: Cross-Runner Durable State Race
|
|
5
|
+
*
|
|
6
|
+
* Multiple LeanTurboRunner instances with the same sessionID share turbo-state.json.
|
|
7
|
+
* Each runner has its own in-process _stateLock promise chain, but without file-level
|
|
8
|
+
* coordination, concurrent runners can race:
|
|
9
|
+
*
|
|
10
|
+
* - Runner A: load state → modify → write
|
|
11
|
+
* - Runner B: load state (gets A's version) → modify → write (overwrites A's changes)
|
|
12
|
+
*
|
|
13
|
+
* This module adds file-based locking using proper-lockfile to coordinate read-modify-write
|
|
14
|
+
* cycles across processes, preventing inter-runner write races.
|
|
15
|
+
*
|
|
16
|
+
* ## Pattern
|
|
17
|
+
*
|
|
18
|
+
* Mirrors the evidence-lock pattern (src/evidence/lock.ts) with:
|
|
19
|
+
* - Exponential backoff (50ms start, 2000ms max) with jitter
|
|
20
|
+
* - Timeout protection (default 30 seconds for state, 60 seconds for evidence)
|
|
21
|
+
* - Best-effort lock release in finally block
|
|
22
|
+
* - Non-fatal lock release failures (proper-lockfile TTL cleanup)
|
|
23
|
+
*
|
|
24
|
+
* ## Telemetry
|
|
25
|
+
*
|
|
26
|
+
* Events emitted (when integrated into runners):
|
|
27
|
+
* - turbo_state_lock_acquired
|
|
28
|
+
* - turbo_state_lock_contended
|
|
29
|
+
* - turbo_state_lock_timeout (thrown as error)
|
|
30
|
+
*/
|
|
31
|
+
import { tryAcquireLock } from '../../parallel/file-locks';
|
|
32
|
+
/** Test-only seam — allows injecting a mock tryAcquireLock without mock.module. */
|
|
33
|
+
export declare const _internals: {
|
|
34
|
+
tryAcquireLock: typeof tryAcquireLock;
|
|
35
|
+
};
|
|
36
|
+
export declare class TurboStateLockTimeoutError extends Error {
|
|
37
|
+
readonly directory: string;
|
|
38
|
+
readonly sessionID: string;
|
|
39
|
+
constructor(directory: string, sessionID: string, timeoutMs: number);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Acquire an exclusive turbo-state lock, execute `fn`, then release the lock.
|
|
43
|
+
*
|
|
44
|
+
* Retries with exponential backoff until `timeoutMs` elapses,
|
|
45
|
+
* then throws `TurboStateLockTimeoutError`. Always releases the lock in a
|
|
46
|
+
* `finally` block even if `fn` throws.
|
|
47
|
+
*
|
|
48
|
+
* **Timeout scope**: `timeoutMs` governs lock _acquisition_ only. Once the lock
|
|
49
|
+
* is held, `fn()` runs to completion with no separate execution deadline.
|
|
50
|
+
* Callers must ensure `fn` is bounded (e.g., synchronous disk writes). This
|
|
51
|
+
* matches the behaviour of the previous `Promise.race` implementation, which
|
|
52
|
+
* also could not cancel an in-flight `fn()`.
|
|
53
|
+
*
|
|
54
|
+
* @param directory Project root directory
|
|
55
|
+
* @param sessionID Session identifier (for lock metadata and diagnostics)
|
|
56
|
+
* @param fn Callback that performs the read-modify-write on turbo state
|
|
57
|
+
* @param timeoutMs Maximum wait time for lock acquisition (default 30 000ms)
|
|
58
|
+
*/
|
|
59
|
+
export declare function withTurboStateLock<T>(directory: string, sessionID: string, fn: () => Promise<T>, timeoutMs?: number): Promise<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.78.
|
|
3
|
+
"version": "7.78.5",
|
|
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",
|