opencode-swarm 7.77.5 → 7.77.7

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.77.5",
55
+ version: "7.77.7",
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.77.5",
72
+ version: "7.77.7",
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",
@@ -105641,7 +105641,9 @@ async function dispatchFullAutoOversight(input) {
105641
105641
  const oversightAgent = createCriticAutonomousOversightAgent(input.criticModel, buildOversightPrompt(input));
105642
105642
  log(`[full-auto/oversight] Dispatching ${oversightAgent.name} via ${input.oversightAgentName} (model=${input.criticModel}, trigger=${input.triggerSource})`);
105643
105643
  let ephemeralSessionId;
105644
+ const promptController = new AbortController;
105644
105645
  const cleanup = () => {
105646
+ promptController.abort();
105645
105647
  if (ephemeralSessionId) {
105646
105648
  const id = ephemeralSessionId;
105647
105649
  ephemeralSessionId = undefined;
@@ -105670,7 +105672,8 @@ async function dispatchFullAutoOversight(input) {
105670
105672
  agent: input.oversightAgentName,
105671
105673
  tools: { write: false, edit: false, patch: false },
105672
105674
  parts: [{ type: "text", text: buildOversightPrompt(input) }]
105673
- }
105675
+ },
105676
+ signal: promptController.signal
105674
105677
  });
