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 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.4",
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.4",
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 === "violation").length;
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 === "violation").length;
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 === "violation").slice(-10).map((e) => ({
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: () => _internals94
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, _internals94;
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
- _internals94 = {
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: () => _internals107,
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 _internals107.pickBackend(directory);
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 = _internals107.pickedProfiles(directory);
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, _internals107;
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
- _internals107 = {
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
- try {
131066
- const evidence = {
131067
- laneId: lane.laneId,
131068
- taskIds: lane.taskIds,
131069
- files: lane.files,
131070
- status,
131071
- startedAt: lane.startedAt,
131072
- ...extras
131073
- };
131074
- const runState = LeanTurboRunner._internals.loadLeanTurboRunState(this._directory, this._sessionID);
131075
- const phase = runState?.phase;
131076
- if (phase !== undefined) {
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
- } catch {}
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 timeoutMs = 1e4;
131099
- let timeoutId;
131100
- const withTimeout3 = new Promise((_resolve, reject) => {
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 _internals89 = {
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 } = _internals89.loadPluginConfigWithMeta(directory);
131270
+ const { config: config3 } = _internals90.loadPluginConfigWithMeta(directory);
131200
131271
  const leanConfig = config3.turbo?.strategy === "lean" ? config3.turbo.lean : undefined;
131201
- runner = new _internals89.LeanTurboRunner({
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 _internals90 = {
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 _internals90.checkEquivalence(patch, originalCode, mutatedCode, llmJudge);
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 _internals91 = {
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 = _internals91.spawnSync("git", ["apply", "--", patchFile], {
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 = _internals91.spawnSync(testCommand[0], testArgs, {
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 = _internals91.spawnSync("git", ["apply", "-R", "--", patchFile], {
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 _internals92 = {
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 = _internals92.buildTestImprovementPrompt(report, passThreshold, verdict);
131952
- const message = _internals92.buildMessage(verdict, adjustedKillRate, report.killed, report.totalMutants, report.equivalent, warnThreshold);
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 _internals93 = {
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 = _internals93.readPersisted(directory);
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(_internals93.listLaneEvidenceSync(directory, phase));
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 = _internals93.listActiveLocks(directory);
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 = _internals93.readPlanJson(directory);
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 = _internals93.readPlanJson(directory);
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 = _internals93.readReviewerEvidence(directory, phase);
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 = _internals93.readCriticEvidence(directory, phase);
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 = _internals93.verifyLeanTurboPhaseReady(dir, phase, sessionID, leanPhaseReadyConfig);
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 _internals95.qualityBudget(args2, directory);
136346
+ const result = await _internals96.qualityBudget(args2, directory);
136276
136347
  return JSON.stringify(result);
136277
136348
  }
136278
136349
  });
136279
- var _internals95 = {
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 _internals96 = {
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 _internals96.isSemgrepAvailable();
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: _internals96.isSemgrepAvailable(),
137258
+ available: _internals97.isSemgrepAvailable(),
137188
137259
  findings: [],
137189
137260
  engine: "tier_a"
137190
137261
  };
137191
137262
  }
137192
- if (!_internals96.isSemgrepAvailable()) {
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 = _internals97.fingerprintFinding(finding, directory, occIdx);
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 = _internals97.assignOccurrenceIndices(findings, directory);
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 _internals97 = {
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 _internals98.sastScan(input, directory);
138049
+ const result = await _internals99.sastScan(input, directory);
137979
138050
  return JSON.stringify(result, null, 2);
137980
138051
  }
137981
138052
  });
137982
- var _internals98 = {
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 } = _internals99.loadPluginConfigWithMeta(directory);
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 = _internals99.createMemoryGateway({
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 _internals99 = {
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 } = _internals100.loadPluginConfigWithMeta(directory);
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 = _internals100.createMemoryGateway({
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 _internals100 = {
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 _internals101 = { bunSpawn };
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 = _internals101.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
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 = _internals101.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
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 = _internals101.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
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 = _internals101.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
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 _internals102 = { bunSpawn };
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 = _internals102.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
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 = _internals102.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
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 _internals103 = {
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 = _internals103.listActiveLocks(directory);
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 _internals104 = {
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 = _internals104.resolveWorkingDirectory(workingDirectory, fallbackDir);
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 = _internals104.resolveWorkingDirectory(args2.working_directory, fallbackDir);
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 _internals104.tryAcquireLock(directory, planFilePath, agentName, lockTaskId);
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 _internals104.updateTaskStatus(directory, args2.task_id, args2.status);
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 = _internals105.loadPluginConfig(dirResult.directory);
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, _internals105.dnsLookup);
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, _internals105);
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 _internals105.writeEvidenceDocuments(directory, [
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 _internals105 = {
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 _internals106.writeEvidenceDocuments(directory, results.map((result) => ({
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 _internals106 = {
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
- /** Task IDs excluded from parallel lanes, must complete via standard serial flow */
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
- * Safely write lane evidence, catching errors to prevent evidence write
330
- * failure from blocking lane processing.
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
- * Prevents concurrent lane updates from racing on turbo-state.json writes.
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
- * Includes a 10-second timeout: if state persistence hangs, the lock is
342
- * released so subsequent updates are not blocked indefinitely.
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.4",
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",