opencode-swarm-plugin 0.12.2 → 0.12.6

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/index.js CHANGED
@@ -21560,15 +21560,15 @@ var BeadDependencySchema = exports_external.object({
21560
21560
  type: exports_external.enum(["blocks", "blocked-by", "related", "discovered-from"])
21561
21561
  });
21562
21562
  var BeadSchema = exports_external.object({
21563
- id: exports_external.string().regex(/^[a-z0-9-]+-[a-z0-9]+(\.\d+)?$/, "Invalid bead ID format"),
21563
+ id: exports_external.string().regex(/^[a-z0-9]+(-[a-z0-9]+)+(\.\d+)?$/, "Invalid bead ID format"),
21564
21564
  title: exports_external.string().min(1, "Title required"),
21565
21565
  description: exports_external.string().optional().default(""),
21566
21566
  status: BeadStatusSchema.default("open"),
21567
21567
  priority: exports_external.number().int().min(0).max(3).default(2),
21568
21568
  issue_type: BeadTypeSchema.default("task"),
21569
- created_at: exports_external.string(),
21570
- updated_at: exports_external.string().optional(),
21571
- closed_at: exports_external.string().optional(),
21569
+ created_at: exports_external.string().datetime({ offset: true }),
21570
+ updated_at: exports_external.string().datetime({ offset: true }).optional(),
21571
+ closed_at: exports_external.string().datetime({ offset: true }).optional(),
21572
21572
  parent_id: exports_external.string().optional(),
21573
21573
  dependencies: exports_external.array(BeadDependencySchema).optional().default([]),
21574
21574
  metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
@@ -21641,7 +21641,7 @@ var EvaluationSchema = exports_external.object({
21641
21641
  criteria: exports_external.record(exports_external.string(), CriterionEvaluationSchema),
21642
21642
  overall_feedback: exports_external.string(),
21643
21643
  retry_suggestion: exports_external.string().nullable(),
21644
- timestamp: exports_external.string().optional()
21644
+ timestamp: exports_external.string().datetime({ offset: true }).optional()
21645
21645
  });
21646
21646
  var DEFAULT_CRITERIA = [
21647
21647
  "type_safe",
@@ -21659,7 +21659,7 @@ var WeightedEvaluationSchema = exports_external.object({
21659
21659
  criteria: exports_external.record(exports_external.string(), WeightedCriterionEvaluationSchema),
21660
21660
  overall_feedback: exports_external.string(),
21661
21661
  retry_suggestion: exports_external.string().nullable(),
21662
- timestamp: exports_external.string().optional(),
21662
+ timestamp: exports_external.string().datetime({ offset: true }).optional(),
21663
21663
  average_weight: exports_external.number().min(0).max(1).optional(),
21664
21664
  raw_score: exports_external.number().min(0).max(1).optional(),
21665
21665
  weighted_score: exports_external.number().min(0).max(1).optional()
@@ -21732,7 +21732,7 @@ var SwarmSpawnResultSchema = exports_external.object({
21732
21732
  coordinator_name: exports_external.string(),
21733
21733
  thread_id: exports_external.string(),
21734
21734
  agents: exports_external.array(SpawnedAgentSchema),
21735
- started_at: exports_external.string()
21735
+ started_at: exports_external.string().datetime({ offset: true })
21736
21736
  });
21737
21737
  var AgentProgressSchema = exports_external.object({
21738
21738
  bead_id: exports_external.string(),
@@ -21742,7 +21742,7 @@ var AgentProgressSchema = exports_external.object({
21742
21742
  message: exports_external.string().optional(),
21743
21743
  files_touched: exports_external.array(exports_external.string()).optional(),
21744
21744
  blockers: exports_external.array(exports_external.string()).optional(),
21745
- timestamp: exports_external.string()
21745
+ timestamp: exports_external.string().datetime({ offset: true })
21746
21746
  });
21747
21747
  var SwarmStatusSchema = exports_external.object({
21748
21748
  epic_id: exports_external.string(),
@@ -21752,7 +21752,7 @@ var SwarmStatusSchema = exports_external.object({
21752
21752
  failed: exports_external.number().int().min(0),
21753
21753
  blocked: exports_external.number().int().min(0),
21754
21754
  agents: exports_external.array(SpawnedAgentSchema),
21755
- last_update: exports_external.string()
21755
+ last_update: exports_external.string().datetime({ offset: true })
21756
21756
  });
21757
21757
  // src/beads.ts
21758
21758
  class BeadError extends Error {
@@ -21890,18 +21890,32 @@ var beads_create_epic = tool({
21890
21890
  };
21891
21891
  return JSON.stringify(result, null, 2);
21892
21892
  } catch (error45) {
21893
- const rollbackHint = created.map((b) => `bd close ${b.id} --reason "Rollback partial epic"`).join(`
21894
- `);
21895
- const result = {
21896
- success: false,
21897
- epic: created[0] || {},
21898
- subtasks: created.slice(1),
21899
- rollback_hint: rollbackHint
21900
- };
21901
- return JSON.stringify({
21902
- ...result,
21903
- error: error45 instanceof Error ? error45.message : String(error45)
21904
- }, null, 2);
21893
+ const rollbackCommands = [];
21894
+ for (const bead of created) {
21895
+ try {
21896
+ const closeCmd = [
21897
+ "bd",
21898
+ "close",
21899
+ bead.id,
21900
+ "--reason",
21901
+ "Rollback partial epic",
21902
+ "--json"
21903
+ ];
21904
+ await Bun.$`${closeCmd}`.quiet().nothrow();
21905
+ rollbackCommands.push(`bd close ${bead.id} --reason "Rollback partial epic"`);
21906
+ } catch (rollbackError) {
21907
+ console.error(`Failed to rollback bead ${bead.id}:`, rollbackError);
21908
+ }
21909
+ }
21910
+ const errorMsg = error45 instanceof Error ? error45.message : String(error45);
21911
+ const rollbackInfo = rollbackCommands.length > 0 ? `
21912
+
21913
+ Rolled back ${rollbackCommands.length} bead(s):
21914
+ ${rollbackCommands.join(`
21915
+ `)}` : `
21916
+
21917
+ No beads to rollback.`;
21918
+ throw new BeadError(`Epic creation failed: ${errorMsg}${rollbackInfo}`, "beads_create_epic", 1);
21905
21919
  }
21906
21920
  }
21907
21921
  });
@@ -22028,17 +22042,22 @@ var beads_sync = tool({
22028
22042
  },
22029
22043
  async execute(args, ctx) {
22030
22044
  const autoPull = args.auto_pull ?? true;
22045
+ const TIMEOUT_MS = 30000;
22046
+ const withTimeout = async (promise2, timeoutMs, operation) => {
22047
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new BeadError(`Operation timed out after ${timeoutMs}ms`, operation)), timeoutMs));
22048
+ return Promise.race([promise2, timeoutPromise]);
22049
+ };
22031
22050
  if (autoPull) {
22032
- const pullResult = await Bun.$`git pull --rebase`.quiet().nothrow();
22051
+ const pullResult = await withTimeout(Bun.$`git pull --rebase`.quiet().nothrow(), TIMEOUT_MS, "git pull --rebase");
22033
22052
  if (pullResult.exitCode !== 0) {
22034
22053
  throw new BeadError(`Failed to pull: ${pullResult.stderr.toString()}`, "git pull --rebase", pullResult.exitCode);
22035
22054
  }
22036
22055
  }
22037
- const syncResult = await Bun.$`bd sync`.quiet().nothrow();
22056
+ const syncResult = await withTimeout(Bun.$`bd sync`.quiet().nothrow(), TIMEOUT_MS, "bd sync");
22038
22057
  if (syncResult.exitCode !== 0) {
22039
22058
  throw new BeadError(`Failed to sync beads: ${syncResult.stderr.toString()}`, "bd sync", syncResult.exitCode);
22040
22059
  }
22041
- const pushResult = await Bun.$`git push`.quiet().nothrow();
22060
+ const pushResult = await withTimeout(Bun.$`git push`.quiet().nothrow(), TIMEOUT_MS, "git push");
22042
22061
  if (pushResult.exitCode !== 0) {
22043
22062
  throw new BeadError(`Failed to push: ${pushResult.stderr.toString()}`, "git push", pushResult.exitCode);
22044
22063
  }
@@ -22347,9 +22366,11 @@ function getLimitsForEndpoint(endpoint) {
22347
22366
  const upperEndpoint = endpoint.toUpperCase();
22348
22367
  const perMinuteEnv = process.env[`OPENCODE_RATE_LIMIT_${upperEndpoint}_PER_MIN`];
22349
22368
  const perHourEnv = process.env[`OPENCODE_RATE_LIMIT_${upperEndpoint}_PER_HOUR`];
22369
+ const parsedPerMinute = perMinuteEnv ? parseInt(perMinuteEnv, 10) : NaN;
22370
+ const parsedPerHour = perHourEnv ? parseInt(perHourEnv, 10) : NaN;
22350
22371
  return {
22351
- perMinute: perMinuteEnv ? parseInt(perMinuteEnv, 10) : defaults.perMinute,
22352
- perHour: perHourEnv ? parseInt(perHourEnv, 10) : defaults.perHour
22372
+ perMinute: Number.isNaN(parsedPerMinute) ? defaults.perMinute : parsedPerMinute,
22373
+ perHour: Number.isNaN(parsedPerHour) ? defaults.perHour : parsedPerHour
22353
22374
  };
22354
22375
  }
22355
22376
 
@@ -22388,6 +22409,7 @@ class RedisRateLimiter {
22388
22409
  const pipeline = this.redis.pipeline();
22389
22410
  pipeline.zremrangebyscore(key, 0, windowStart);
22390
22411
  pipeline.zcard(key);
22412
+ pipeline.zrange(key, 0, 0, "WITHSCORES");
22391
22413
  const results = await pipeline.exec();
22392
22414
  if (!results) {
22393
22415
  return { allowed: true, remaining: limit, resetAt: now + windowDuration };
@@ -22397,7 +22419,7 @@ class RedisRateLimiter {
22397
22419
  const allowed = count < limit;
22398
22420
  let resetAt = now + windowDuration;
22399
22421
  if (!allowed) {
22400
- const oldest = await this.redis.zrange(key, 0, 0, "WITHSCORES");
22422
+ const oldest = results[2]?.[1] || [];
22401
22423
  if (oldest.length >= 2) {
22402
22424
  const oldestTimestamp = parseInt(oldest[1], 10);
22403
22425
  resetAt = oldestTimestamp + windowDuration;
@@ -22638,8 +22660,11 @@ function saveSessionState(sessionID, state) {
22638
22660
  }
22639
22661
  const path = getSessionStatePath(sessionID);
22640
22662
  writeFileSync(path, JSON.stringify(state, null, 2));
22663
+ return true;
22641
22664
  } catch (error45) {
22642
- console.warn(`[agent-mail] Could not save session state: ${error45}`);
22665
+ console.error(`[agent-mail] CRITICAL: Could not save session state: ${error45}`);
22666
+ console.error(`[agent-mail] Session state will not persist across CLI invocations!`);
22667
+ return false;
22643
22668
  }
22644
22669
  }
22645
22670
  var sessionStates = new Map;
@@ -22919,6 +22944,7 @@ async function mcpCall(toolName, args) {
22919
22944
  } catch (error45) {
22920
22945
  lastError = error45 instanceof Error ? error45 : new Error(String(error45));
22921
22946
  consecutiveFailures++;
22947
+ const retryable = isRetryableError(error45);
22922
22948
  if (consecutiveFailures >= RECOVERY_CONFIG.failureThreshold && RECOVERY_CONFIG.enabled) {
22923
22949
  console.warn(`[agent-mail] ${consecutiveFailures} consecutive failures, checking server health...`);
22924
22950
  const healthy = await isServerFunctional();
@@ -22927,12 +22953,14 @@ async function mcpCall(toolName, args) {
22927
22953
  const restarted = await restartServer();
22928
22954
  if (restarted) {
22929
22955
  agentMailAvailable = null;
22930
- attempt--;
22931
- continue;
22956
+ if (retryable) {
22957
+ attempt--;
22958
+ continue;
22959
+ }
22932
22960
  }
22933
22961
  }
22934
22962
  }
22935
- if (!isRetryableError(error45)) {
22963
+ if (!retryable) {
22936
22964
  console.warn(`[agent-mail] Non-retryable error for ${toolName}: ${lastError.message}`);
22937
22965
  throw lastError;
22938
22966
  }
@@ -23065,12 +23093,12 @@ var agentmail_read_message = tool({
23065
23093
  const messages = await mcpCall("fetch_inbox", {
23066
23094
  project_key: state.projectKey,
23067
23095
  agent_name: state.agentName,
23068
- limit: 1,
23096
+ limit: 50,
23069
23097
  include_bodies: true
23070
23098
  });
23071
23099
  const message = messages.find((m) => m.id === args.message_id);
23072
23100
  if (!message) {
23073
- return `Message ${args.message_id} not found`;
23101
+ return `Message ${args.message_id} not found in recent 50 messages. Try using agentmail_search to locate it.`;
23074
23102
  }
23075
23103
  await recordRateLimitedRequest(state.agentName, "read_message");
23076
23104
  return JSON.stringify(message, null, 2);
@@ -24567,6 +24595,13 @@ var swarm_validate_decomposition = tool({
24567
24595
  for (let i = 0;i < validated.subtasks.length; i++) {
24568
24596
  const deps = validated.subtasks[i].dependencies;
24569
24597
  for (const dep of deps) {
24598
+ if (dep < 0 || dep >= validated.subtasks.length) {
24599
+ return JSON.stringify({
24600
+ valid: false,
24601
+ error: `Invalid dependency: subtask ${i} depends on ${dep}, but only ${validated.subtasks.length} subtasks exist (indices 0-${validated.subtasks.length - 1})`,
24602
+ hint: "Dependency index is out of bounds"
24603
+ }, null, 2);
24604
+ }
24570
24605
  if (dep >= i) {
24571
24606
  return JSON.stringify({
24572
24607
  valid: false,
@@ -25232,16 +25267,29 @@ async function resolveSemanticMemoryCommand() {
25232
25267
  return cachedCommand;
25233
25268
  }
25234
25269
  async function execSemanticMemory(args) {
25235
- const cmd = await resolveSemanticMemoryCommand();
25236
- const fullCmd = [...cmd, ...args];
25237
- const proc = Bun.spawn(fullCmd, {
25238
- stdout: "pipe",
25239
- stderr: "pipe"
25240
- });
25241
- const stdout = Buffer.from(await new Response(proc.stdout).arrayBuffer());
25242
- const stderr = Buffer.from(await new Response(proc.stderr).arrayBuffer());
25243
- const exitCode = await proc.exited;
25244
- return { exitCode, stdout, stderr };
25270
+ try {
25271
+ const cmd = await resolveSemanticMemoryCommand();
25272
+ const fullCmd = [...cmd, ...args];
25273
+ const proc = Bun.spawn(fullCmd, {
25274
+ stdout: "pipe",
25275
+ stderr: "pipe"
25276
+ });
25277
+ try {
25278
+ const stdout = Buffer.from(await new Response(proc.stdout).arrayBuffer());
25279
+ const stderr = Buffer.from(await new Response(proc.stderr).arrayBuffer());
25280
+ const exitCode = await proc.exited;
25281
+ return { exitCode, stdout, stderr };
25282
+ } finally {
25283
+ proc.kill();
25284
+ }
25285
+ } catch (error45) {
25286
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
25287
+ return {
25288
+ exitCode: 1,
25289
+ stdout: Buffer.from(""),
25290
+ stderr: Buffer.from(`Error executing semantic-memory: ${errorMessage}`)
25291
+ };
25292
+ }
25245
25293
  }
25246
25294
  var DEFAULT_STORAGE_CONFIG = {
25247
25295
  backend: "semantic-memory",
@@ -25503,20 +25551,26 @@ async function createStorageWithFallback(config2 = {}) {
25503
25551
  return new InMemoryStorage;
25504
25552
  }
25505
25553
  var globalStorage = null;
25554
+ var globalStoragePromise = null;
25506
25555
  async function getStorage() {
25507
- if (!globalStorage) {
25508
- globalStorage = await createStorageWithFallback();
25556
+ if (!globalStoragePromise) {
25557
+ globalStoragePromise = createStorageWithFallback().then((storage) => {
25558
+ globalStorage = storage;
25559
+ return storage;
25560
+ });
25509
25561
  }
25510
- return globalStorage;
25562
+ return globalStoragePromise;
25511
25563
  }
25512
25564
  function setStorage(storage) {
25513
25565
  globalStorage = storage;
25566
+ globalStoragePromise = Promise.resolve(storage);
25514
25567
  }
25515
25568
  async function resetStorage() {
25516
25569
  if (globalStorage) {
25517
25570
  await globalStorage.close();
25518
25571
  globalStorage = null;
25519
25572
  }
25573
+ globalStoragePromise = null;
25520
25574
  }
25521
25575
 
25522
25576
  // src/index.ts
package/dist/plugin.js CHANGED
@@ -21560,15 +21560,15 @@ var BeadDependencySchema = exports_external.object({
21560
21560
  type: exports_external.enum(["blocks", "blocked-by", "related", "discovered-from"])
21561
21561
  });
21562
21562
  var BeadSchema = exports_external.object({
21563
- id: exports_external.string().regex(/^[a-z0-9-]+-[a-z0-9]+(\.\d+)?$/, "Invalid bead ID format"),
21563
+ id: exports_external.string().regex(/^[a-z0-9]+(-[a-z0-9]+)+(\.\d+)?$/, "Invalid bead ID format"),
21564
21564
  title: exports_external.string().min(1, "Title required"),
21565
21565
  description: exports_external.string().optional().default(""),
21566
21566
  status: BeadStatusSchema.default("open"),
21567
21567
  priority: exports_external.number().int().min(0).max(3).default(2),
21568
21568
  issue_type: BeadTypeSchema.default("task"),
21569
- created_at: exports_external.string(),
21570
- updated_at: exports_external.string().optional(),
21571
- closed_at: exports_external.string().optional(),
21569
+ created_at: exports_external.string().datetime({ offset: true }),
21570
+ updated_at: exports_external.string().datetime({ offset: true }).optional(),
21571
+ closed_at: exports_external.string().datetime({ offset: true }).optional(),
21572
21572
  parent_id: exports_external.string().optional(),
21573
21573
  dependencies: exports_external.array(BeadDependencySchema).optional().default([]),
21574
21574
  metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
@@ -21641,7 +21641,7 @@ var EvaluationSchema = exports_external.object({
21641
21641
  criteria: exports_external.record(exports_external.string(), CriterionEvaluationSchema),
21642
21642
  overall_feedback: exports_external.string(),
21643
21643
  retry_suggestion: exports_external.string().nullable(),
21644
- timestamp: exports_external.string().optional()
21644
+ timestamp: exports_external.string().datetime({ offset: true }).optional()
21645
21645
  });
21646
21646
  var DEFAULT_CRITERIA = [
21647
21647
  "type_safe",
@@ -21659,7 +21659,7 @@ var WeightedEvaluationSchema = exports_external.object({
21659
21659
  criteria: exports_external.record(exports_external.string(), WeightedCriterionEvaluationSchema),
21660
21660
  overall_feedback: exports_external.string(),
21661
21661
  retry_suggestion: exports_external.string().nullable(),
21662
- timestamp: exports_external.string().optional(),
21662
+ timestamp: exports_external.string().datetime({ offset: true }).optional(),
21663
21663
  average_weight: exports_external.number().min(0).max(1).optional(),
21664
21664
  raw_score: exports_external.number().min(0).max(1).optional(),
21665
21665
  weighted_score: exports_external.number().min(0).max(1).optional()
@@ -21732,7 +21732,7 @@ var SwarmSpawnResultSchema = exports_external.object({
21732
21732
  coordinator_name: exports_external.string(),
21733
21733
  thread_id: exports_external.string(),
21734
21734
  agents: exports_external.array(SpawnedAgentSchema),
21735
- started_at: exports_external.string()
21735
+ started_at: exports_external.string().datetime({ offset: true })
21736
21736
  });
21737
21737
  var AgentProgressSchema = exports_external.object({
21738
21738
  bead_id: exports_external.string(),
@@ -21742,7 +21742,7 @@ var AgentProgressSchema = exports_external.object({
21742
21742
  message: exports_external.string().optional(),
21743
21743
  files_touched: exports_external.array(exports_external.string()).optional(),
21744
21744
  blockers: exports_external.array(exports_external.string()).optional(),
21745
- timestamp: exports_external.string()
21745
+ timestamp: exports_external.string().datetime({ offset: true })
21746
21746
  });
21747
21747
  var SwarmStatusSchema = exports_external.object({
21748
21748
  epic_id: exports_external.string(),
@@ -21752,7 +21752,7 @@ var SwarmStatusSchema = exports_external.object({
21752
21752
  failed: exports_external.number().int().min(0),
21753
21753
  blocked: exports_external.number().int().min(0),
21754
21754
  agents: exports_external.array(SpawnedAgentSchema),
21755
- last_update: exports_external.string()
21755
+ last_update: exports_external.string().datetime({ offset: true })
21756
21756
  });
21757
21757
  // src/beads.ts
21758
21758
  class BeadError extends Error {
@@ -21890,18 +21890,32 @@ var beads_create_epic = tool({
21890
21890
  };
21891
21891
  return JSON.stringify(result, null, 2);
21892
21892
  } catch (error45) {
21893
- const rollbackHint = created.map((b) => `bd close ${b.id} --reason "Rollback partial epic"`).join(`
21894
- `);
21895
- const result = {
21896
- success: false,
21897
- epic: created[0] || {},
21898
- subtasks: created.slice(1),
21899
- rollback_hint: rollbackHint
21900
- };
21901
- return JSON.stringify({
21902
- ...result,
21903
- error: error45 instanceof Error ? error45.message : String(error45)
21904
- }, null, 2);
21893
+ const rollbackCommands = [];
21894
+ for (const bead of created) {
21895
+ try {
21896
+ const closeCmd = [
21897
+ "bd",
21898
+ "close",
21899
+ bead.id,
21900
+ "--reason",
21901
+ "Rollback partial epic",
21902
+ "--json"
21903
+ ];
21904
+ await Bun.$`${closeCmd}`.quiet().nothrow();
21905
+ rollbackCommands.push(`bd close ${bead.id} --reason "Rollback partial epic"`);
21906
+ } catch (rollbackError) {
21907
+ console.error(`Failed to rollback bead ${bead.id}:`, rollbackError);
21908
+ }
21909
+ }
21910
+ const errorMsg = error45 instanceof Error ? error45.message : String(error45);
21911
+ const rollbackInfo = rollbackCommands.length > 0 ? `
21912
+
21913
+ Rolled back ${rollbackCommands.length} bead(s):
21914
+ ${rollbackCommands.join(`
21915
+ `)}` : `
21916
+
21917
+ No beads to rollback.`;
21918
+ throw new BeadError(`Epic creation failed: ${errorMsg}${rollbackInfo}`, "beads_create_epic", 1);
21905
21919
  }
21906
21920
  }
21907
21921
  });
@@ -22028,17 +22042,22 @@ var beads_sync = tool({
22028
22042
  },
22029
22043
  async execute(args, ctx) {
22030
22044
  const autoPull = args.auto_pull ?? true;
22045
+ const TIMEOUT_MS = 30000;
22046
+ const withTimeout = async (promise2, timeoutMs, operation) => {
22047
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new BeadError(`Operation timed out after ${timeoutMs}ms`, operation)), timeoutMs));
22048
+ return Promise.race([promise2, timeoutPromise]);
22049
+ };
22031
22050
  if (autoPull) {
22032
- const pullResult = await Bun.$`git pull --rebase`.quiet().nothrow();
22051
+ const pullResult = await withTimeout(Bun.$`git pull --rebase`.quiet().nothrow(), TIMEOUT_MS, "git pull --rebase");
22033
22052
  if (pullResult.exitCode !== 0) {
22034
22053
  throw new BeadError(`Failed to pull: ${pullResult.stderr.toString()}`, "git pull --rebase", pullResult.exitCode);
22035
22054
  }
22036
22055
  }
22037
- const syncResult = await Bun.$`bd sync`.quiet().nothrow();
22056
+ const syncResult = await withTimeout(Bun.$`bd sync`.quiet().nothrow(), TIMEOUT_MS, "bd sync");
22038
22057
  if (syncResult.exitCode !== 0) {
22039
22058
  throw new BeadError(`Failed to sync beads: ${syncResult.stderr.toString()}`, "bd sync", syncResult.exitCode);
22040
22059
  }
22041
- const pushResult = await Bun.$`git push`.quiet().nothrow();
22060
+ const pushResult = await withTimeout(Bun.$`git push`.quiet().nothrow(), TIMEOUT_MS, "git push");
22042
22061
  if (pushResult.exitCode !== 0) {
22043
22062
  throw new BeadError(`Failed to push: ${pushResult.stderr.toString()}`, "git push", pushResult.exitCode);
22044
22063
  }
@@ -22321,9 +22340,11 @@ function getLimitsForEndpoint(endpoint) {
22321
22340
  const upperEndpoint = endpoint.toUpperCase();
22322
22341
  const perMinuteEnv = process.env[`OPENCODE_RATE_LIMIT_${upperEndpoint}_PER_MIN`];
22323
22342
  const perHourEnv = process.env[`OPENCODE_RATE_LIMIT_${upperEndpoint}_PER_HOUR`];
22343
+ const parsedPerMinute = perMinuteEnv ? parseInt(perMinuteEnv, 10) : NaN;
22344
+ const parsedPerHour = perHourEnv ? parseInt(perHourEnv, 10) : NaN;
22324
22345
  return {
22325
- perMinute: perMinuteEnv ? parseInt(perMinuteEnv, 10) : defaults.perMinute,
22326
- perHour: perHourEnv ? parseInt(perHourEnv, 10) : defaults.perHour
22346
+ perMinute: Number.isNaN(parsedPerMinute) ? defaults.perMinute : parsedPerMinute,
22347
+ perHour: Number.isNaN(parsedPerHour) ? defaults.perHour : parsedPerHour
22327
22348
  };
22328
22349
  }
22329
22350
 
@@ -22362,6 +22383,7 @@ class RedisRateLimiter {
22362
22383
  const pipeline = this.redis.pipeline();
22363
22384
  pipeline.zremrangebyscore(key, 0, windowStart);
22364
22385
  pipeline.zcard(key);
22386
+ pipeline.zrange(key, 0, 0, "WITHSCORES");
22365
22387
  const results = await pipeline.exec();
22366
22388
  if (!results) {
22367
22389
  return { allowed: true, remaining: limit, resetAt: now + windowDuration };
@@ -22371,7 +22393,7 @@ class RedisRateLimiter {
22371
22393
  const allowed = count < limit;
22372
22394
  let resetAt = now + windowDuration;
22373
22395
  if (!allowed) {
22374
- const oldest = await this.redis.zrange(key, 0, 0, "WITHSCORES");
22396
+ const oldest = results[2]?.[1] || [];
22375
22397
  if (oldest.length >= 2) {
22376
22398
  const oldestTimestamp = parseInt(oldest[1], 10);
22377
22399
  resetAt = oldestTimestamp + windowDuration;
@@ -22612,8 +22634,11 @@ function saveSessionState(sessionID, state) {
22612
22634
  }
22613
22635
  const path = getSessionStatePath(sessionID);
22614
22636
  writeFileSync(path, JSON.stringify(state, null, 2));
22637
+ return true;
22615
22638
  } catch (error45) {
22616
- console.warn(`[agent-mail] Could not save session state: ${error45}`);
22639
+ console.error(`[agent-mail] CRITICAL: Could not save session state: ${error45}`);
22640
+ console.error(`[agent-mail] Session state will not persist across CLI invocations!`);
22641
+ return false;
22617
22642
  }
22618
22643
  }
22619
22644
  var sessionStates = new Map;
@@ -22893,6 +22918,7 @@ async function mcpCall(toolName, args) {
22893
22918
  } catch (error45) {
22894
22919
  lastError = error45 instanceof Error ? error45 : new Error(String(error45));
22895
22920
  consecutiveFailures++;
22921
+ const retryable = isRetryableError(error45);
22896
22922
  if (consecutiveFailures >= RECOVERY_CONFIG.failureThreshold && RECOVERY_CONFIG.enabled) {
22897
22923
  console.warn(`[agent-mail] ${consecutiveFailures} consecutive failures, checking server health...`);
22898
22924
  const healthy = await isServerFunctional();
@@ -22901,12 +22927,14 @@ async function mcpCall(toolName, args) {
22901
22927
  const restarted = await restartServer();
22902
22928
  if (restarted) {
22903
22929
  agentMailAvailable = null;
22904
- attempt--;
22905
- continue;
22930
+ if (retryable) {
22931
+ attempt--;
22932
+ continue;
22933
+ }
22906
22934
  }
22907
22935
  }
22908
22936
  }
22909
- if (!isRetryableError(error45)) {
22937
+ if (!retryable) {
22910
22938
  console.warn(`[agent-mail] Non-retryable error for ${toolName}: ${lastError.message}`);
22911
22939
  throw lastError;
22912
22940
  }
@@ -23039,12 +23067,12 @@ var agentmail_read_message = tool({
23039
23067
  const messages = await mcpCall("fetch_inbox", {
23040
23068
  project_key: state.projectKey,
23041
23069
  agent_name: state.agentName,
23042
- limit: 1,
23070
+ limit: 50,
23043
23071
  include_bodies: true
23044
23072
  });
23045
23073
  const message = messages.find((m) => m.id === args.message_id);
23046
23074
  if (!message) {
23047
- return `Message ${args.message_id} not found`;
23075
+ return `Message ${args.message_id} not found in recent 50 messages. Try using agentmail_search to locate it.`;
23048
23076
  }
23049
23077
  await recordRateLimitedRequest(state.agentName, "read_message");
23050
23078
  return JSON.stringify(message, null, 2);
@@ -24518,6 +24546,13 @@ var swarm_validate_decomposition = tool({
24518
24546
  for (let i = 0;i < validated.subtasks.length; i++) {
24519
24547
  const deps = validated.subtasks[i].dependencies;
24520
24548
  for (const dep of deps) {
24549
+ if (dep < 0 || dep >= validated.subtasks.length) {
24550
+ return JSON.stringify({
24551
+ valid: false,
24552
+ error: `Invalid dependency: subtask ${i} depends on ${dep}, but only ${validated.subtasks.length} subtasks exist (indices 0-${validated.subtasks.length - 1})`,
24553
+ hint: "Dependency index is out of bounds"
24554
+ }, null, 2);
24555
+ }
24521
24556
  if (dep >= i) {
24522
24557
  return JSON.stringify({
24523
24558
  valid: false,