105675
105678
  if (!promptResult.data) {
105676
105679
  throw new Error(`Critic prompt failed: ${JSON.stringify(promptResult.error)}`);
@@ -107582,7 +107585,9 @@ async function dispatchCriticAndWriteEvent(directory, architectOutput, criticCon
107582
107585
  const oversightAgent = createCriticAutonomousOversightAgent(criticModel, criticContext);
107583
107586
  log(`[full-auto-intercept] Dispatching critic: ${oversightAgent.name} using model ${criticModel}`);
107584
107587
  let ephemeralSessionId;
107588
+ const promptController = new AbortController;
107585
107589
  const cleanup = () => {
107590
+ promptController.abort();
107586
107591
  if (ephemeralSessionId) {
107587
107592
  const id = ephemeralSessionId;
107588
107593
  ephemeralSessionId = undefined;
@@ -107608,7 +107613,8 @@ async function dispatchCriticAndWriteEvent(directory, architectOutput, criticCon
107608
107613
  agent: oversightAgentName,
107609
107614
  tools: { write: false, edit: false, patch: false },
107610
107615
  parts: [{ type: "text", text: criticContext }]
107611
- }
107616
+ },
107617
+ signal: promptController.signal
107612
107618
  });
107613
107619
  if (!promptResult.data) {
107614
107620
  throw new Error(`Critic LLM prompt failed: ${JSON.stringify(promptResult.error)}`);
@@ -110351,11 +110357,13 @@ init_path_security();
110351
110357
  import { constants as constants5, existsSync as existsSync64, readFileSync as readFileSync44, statSync as statSync24 } from "node:fs";
110352
110358
  import * as fsPromises6 from "node:fs/promises";
110353
110359
  import * as path111 from "node:path";
110360
+ var WINDOWS_RENAME_MAX_RETRIES2 = 5;
110361
+ var WINDOWS_RENAME_RETRY_DELAY_MS2 = 100;
110354
110362
  var _internals68 = {
110355
- safeRealpathSync
110363
+ safeRealpathSync,
110364
+ fsRename: fsPromises6.rename.bind(fsPromises6),
110365
+ retryDelayMs: WINDOWS_RENAME_RETRY_DELAY_MS2
110356
110366
  };
110357
- var WINDOWS_RENAME_MAX_RETRIES2 = 3;
110358
- var WINDOWS_RENAME_RETRY_DELAY_MS2 = 50;
110359
110367
  function validateLoadedGraph(parsed) {
110360
110368
  if (!parsed.schema_version) {
110361
110369
  throw Object.assign(new Error("repo-graph.json missing schema_version"), {
@@ -110537,21 +110545,25 @@ async function saveGraph(workspace, graph, options) {
110537
110545
  throw lastError;
110538
110546
  }
110539
110547
  } else {
110540
- let retries = 0;
110541
- while (retries < WINDOWS_RENAME_MAX_RETRIES2) {
110548
+ for (let attempt = 0;attempt < WINDOWS_RENAME_MAX_RETRIES2; attempt++) {
110542
110549
  try {
110543
- await fsPromises6.rename(tempPath, graphPath);
110550
+ await _internals68.fsRename(tempPath, graphPath);
110551
+ lastError = null;
110544
110552
  break;
110545
110553
  } catch (error93) {
110546
110554
  lastError = error93 instanceof Error ? error93 : new Error(String(error93));
110547
- if (lastError instanceof Error && "code" in lastError && lastError.code === "EEXIST" && retries < WINDOWS_RENAME_MAX_RETRIES2 - 1) {
110548
- retries++;
110549
- await new Promise((resolve41) => setTimeout(resolve41, WINDOWS_RENAME_RETRY_DELAY_MS2));
110550
- continue;
110555
+ const code = lastError.code;
110556
+ if (code !== "EEXIST" && code !== "EPERM" && code !== "EBUSY") {
110557
+ break;
110558
+ }
110559
+ if (attempt < WINDOWS_RENAME_MAX_RETRIES2 - 1) {
110560
+ await new Promise((resolve41) => setTimeout(resolve41, _internals68.retryDelayMs));
110551
110561
  }
110552
- throw lastError;
110553
110562
  }
110554
110563
  }
110564
+ if (lastError) {
110565
+ throw lastError;
110566
+ }
110555
110567
  }
110556
110568
  } finally {
110557
110569
  try {
@@ -114183,6 +114195,8 @@ async function defaultDispatchReviewerAgent(directory, reviewPackage, agentName,
114183
114195
  throw new Error("Failed to create reviewer session");
114184
114196
  }
114185
114197
  const sessionId = sessionResult.data.id;
114198
+ const promptController = new AbortController;
114199
+ let timeoutHandle;
114186
114200
  try {
114187
114201
  const promptText = `You are a read-only phase reviewer for Lean Turbo execution.
114188
114202
 
@@ -114227,16 +114241,23 @@ Be specific and evidence-based. Do not approve a phase with unresolved degraded
114227
114241
  agent: agentName,
114228
114242
  tools: { write: false, edit: false, patch: false },
114229
114243
  parts: [{ type: "text", text: promptText }]
114230
- }
114244
+ },
114245
+ signal: promptController.signal
114231
114246
  }),
114232
- new Promise((_, reject) => setTimeout(() => reject(new Error(`Reviewer dispatch timed out after ${timeoutMs}ms`)), timeoutMs))
114247
+ new Promise((_, reject) => {
114248
+ timeoutHandle = setTimeout(() => {
114249
+ promptController.abort();
114250
+ reject(new Error(`Reviewer dispatch timed out after ${timeoutMs}ms`));
114251
+ }, timeoutMs);
114252
+ })
114233
114253
  ]) : await client.session.prompt({
114234
114254
  path: { id: sessionId },
114235
114255
  body: {
114236
114256
  agent: agentName,
114237
114257
  tools: { write: false, edit: false, patch: false },
114238
114258
  parts: [{ type: "text", text: promptText }]
114239
- }
114259
+ },
114260
+ signal: promptController.signal
114240
114261
  });
114241
114262
  if (!response.data) {
114242
114263
  throw new Error("Reviewer session returned no data");
@@ -114245,6 +114266,9 @@ Be specific and evidence-based. Do not approve a phase with unresolved degraded
114245
114266
  `);
114246
114267
  return textParts;
114247
114268
  } finally {
114269
+ if (timeoutHandle !== undefined)
114270
+ clearTimeout(timeoutHandle);
114271
+ promptController.abort();
114248
114272
  client.session.delete({ path: { id: sessionId } }).catch(() => {});
114249
114273
  }
114250
114274
  }
@@ -114528,6 +114552,7 @@ async function dispatchReviewer(directory, prompt, agentName, timeoutMs) {
114528
114552
  throw new Error("Failed to create auto-review session");
114529
114553
  }
114530
114554
  const sessionId = createResult.data.id;
114555
+ const promptController = new AbortController;
114531
114556
  let timeoutHandle;
114532
114557
  try {
114533
114558
  const promptCall = client.session.prompt({
@@ -114536,12 +114561,16 @@ async function dispatchReviewer(directory, prompt, agentName, timeoutMs) {
114536
114561
  agent: agentName,
114537
114562
  tools: { write: false, edit: false, patch: false },
114538
114563
  parts: [{ type: "text", text: prompt }]
114539
- }
114564
+ },
114565
+ signal: promptController.signal
114540
114566
  });
114541
114567
  const response = await Promise.race([
114542
114568
  promptCall,
114543
114569
  new Promise((_, reject) => {
114544
- timeoutHandle = setTimeout(() => reject(new Error(`auto-review timed out after ${timeoutMs}ms`)), timeoutMs);
114570
+ timeoutHandle = setTimeout(() => {
114571
+ promptController.abort();
114572
+ reject(new Error(`auto-review timed out after ${timeoutMs}ms`));
114573
+ }, timeoutMs);
114545
114574
  })
114546
114575
  ]);
114547
114576
  if (!response.data) {
@@ -114552,6 +114581,7 @@ async function dispatchReviewer(directory, prompt, agentName, timeoutMs) {
114552
114581
  } finally {
114553
114582
  if (timeoutHandle !== undefined)
114554
114583
  clearTimeout(timeoutHandle);
114584
+ promptController.abort();
114555
114585
  client.session.delete({ path: { id: sessionId } }).catch(() => {});
114556
114586
  }
114557
114587
  }
@@ -124895,6 +124925,7 @@ async function runLane(session, dispatcher, lane, directory, timeoutMs, context)
124895
124925
  error: `dispatcher ${decision.action}: ${decision.reason}`
124896
124926
  };
124897
124927
  }
124928
+ const promptController = new AbortController;
124898
124929
  let sessionId;
124899
124930
  try {
124900
124931
  const createTimeoutMessage = `Lane "${lane.id}" session.create timed out after ${timeoutMs}ms`;
@@ -124923,8 +124954,9 @@ async function runLane(session, dispatcher, lane, directory, timeoutMs, context)
124923
124954
  agent: lane.agent,
124924
124955
  tools: buildReadOnlyTools(),
124925
124956
  parts: [{ type: "text", text: lane.prompt }]
124926
- }
124927
- }), timeoutMs, `Lane "${lane.id}" session.prompt timed out after ${timeoutMs}ms`);
124957
+ },
124958
+ signal: promptController.signal
124959
+ }), timeoutMs, `Lane "${lane.id}" session.prompt timed out after ${timeoutMs}ms`, promptController);
124928
124960
  if (!promptResult.data) {
124929
124961
  return failedLane(lane, role, startedAt, `session.prompt failed: ${formatError3(promptResult.error)}`, decision.slot.slotId, decision.slot.runId, sessionId);
124930
124962
  }
@@ -124945,6 +124977,7 @@ async function runLane(session, dispatcher, lane, directory, timeoutMs, context)
124945
124977
  return failedLane(lane, role, startedAt, formatError3(error93), decision.slot.slotId, decision.slot.runId, sessionId);
124946
124978
  } finally {
124947
124979
  dispatcher.releaseSlot(decision.slot.slotId);
124980
+ promptController.abort();
124948
124981
  if (sessionId) {
124949
124982
  scheduleSessionCleanup(session, sessionId);
124950
124983
  }
@@ -125082,13 +125115,19 @@ function scheduleSessionCleanup(session, sessionId) {
125082
125115
  return;
125083
125116
  });
125084
125117
  }
125085
- async function withTimeout2(promise3, timeoutMs, message) {
125118
+ async function withTimeout2(promise3, timeoutMs, message, controller) {
125086
125119
  let timeout;
125087
125120
  try {
125088
125121
  return await Promise.race([
125089
125122
  promise3,
125090
125123
  new Promise((_, reject) => {
125091
- timeout = setTimeout(() => reject(new Error(message)), timeoutMs);
125124
+ timeout = setTimeout(() => {
125125
+ controller?.abort();
125126
+ reject(new Error(message));
125127
+ }, timeoutMs);
125128
+ if (typeof timeout.unref === "function") {
125129
+ timeout.unref();
125130
+ }
125092
125131
  })
125093
125132
  ]);
125094
125133
  } finally {
@@ -127361,7 +127400,9 @@ async function generateMutants(files, ctx) {
127361
127400
  }
127362
127401
  const directory = ctx.directory ?? process.cwd();
127363
127402
  let ephemeralSessionId;
127403
+ const promptController = new AbortController;
127364
127404
  const cleanup = () => {
127405
+ promptController.abort();
127365
127406
  if (ephemeralSessionId) {
127366
127407
  const id = ephemeralSessionId;
127367
127408
  ephemeralSessionId = undefined;
@@ -127408,7 +127449,8 @@ Return ONLY a valid JSON array. No markdown, no code fences, no explanation. Sta
127408
127449
  agent: undefined,
127409
127450
  tools: { write: false, edit: false, patch: false },
127410
127451
  parts: [{ type: "text", text: promptText }]
127411
- }
127452
+ },
127453
+ signal: promptController.signal
127412
127454
  });
127413
127455
  if (!promptResult.data) {
127414
127456
  console.warn(`[generateMutants] LLM prompt failed: ${JSON.stringify(promptResult.error)}; returning empty patch set`);
@@ -130336,10 +130378,17 @@ class LeanTurboRunner {
130336
130378
  if (!session) {
130337
130379
  return { ok: false, error: "NO_CLIENT" };
130338
130380
  }
130339
- const dispatchPromise = this._doDispatch(session, lane, agentName, worktreeDirectory);
130381
+ const promptController = new AbortController;
130382
+ const dispatchPromise = this._doDispatch(session, lane, agentName, worktreeDirectory, promptController);
130340
130383
  const timeoutMs = LeanTurboRunner._internals.laneDispatchTimeoutMs;
130341
130384
  if (timeoutMs !== undefined && timeoutMs > 0) {
130342
- const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Lane dispatch timed out after ${timeoutMs}ms`)), timeoutMs));
130385
+ let timeoutHandle;
130386
+ const timeoutPromise = new Promise((_, reject) => {
130387
+ timeoutHandle = setTimeout(() => {
130388
+ promptController.abort();
130389
+ reject(new Error(`Lane dispatch timed out after ${timeoutMs}ms`));
130390
+ }, timeoutMs);
130391
+ });
130343
130392
  try {
130344
130393
  return await Promise.race([dispatchPromise, timeoutPromise]);
130345
130394
  } catch (err2) {
@@ -130363,11 +130412,15 @@ class LeanTurboRunner {
130363
130412
  return { ok: false, error: err2.message };
130364
130413
  }
130365
130414
  throw err2;
130415
+ } finally {
130416
+ if (timeoutHandle !== undefined)
130417
+ clearTimeout(timeoutHandle);
130366
130418
  }
130367
130419
  }
130368
130420
  return dispatchPromise;
130369
130421
  }
130370
- async _doDispatch(session, lane, agentName, worktreeDirectory) {
130422
+ async _doDispatch(session, lane, agentName, worktreeDirectory, abortController) {
130423
+ let sessionId;
130371
130424
  try {
130372
130425
  const effectiveDirectory = worktreeDirectory ?? this._directory;
130373
130426
  const createResult = await session.create({
@@ -130385,7 +130438,7 @@ class LeanTurboRunner {
130385
130438
  error: `session.create failed: ${typeof createResult.error === "string" ? createResult.error : JSON.stringify(createResult.error)}`
130386
130439
  };
130387
130440
  }
130388
- const sessionId = createResult.data.id;
130441
+ sessionId = createResult.data.id;
130389
130442
  const promptText = this._buildLanePrompt(lane);
130390
130443
  const promptResult = await session.prompt({
130391
130444
  path: { id: sessionId },
@@ -130393,9 +130446,11 @@ class LeanTurboRunner {
130393
130446
  agent: agentName,
130394
130447
  tools: { write: true, edit: true, patch: true },
130395
130448
  parts: [{ type: "text", text: promptText }]
130396
- }
130449
+ },
130450
+ signal: abortController?.signal
130397
130451
  });
130398
130452
  if (!promptResult.data) {
130453
+ abortController?.abort();
130399
130454
  session.delete({ path: { id: sessionId } }).catch(() => {});
130400
130455
  return {
130401
130456
  ok: false,
@@ -130404,6 +130459,10 @@ class LeanTurboRunner {
130404
130459
  }
130405
130460
  return { ok: true, sessionId };
130406
130461
  } catch (err2) {
130462
+ if (sessionId) {
130463
+ abortController?.abort();
130464
+ session.delete({ path: { id: sessionId } }).catch(() => {});
130465
+ }
130407
130466
  const msg = err2 instanceof Error ? err2.message : String(err2);
130408
130467
  return { ok: false, error: msg };
130409
130468
  }
@@ -69,6 +69,7 @@ export interface SessionOps {
69
69
  text: string;
70
70
  }>;
71
71
  };
72
+ signal?: AbortSignal;
72
73
  }): Promise<{
73
74
  data?: {
74
75
  parts?: Array<{
@@ -5,14 +5,21 @@
5
5
  * writes. Reads validate schema and content before updating the in-memory
6
6
  * cache. Symlink resolution guards against workspace-escape attacks.
7
7
  */
8
+ import * as fsPromises from 'node:fs/promises';
8
9
  import { safeRealpathSync } from './safe-realpath';
9
10
  import type { RepoGraph } from './types';
10
11
  /**
11
12
  * Internal function references for testability.
12
13
  * Replace _internals.safeRealpathSync in tests to mock symlink resolution.
14
+ * Replace _internals.fsRename to exercise the rename retry path (e.g. EPERM).
15
+ * Set _internals.retryDelayMs = 0 in tests to skip real sleeps while still
16
+ * exercising multi-retry paths.
17
+ * Restore each entry in afterEach via the saved original reference.
13
18
  */
14
19
  export declare const _internals: {
15
20
  safeRealpathSync: typeof safeRealpathSync;
21
+ fsRename: typeof fsPromises.rename;
22
+ retryDelayMs: number;
16
23
  };
17
24
  /**
18
25
  * Get the validated path for the repo-graph.json file.
@@ -63,6 +63,7 @@ interface SessionClient {
63
63
  text: string;
64
64
  }>;
65
65
  };
66
+ signal?: AbortSignal;
66
67
  }): Promise<{
67
68
  data: {
68
69
  parts: Array<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.77.5",
3
+ "version": "7.77.7",
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",