claude-threads 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11880,7 +11880,7 @@ import * as fs from "fs/promises";
11880
11880
  import { homedir as homedir4 } from "os";
11881
11881
  async function execGit(args, cwd) {
11882
11882
  const cmd = `git ${args.join(" ")}`;
11883
- log7.debug(`Executing: ${cmd}`);
11883
+ log8.debug(`Executing: ${cmd}`);
11884
11884
  return new Promise((resolve3, reject) => {
11885
11885
  const proc = crossSpawn("git", args, { cwd });
11886
11886
  let stdout = "";
@@ -11893,15 +11893,15 @@ async function execGit(args, cwd) {
11893
11893
  });
11894
11894
  proc.on("close", (code) => {
11895
11895
  if (code === 0) {
11896
- log7.debug(`${cmd} → success`);
11896
+ log8.debug(`${cmd} → success`);
11897
11897
  resolve3(stdout.trim());
11898
11898
  } else {
11899
- log7.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
11899
+ log8.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
11900
11900
  reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
11901
11901
  }
11902
11902
  });
11903
11903
  proc.on("error", (err) => {
11904
- log7.warn(`${cmd} → error: ${err}`);
11904
+ log8.warn(`${cmd} → error: ${err}`);
11905
11905
  reject(err);
11906
11906
  });
11907
11907
  });
@@ -11911,7 +11911,7 @@ async function isGitRepository(dir) {
11911
11911
  await execGit(["rev-parse", "--git-dir"], dir);
11912
11912
  return true;
11913
11913
  } catch (err) {
11914
- log7.debug(`Not a git repository: ${dir} (${err})`);
11914
+ log8.debug(`Not a git repository: ${dir} (${err})`);
11915
11915
  return false;
11916
11916
  }
11917
11917
  }
@@ -12046,7 +12046,7 @@ async function detectWorktreeInfo(workingDir) {
12046
12046
  const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
12047
12047
  const branch = branchOutput?.trim();
12048
12048
  if (!branch) {
12049
- log7.debug(`Could not detect branch for worktree at ${workingDir}`);
12049
+ log8.debug(`Could not detect branch for worktree at ${workingDir}`);
12050
12050
  return null;
12051
12051
  }
12052
12052
  const gitDirOutput = await execGit(["rev-parse", "--git-common-dir"], workingDir);
@@ -12058,45 +12058,45 @@ async function detectWorktreeInfo(workingDir) {
12058
12058
  repoRoot = repoRoot.slice(0, -4);
12059
12059
  }
12060
12060
  }
12061
- log7.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
12061
+ log8.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
12062
12062
  return {
12063
12063
  worktreePath: workingDir,
12064
12064
  branch,
12065
12065
  repoRoot: repoRoot || workingDir
12066
12066
  };
12067
12067
  } catch (err) {
12068
- log7.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
12068
+ log8.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
12069
12069
  return null;
12070
12070
  }
12071
12071
  }
12072
12072
  async function createWorktree(repoRoot, branch, targetDir) {
12073
- log7.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
12073
+ log8.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
12074
12074
  const parentDir = path.dirname(targetDir);
12075
- log7.debug(`Creating parent directory: ${parentDir}`);
12075
+ log8.debug(`Creating parent directory: ${parentDir}`);
12076
12076
  await fs.mkdir(parentDir, { recursive: true });
12077
12077
  const exists = await branchExists(repoRoot, branch);
12078
12078
  if (exists) {
12079
- log7.debug(`Branch '${branch}' exists, adding worktree`);
12079
+ log8.debug(`Branch '${branch}' exists, adding worktree`);
12080
12080
  await execGit(["worktree", "add", targetDir, branch], repoRoot);
12081
12081
  } else {
12082
- log7.debug(`Branch '${branch}' does not exist, creating with worktree`);
12082
+ log8.debug(`Branch '${branch}' does not exist, creating with worktree`);
12083
12083
  await execGit(["worktree", "add", "-b", branch, targetDir], repoRoot);
12084
12084
  }
12085
- log7.info(`Worktree created successfully: ${targetDir}`);
12085
+ log8.info(`Worktree created successfully: ${targetDir}`);
12086
12086
  return targetDir;
12087
12087
  }
12088
12088
  async function removeWorktree(repoRoot, worktreePath) {
12089
- log7.info(`Removing worktree: ${worktreePath}`);
12089
+ log8.info(`Removing worktree: ${worktreePath}`);
12090
12090
  try {
12091
12091
  await execGit(["worktree", "remove", worktreePath], repoRoot);
12092
- log7.debug("Worktree removed cleanly");
12092
+ log8.debug("Worktree removed cleanly");
12093
12093
  } catch (err) {
12094
- log7.debug(`Clean remove failed (${err}), trying force remove`);
12094
+ log8.debug(`Clean remove failed (${err}), trying force remove`);
12095
12095
  await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
12096
12096
  }
12097
- log7.debug("Pruning stale worktree references");
12097
+ log8.debug("Pruning stale worktree references");
12098
12098
  await execGit(["worktree", "prune"], repoRoot);
12099
- log7.info("Worktree removed and pruned successfully");
12099
+ log8.info("Worktree removed and pruned successfully");
12100
12100
  }
12101
12101
  async function findWorktreeByBranch(repoRoot, branch) {
12102
12102
  const worktrees = await listWorktrees(repoRoot);
@@ -12137,14 +12137,14 @@ async function writeMetadataStore(store) {
12137
12137
  await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
12138
12138
  await fs.chmod(METADATA_STORE_PATH, 384);
12139
12139
  } catch (err) {
12140
- log7.warn(`Failed to write worktree metadata store: ${err}`);
12140
+ log8.warn(`Failed to write worktree metadata store: ${err}`);
12141
12141
  }
12142
12142
  }
12143
12143
  async function writeWorktreeMetadata(worktreePath, metadata) {
12144
12144
  const store = await readMetadataStore();
12145
12145
  store[worktreePath] = metadata;
12146
12146
  await writeMetadataStore(store);
12147
- log7.debug(`Wrote worktree metadata for: ${worktreePath}`);
12147
+ log8.debug(`Wrote worktree metadata for: ${worktreePath}`);
12148
12148
  }
12149
12149
  async function readWorktreeMetadata(worktreePath) {
12150
12150
  const store = await readMetadataStore();
@@ -12167,14 +12167,14 @@ async function removeWorktreeMetadata(worktreePath) {
12167
12167
  if (store[worktreePath]) {
12168
12168
  delete store[worktreePath];
12169
12169
  await writeMetadataStore(store);
12170
- log7.debug(`Removed worktree metadata for: ${worktreePath}`);
12170
+ log8.debug(`Removed worktree metadata for: ${worktreePath}`);
12171
12171
  }
12172
12172
  }
12173
- var log7, WORKTREES_DIR, METADATA_STORE_PATH;
12173
+ var log8, WORKTREES_DIR, METADATA_STORE_PATH;
12174
12174
  var init_worktree = __esm(() => {
12175
12175
  init_spawn();
12176
12176
  init_logger();
12177
- log7 = createLogger("git-wt");
12177
+ log8 = createLogger("git-wt");
12178
12178
  WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
12179
12179
  METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
12180
12180
  });
@@ -19514,7 +19514,7 @@ var require_react_reconciler_development = __commonJS((exports, module) => {
19514
19514
  return hook.checkDCE ? true : false;
19515
19515
  }
19516
19516
  function setIsStrictModeForDevtools(newIsStrictMode) {
19517
- typeof log30 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
19517
+ typeof log31 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
19518
19518
  if (injectedHook && typeof injectedHook.setStrictMode === "function")
19519
19519
  try {
19520
19520
  injectedHook.setStrictMode(rendererID, newIsStrictMode);
@@ -27598,7 +27598,7 @@ Check the render method of %s.`, getComponentNameFromFiber(current) || "Unknown"
27598
27598
  var fiberStack = [];
27599
27599
  var index$jscomp$0 = -1, emptyContextObject = {};
27600
27600
  Object.freeze(emptyContextObject);
27601
- var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority, log30 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
27601
+ var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority, log31 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
27602
27602
  if (typeof performance === "object" && typeof performance.now === "function") {
27603
27603
  var localPerformance = performance;
27604
27604
  var getCurrentTime = function() {
@@ -53546,6 +53546,99 @@ class SessionStore {
53546
53546
  chmodSync2(this.sessionsFile, 384);
53547
53547
  }
53548
53548
  }
53549
+ // src/claude/account-pool.ts
53550
+ init_logger();
53551
+ var log6 = createLogger("account-pool");
53552
+
53553
+ class AccountPool {
53554
+ accounts;
53555
+ byId;
53556
+ activeCounts = new Map;
53557
+ coolingUntil = new Map;
53558
+ roundRobinIndex = 0;
53559
+ constructor(accounts) {
53560
+ this.accounts = (accounts ?? []).filter((acc) => {
53561
+ const hasAuth = !!acc.home || !!acc.apiKey;
53562
+ if (!hasAuth) {
53563
+ log6.warn(`Claude account ${acc.id} has neither home nor apiKey — ignoring`);
53564
+ }
53565
+ return hasAuth;
53566
+ });
53567
+ this.byId = new Map(this.accounts.map((acc) => [acc.id, acc]));
53568
+ for (const acc of this.accounts) {
53569
+ this.activeCounts.set(acc.id, 0);
53570
+ }
53571
+ }
53572
+ get isEmpty() {
53573
+ return this.accounts.length === 0;
53574
+ }
53575
+ get size() {
53576
+ return this.accounts.length;
53577
+ }
53578
+ acquire(preferredId) {
53579
+ if (this.isEmpty)
53580
+ return null;
53581
+ if (preferredId) {
53582
+ const preferred = this.byId.get(preferredId);
53583
+ if (preferred) {
53584
+ this.incrementActive(preferred.id);
53585
+ return preferred;
53586
+ }
53587
+ log6.warn(`Preferred account "${preferredId}" not in pool — falling back to round-robin`);
53588
+ }
53589
+ const now = Date.now();
53590
+ const n = this.accounts.length;
53591
+ for (let i2 = 0;i2 < n; i2++) {
53592
+ const idx = (this.roundRobinIndex + i2) % n;
53593
+ const candidate = this.accounts[idx];
53594
+ const cooling = this.coolingUntil.get(candidate.id) ?? 0;
53595
+ if (cooling <= now) {
53596
+ this.roundRobinIndex = (idx + 1) % n;
53597
+ this.incrementActive(candidate.id);
53598
+ return candidate;
53599
+ }
53600
+ }
53601
+ log6.warn(`All ${n} accounts are in rate-limit cooldown`);
53602
+ return null;
53603
+ }
53604
+ release(accountId) {
53605
+ const current = this.activeCounts.get(accountId);
53606
+ if (current === undefined)
53607
+ return;
53608
+ this.activeCounts.set(accountId, Math.max(0, current - 1));
53609
+ }
53610
+ markCooling(accountId, untilEpochMs) {
53611
+ if (!this.byId.has(accountId)) {
53612
+ log6.warn(`markCooling called for unknown account "${accountId}"`);
53613
+ return;
53614
+ }
53615
+ const existing = this.coolingUntil.get(accountId) ?? 0;
53616
+ if (untilEpochMs > existing) {
53617
+ this.coolingUntil.set(accountId, untilEpochMs);
53618
+ const minutes = Math.ceil((untilEpochMs - Date.now()) / 60000);
53619
+ log6.info(`Account "${accountId}" cooling for ~${minutes}min`);
53620
+ }
53621
+ }
53622
+ get(accountId) {
53623
+ return this.byId.get(accountId);
53624
+ }
53625
+ status() {
53626
+ const now = Date.now();
53627
+ return this.accounts.map((acc) => {
53628
+ const cooling = this.coolingUntil.get(acc.id) ?? 0;
53629
+ return {
53630
+ id: acc.id,
53631
+ displayName: acc.displayName ?? acc.id,
53632
+ activeSessions: this.activeCounts.get(acc.id) ?? 0,
53633
+ coolingUntil: cooling > now ? cooling : null
53634
+ };
53635
+ });
53636
+ }
53637
+ incrementActive(accountId) {
53638
+ this.activeCounts.set(accountId, (this.activeCounts.get(accountId) ?? 0) + 1);
53639
+ }
53640
+ }
53641
+
53549
53642
  // src/session/manager.ts
53550
53643
  init_emoji();
53551
53644
 
@@ -53560,7 +53653,7 @@ init_logger();
53560
53653
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync, readFileSync as readFileSync5, chmodSync as chmodSync3 } from "fs";
53561
53654
  import { homedir as homedir3 } from "os";
53562
53655
  import { join as join4, dirname as dirname4 } from "path";
53563
- var log6 = createLogger("thread-log");
53656
+ var log7 = createLogger("thread-log");
53564
53657
  var LOGS_BASE_DIR = join4(homedir3(), ".claude-threads", "logs");
53565
53658
 
53566
53659
  class ThreadLoggerImpl {
@@ -53590,7 +53683,7 @@ class ThreadLoggerImpl {
53590
53683
  this.flushTimer = setInterval(() => {
53591
53684
  this.flushSync();
53592
53685
  }, this.flushIntervalMs);
53593
- log6.debug(`Thread logger initialized: ${this.logPath}`);
53686
+ log7.debug(`Thread logger initialized: ${this.logPath}`);
53594
53687
  }
53595
53688
  }
53596
53689
  isEnabled() {
@@ -53704,7 +53797,7 @@ class ThreadLoggerImpl {
53704
53797
  this.flushTimer = null;
53705
53798
  }
53706
53799
  this.flushSync();
53707
- log6.debug(`Thread logger closed: ${this.logPath}`);
53800
+ log7.debug(`Thread logger closed: ${this.logPath}`);
53708
53801
  }
53709
53802
  addEntry(entry) {
53710
53803
  this.buffer.push(entry);
@@ -53726,7 +53819,7 @@ class ThreadLoggerImpl {
53726
53819
  }
53727
53820
  this.buffer = [];
53728
53821
  } catch (err) {
53729
- log6.error(`Failed to flush thread log: ${err}`);
53822
+ log7.error(`Failed to flush thread log: ${err}`);
53730
53823
  }
53731
53824
  }
53732
53825
  }
@@ -53777,25 +53870,25 @@ function cleanupOldLogs(retentionDays = 30) {
53777
53870
  if (fileStat.mtimeMs < cutoffMs) {
53778
53871
  unlinkSync(filePath);
53779
53872
  deletedCount++;
53780
- log6.debug(`Deleted old log file: ${filePath}`);
53873
+ log7.debug(`Deleted old log file: ${filePath}`);
53781
53874
  }
53782
53875
  } catch (err) {
53783
- log6.warn(`Failed to check/delete log file ${filePath}: ${err}`);
53876
+ log7.warn(`Failed to check/delete log file ${filePath}: ${err}`);
53784
53877
  }
53785
53878
  }
53786
53879
  try {
53787
53880
  const remaining = readdirSync(platformDir);
53788
53881
  if (remaining.length === 0) {
53789
53882
  rmdirSync(platformDir);
53790
- log6.debug(`Removed empty platform log directory: ${platformDir}`);
53883
+ log7.debug(`Removed empty platform log directory: ${platformDir}`);
53791
53884
  }
53792
53885
  } catch {}
53793
53886
  }
53794
53887
  if (deletedCount > 0) {
53795
- log6.info(`Cleaned up ${deletedCount} old log file(s)`);
53888
+ log7.info(`Cleaned up ${deletedCount} old log file(s)`);
53796
53889
  }
53797
53890
  } catch (err) {
53798
- log6.error(`Failed to clean up old logs: ${err}`);
53891
+ log7.error(`Failed to clean up old logs: ${err}`);
53799
53892
  }
53800
53893
  return deletedCount;
53801
53894
  }
@@ -53804,16 +53897,16 @@ function getLogFilePath(platformId, sessionId) {
53804
53897
  }
53805
53898
  function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
53806
53899
  const logPath = getLogFilePath(platformId, sessionId);
53807
- log6.debug(`Reading log entries from: ${logPath}`);
53900
+ log7.debug(`Reading log entries from: ${logPath}`);
53808
53901
  if (!existsSync6(logPath)) {
53809
- log6.debug(`Log file does not exist: ${logPath}`);
53902
+ log7.debug(`Log file does not exist: ${logPath}`);
53810
53903
  return [];
53811
53904
  }
53812
53905
  try {
53813
53906
  const content = readFileSync5(logPath, "utf8");
53814
53907
  const lines = content.trim().split(`
53815
53908
  `);
53816
- log6.debug(`Log file has ${lines.length} lines`);
53909
+ log7.debug(`Log file has ${lines.length} lines`);
53817
53910
  const recentLines = lines.slice(-maxLines);
53818
53911
  const entries = [];
53819
53912
  for (const line of recentLines) {
@@ -53823,17 +53916,17 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
53823
53916
  entries.push(JSON.parse(line));
53824
53917
  } catch {}
53825
53918
  }
53826
- log6.debug(`Parsed ${entries.length} log entries`);
53919
+ log7.debug(`Parsed ${entries.length} log entries`);
53827
53920
  return entries;
53828
53921
  } catch (err) {
53829
- log6.error(`Failed to read log file: ${err}`);
53922
+ log7.error(`Failed to read log file: ${err}`);
53830
53923
  return [];
53831
53924
  }
53832
53925
  }
53833
53926
 
53834
53927
  // src/cleanup/scheduler.ts
53835
53928
  init_worktree();
53836
- var log8 = createLogger("cleanup");
53929
+ var log9 = createLogger("cleanup");
53837
53930
  var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
53838
53931
  var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
53839
53932
 
@@ -53856,17 +53949,17 @@ class CleanupScheduler {
53856
53949
  }
53857
53950
  start() {
53858
53951
  if (this.isRunning) {
53859
- log8.debug("Cleanup scheduler already running");
53952
+ log9.debug("Cleanup scheduler already running");
53860
53953
  return;
53861
53954
  }
53862
53955
  this.isRunning = true;
53863
- log8.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
53956
+ log9.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
53864
53957
  this.runCleanup().catch((err) => {
53865
- log8.warn(`Initial cleanup failed: ${err}`);
53958
+ log9.warn(`Initial cleanup failed: ${err}`);
53866
53959
  });
53867
53960
  this.timer = setInterval(() => {
53868
53961
  this.runCleanup().catch((err) => {
53869
- log8.warn(`Periodic cleanup failed: ${err}`);
53962
+ log9.warn(`Periodic cleanup failed: ${err}`);
53870
53963
  });
53871
53964
  }, this.intervalMs);
53872
53965
  }
@@ -53876,11 +53969,11 @@ class CleanupScheduler {
53876
53969
  this.timer = null;
53877
53970
  }
53878
53971
  this.isRunning = false;
53879
- log8.debug("Cleanup scheduler stopped");
53972
+ log9.debug("Cleanup scheduler stopped");
53880
53973
  }
53881
53974
  async runCleanup() {
53882
53975
  const startTime = Date.now();
53883
- log8.debug("Running background cleanup...");
53976
+ log9.debug("Running background cleanup...");
53884
53977
  const stats = {
53885
53978
  logsDeleted: 0,
53886
53979
  worktreesCleaned: 0,
@@ -53906,9 +53999,9 @@ class CleanupScheduler {
53906
53999
  const elapsed = Date.now() - startTime;
53907
54000
  const totalCleaned = stats.logsDeleted + stats.worktreesCleaned + stats.metadataCleaned;
53908
54001
  if (totalCleaned > 0 || stats.errors.length > 0) {
53909
- log8.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
54002
+ log9.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
53910
54003
  } else {
53911
- log8.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
54004
+ log9.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
53912
54005
  }
53913
54006
  return stats;
53914
54007
  }
@@ -53921,7 +54014,7 @@ class CleanupScheduler {
53921
54014
  const deleted = cleanupOldLogs(this.logRetentionDays);
53922
54015
  resolve3(deleted);
53923
54016
  } catch (err) {
53924
- log8.warn(`Log cleanup error: ${err}`);
54017
+ log9.warn(`Log cleanup error: ${err}`);
53925
54018
  resolve3(0);
53926
54019
  }
53927
54020
  });
@@ -53930,7 +54023,7 @@ class CleanupScheduler {
53930
54023
  const worktreesDir = getWorktreesDir();
53931
54024
  const result = { cleaned: 0, metadata: 0 };
53932
54025
  if (!existsSync7(worktreesDir)) {
53933
- log8.debug("No worktrees directory exists, nothing to clean");
54026
+ log9.debug("No worktrees directory exists, nothing to clean");
53934
54027
  return result;
53935
54028
  }
53936
54029
  const persisted = this.sessionStore.load();
@@ -53948,7 +54041,7 @@ class CleanupScheduler {
53948
54041
  continue;
53949
54042
  const worktreePath = join6(worktreesDir, entry.name);
53950
54043
  if (activeWorktrees.has(worktreePath)) {
53951
- log8.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
54044
+ log9.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
53952
54045
  continue;
53953
54046
  }
53954
54047
  const meta = await readWorktreeMetadata(worktreePath);
@@ -53958,7 +54051,7 @@ class CleanupScheduler {
53958
54051
  const lastActivity = new Date(meta.lastActivityAt).getTime();
53959
54052
  const age = now - lastActivity;
53960
54053
  if (meta.sessionId && age < this.maxWorktreeAgeMs) {
53961
- log8.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
54054
+ log9.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
53962
54055
  continue;
53963
54056
  }
53964
54057
  const merged = age >= this.maxWorktreeAgeMs ? await isBranchMerged(meta.repoRoot, meta.branch).catch(() => false) : false;
@@ -53969,7 +54062,7 @@ class CleanupScheduler {
53969
54062
  shouldCleanup = true;
53970
54063
  cleanupReason = `inactive for ${Math.round(age / 3600000)}h`;
53971
54064
  } else {
53972
- log8.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
54065
+ log9.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
53973
54066
  continue;
53974
54067
  }
53975
54068
  } else {
@@ -53978,7 +54071,7 @@ class CleanupScheduler {
53978
54071
  }
53979
54072
  if (!shouldCleanup)
53980
54073
  continue;
53981
- log8.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
54074
+ log9.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
53982
54075
  try {
53983
54076
  if (meta?.repoRoot) {
53984
54077
  await removeWorktree(meta.repoRoot, worktreePath);
@@ -53989,19 +54082,19 @@ class CleanupScheduler {
53989
54082
  await removeWorktreeMetadata(worktreePath);
53990
54083
  result.metadata++;
53991
54084
  } catch (err) {
53992
- log8.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
54085
+ log9.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
53993
54086
  try {
53994
54087
  await rm(worktreePath, { recursive: true, force: true });
53995
54088
  result.cleaned++;
53996
54089
  await removeWorktreeMetadata(worktreePath);
53997
54090
  result.metadata++;
53998
54091
  } catch (rmErr) {
53999
- log8.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
54092
+ log9.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
54000
54093
  }
54001
54094
  }
54002
54095
  }
54003
54096
  } catch (err) {
54004
- log8.warn(`Failed to scan worktrees directory: ${err}`);
54097
+ log9.warn(`Failed to scan worktrees directory: ${err}`);
54005
54098
  }
54006
54099
  return result;
54007
54100
  }
@@ -54078,7 +54171,70 @@ import { fileURLToPath as fileURLToPath3 } from "url";
54078
54171
  import { existsSync as existsSync8, readFileSync as readFileSync6, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
54079
54172
  import { tmpdir } from "os";
54080
54173
  import { join as join7 } from "path";
54081
- var log9 = createLogger("claude");
54174
+
54175
+ // src/claude/rate-limit-detector.ts
54176
+ var RATE_LIMIT_PHRASES = [
54177
+ /usage limit reached/i,
54178
+ /rate[_\s-]?limit[_\s-]?error/i,
54179
+ /you have hit the rate limit/i,
54180
+ /quota (has been )?exceeded/i,
54181
+ /\b429\b.*(rate|limit|quota)/i
54182
+ ];
54183
+ var DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;
54184
+ function detectRateLimit(text, now = Date.now()) {
54185
+ if (!text)
54186
+ return { detected: false };
54187
+ let matched;
54188
+ for (const phrase of RATE_LIMIT_PHRASES) {
54189
+ const m = text.match(phrase);
54190
+ if (m) {
54191
+ matched = m[0];
54192
+ break;
54193
+ }
54194
+ }
54195
+ if (!matched)
54196
+ return { detected: false };
54197
+ const resetAtEpochMs = extractResetAt(text, now);
54198
+ return { detected: true, matched, resetAtEpochMs };
54199
+ }
54200
+ function cooldownDeadline(hit, now = Date.now()) {
54201
+ if (!hit.detected)
54202
+ return now;
54203
+ return hit.resetAtEpochMs ?? now + DEFAULT_COOLDOWN_MS;
54204
+ }
54205
+ function extractResetAt(text, now) {
54206
+ const relative = text.match(/(?:retry[_\s-]?after|resets?\s+in)\s+(\d+)\s*(second|minute|hour|day)s?/i);
54207
+ if (relative) {
54208
+ const value = parseInt(relative[1], 10);
54209
+ const unit = relative[2].toLowerCase();
54210
+ const unitMs = {
54211
+ second: 1000,
54212
+ minute: 60000,
54213
+ hour: 3600000,
54214
+ day: 86400000
54215
+ };
54216
+ return now + value * unitMs[unit];
54217
+ }
54218
+ const unix = text.match(/["']?reset(?:_at)?["']?\s*[:=]\s*(\d{10,13})/);
54219
+ if (unix) {
54220
+ const raw = parseInt(unix[1], 10);
54221
+ return unix[1].length === 13 ? raw : raw * 1000;
54222
+ }
54223
+ const clock = text.match(/resets?\s+at\s+(\d{1,2}):(\d{2})\s*(utc|gmt)?/i);
54224
+ if (clock) {
54225
+ const hh = parseInt(clock[1], 10);
54226
+ const mm = parseInt(clock[2], 10);
54227
+ if (hh < 24 && mm < 60) {
54228
+ const reference = new Date(now);
54229
+ const target = new Date(Date.UTC(reference.getUTCFullYear(), reference.getUTCMonth(), reference.getUTCDate(), hh, mm)).getTime();
54230
+ return target > now ? target : target + 86400000;
54231
+ }
54232
+ }
54233
+ return;
54234
+ }
54235
+
54236
+ // src/claude/cli.ts
54237
+ var log10 = createLogger("claude");
54082
54238
  function cleanupBrowserBridgeSockets() {
54083
54239
  try {
54084
54240
  const tempDir = tmpdir();
@@ -54090,15 +54246,23 @@ function cleanupBrowserBridgeSockets() {
54090
54246
  const stats = statSync2(filePath);
54091
54247
  if (stats.isSocket()) {
54092
54248
  unlinkSync2(filePath);
54093
- log9.debug(`Removed stale browser bridge socket: ${file}`);
54249
+ log10.debug(`Removed stale browser bridge socket: ${file}`);
54094
54250
  }
54095
54251
  } catch {}
54096
54252
  }
54097
54253
  }
54098
54254
  } catch (err) {
54099
- log9.debug(`Browser bridge cleanup failed: ${err}`);
54255
+ log10.debug(`Browser bridge cleanup failed: ${err}`);
54100
54256
  }
54101
54257
  }
54258
+ function isErrorResultEvent(event) {
54259
+ const ev = event;
54260
+ if (typeof ev.subtype === "string" && ev.subtype.startsWith("error"))
54261
+ return true;
54262
+ if (ev.is_error === true)
54263
+ return true;
54264
+ return false;
54265
+ }
54102
54266
 
54103
54267
  class ClaudeCli extends EventEmitter2 {
54104
54268
  process = null;
@@ -54108,6 +54272,7 @@ class ClaudeCli extends EventEmitter2 {
54108
54272
  statusFilePath = null;
54109
54273
  lastStatusData = null;
54110
54274
  stderrBuffer = "";
54275
+ rateLimitEmitted = false;
54111
54276
  log;
54112
54277
  constructor(options2) {
54113
54278
  super();
@@ -54159,6 +54324,7 @@ class ClaudeCli extends EventEmitter2 {
54159
54324
  if (this.process)
54160
54325
  throw new Error("Already running");
54161
54326
  this.stderrBuffer = "";
54327
+ this.rateLimitEmitted = false;
54162
54328
  cleanupBrowserBridgeSockets();
54163
54329
  const claudePath = getClaudePath();
54164
54330
  const args = [
@@ -54228,9 +54394,13 @@ class ClaudeCli extends EventEmitter2 {
54228
54394
  args.push("--settings", JSON.stringify(statusLineSettings));
54229
54395
  }
54230
54396
  this.log.debug(`Starting: ${claudePath} ${args.slice(0, 5).join(" ")}...`);
54397
+ const childEnv = this.buildChildEnv();
54398
+ if (this.options.account) {
54399
+ this.log.debug(`Spawning under Claude account "${this.options.account.id}"`);
54400
+ }
54231
54401
  this.process = crossSpawn(claudePath, args, {
54232
54402
  cwd: this.options.workingDir,
54233
- env: process.env,
54403
+ env: childEnv,
54234
54404
  stdio: ["pipe", "pipe", "pipe"]
54235
54405
  });
54236
54406
  this.log.debug(`Claude process spawned: pid=${this.process.pid}`);
@@ -54244,6 +54414,7 @@ class ClaudeCli extends EventEmitter2 {
54244
54414
  this.stderrBuffer = this.stderrBuffer.slice(-10240);
54245
54415
  }
54246
54416
  this.log.debug(`stderr: ${text.trim()}`);
54417
+ this.maybeEmitRateLimit(text);
54247
54418
  });
54248
54419
  this.process.on("error", (err) => {
54249
54420
  this.log.error(`Claude error: ${err}`);
@@ -54298,9 +54469,22 @@ class ClaudeCli extends EventEmitter2 {
54298
54469
  try {
54299
54470
  const event = JSON.parse(trimmed);
54300
54471
  this.emit("event", event);
54472
+ if (event.type === "result" && isErrorResultEvent(event)) {
54473
+ this.maybeEmitRateLimit(trimmed);
54474
+ }
54301
54475
  } catch {}
54302
54476
  }
54303
54477
  }
54478
+ maybeEmitRateLimit(text) {
54479
+ const hit = detectRateLimit(text);
54480
+ if (!hit.detected)
54481
+ return;
54482
+ if (this.rateLimitEmitted)
54483
+ return;
54484
+ this.rateLimitEmitted = true;
54485
+ this.log.warn(`Rate limit detected: ${hit.matched ?? "(no match text)"}`);
54486
+ this.emit("rate-limit", hit);
54487
+ }
54304
54488
  isRunning() {
54305
54489
  return this.process !== null;
54306
54490
  }
@@ -54369,6 +54553,22 @@ class ClaudeCli extends EventEmitter2 {
54369
54553
  this.process.kill("SIGINT");
54370
54554
  return true;
54371
54555
  }
54556
+ buildChildEnv() {
54557
+ const account = this.options.account;
54558
+ if (!account)
54559
+ return process.env;
54560
+ const env = { ...process.env };
54561
+ if (account.home) {
54562
+ env.HOME = account.home;
54563
+ env.USERPROFILE = account.home;
54564
+ delete env.ANTHROPIC_API_KEY;
54565
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
54566
+ } else if (account.apiKey) {
54567
+ env.ANTHROPIC_API_KEY = account.apiKey;
54568
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
54569
+ }
54570
+ return env;
54571
+ }
54372
54572
  getMcpServerPath() {
54373
54573
  const __filename2 = fileURLToPath3(import.meta.url);
54374
54574
  const __dirname4 = dirname6(__filename2);
@@ -55205,7 +55405,7 @@ import { existsSync as existsSync11 } from "fs";
55205
55405
  // src/utils/keep-alive.ts
55206
55406
  init_logger();
55207
55407
  import { spawn as spawn2 } from "child_process";
55208
- var log10 = createLogger("keepalive");
55408
+ var log11 = createLogger("keepalive");
55209
55409
 
55210
55410
  class KeepAliveManager {
55211
55411
  activeSessionCount = 0;
@@ -55220,7 +55420,7 @@ class KeepAliveManager {
55220
55420
  if (!enabled && this.keepAliveProcess) {
55221
55421
  this.stopKeepAlive();
55222
55422
  }
55223
- log10.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
55423
+ log11.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
55224
55424
  }
55225
55425
  isEnabled() {
55226
55426
  return this.enabled;
@@ -55230,7 +55430,7 @@ class KeepAliveManager {
55230
55430
  }
55231
55431
  sessionStarted() {
55232
55432
  this.activeSessionCount++;
55233
- log10.debug(`Session started (${this.activeSessionCount} active)`);
55433
+ log11.debug(`Session started (${this.activeSessionCount} active)`);
55234
55434
  if (this.activeSessionCount === 1) {
55235
55435
  this.startKeepAlive();
55236
55436
  }
@@ -55239,7 +55439,7 @@ class KeepAliveManager {
55239
55439
  if (this.activeSessionCount > 0) {
55240
55440
  this.activeSessionCount--;
55241
55441
  }
55242
- log10.debug(`Session ended (${this.activeSessionCount} active)`);
55442
+ log11.debug(`Session ended (${this.activeSessionCount} active)`);
55243
55443
  if (this.activeSessionCount === 0) {
55244
55444
  this.stopKeepAlive();
55245
55445
  }
@@ -55253,11 +55453,11 @@ class KeepAliveManager {
55253
55453
  }
55254
55454
  startKeepAlive() {
55255
55455
  if (!this.enabled) {
55256
- log10.debug("Keep-alive disabled, skipping");
55456
+ log11.debug("Keep-alive disabled, skipping");
55257
55457
  return;
55258
55458
  }
55259
55459
  if (this.keepAliveProcess) {
55260
- log10.debug("Keep-alive already running");
55460
+ log11.debug("Keep-alive already running");
55261
55461
  return;
55262
55462
  }
55263
55463
  switch (this.platform) {
@@ -55271,12 +55471,12 @@ class KeepAliveManager {
55271
55471
  this.startWindowsKeepAlive();
55272
55472
  break;
55273
55473
  default:
55274
- log10.warn(`Keep-alive not supported on ${this.platform}`);
55474
+ log11.warn(`Keep-alive not supported on ${this.platform}`);
55275
55475
  }
55276
55476
  }
55277
55477
  stopKeepAlive() {
55278
55478
  if (this.keepAliveProcess) {
55279
- log10.debug("Stopping keep-alive");
55479
+ log11.debug("Stopping keep-alive");
55280
55480
  this.keepAliveProcess.kill();
55281
55481
  this.keepAliveProcess = null;
55282
55482
  }
@@ -55288,18 +55488,18 @@ class KeepAliveManager {
55288
55488
  detached: false
55289
55489
  });
55290
55490
  this.keepAliveProcess.on("error", (err) => {
55291
- log10.error(`Failed to start caffeinate: ${err.message}`);
55491
+ log11.error(`Failed to start caffeinate: ${err.message}`);
55292
55492
  this.keepAliveProcess = null;
55293
55493
  });
55294
55494
  this.keepAliveProcess.on("exit", (code) => {
55295
55495
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
55296
- log10.debug(`caffeinate exited with code ${code}`);
55496
+ log11.debug(`caffeinate exited with code ${code}`);
55297
55497
  }
55298
55498
  this.keepAliveProcess = null;
55299
55499
  });
55300
- log10.info("Sleep prevention active (caffeinate)");
55500
+ log11.info("Sleep prevention active (caffeinate)");
55301
55501
  } catch (err) {
55302
- log10.error(`Failed to start caffeinate: ${err}`);
55502
+ log11.error(`Failed to start caffeinate: ${err}`);
55303
55503
  }
55304
55504
  }
55305
55505
  startLinuxKeepAlive() {
@@ -55315,19 +55515,19 @@ class KeepAliveManager {
55315
55515
  detached: false
55316
55516
  });
55317
55517
  this.keepAliveProcess.on("error", (err) => {
55318
- log10.debug(`systemd-inhibit not available: ${err.message}`);
55518
+ log11.debug(`systemd-inhibit not available: ${err.message}`);
55319
55519
  this.keepAliveProcess = null;
55320
55520
  this.startLinuxKeepAliveFallback();
55321
55521
  });
55322
55522
  this.keepAliveProcess.on("exit", (code) => {
55323
55523
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
55324
- log10.debug(`systemd-inhibit exited with code ${code}`);
55524
+ log11.debug(`systemd-inhibit exited with code ${code}`);
55325
55525
  }
55326
55526
  this.keepAliveProcess = null;
55327
55527
  });
55328
- log10.info("Sleep prevention active (systemd-inhibit)");
55528
+ log11.info("Sleep prevention active (systemd-inhibit)");
55329
55529
  } catch (err) {
55330
- log10.debug(`Failed to start systemd-inhibit: ${err}`);
55530
+ log11.debug(`Failed to start systemd-inhibit: ${err}`);
55331
55531
  this.startLinuxKeepAliveFallback();
55332
55532
  }
55333
55533
  }
@@ -55341,15 +55541,15 @@ class KeepAliveManager {
55341
55541
  detached: false
55342
55542
  });
55343
55543
  this.keepAliveProcess.on("error", (err) => {
55344
- log10.warn(`Linux keep-alive fallback not available: ${err.message}`);
55544
+ log11.warn(`Linux keep-alive fallback not available: ${err.message}`);
55345
55545
  this.keepAliveProcess = null;
55346
55546
  });
55347
55547
  this.keepAliveProcess.on("exit", () => {
55348
55548
  this.keepAliveProcess = null;
55349
55549
  });
55350
- log10.info("Sleep prevention active (xdg-screensaver)");
55550
+ log11.info("Sleep prevention active (xdg-screensaver)");
55351
55551
  } catch (err) {
55352
- log10.warn(`Linux keep-alive not available: ${err}`);
55552
+ log11.warn(`Linux keep-alive not available: ${err}`);
55353
55553
  }
55354
55554
  }
55355
55555
  startWindowsKeepAlive() {
@@ -55374,18 +55574,18 @@ class KeepAliveManager {
55374
55574
  windowsHide: true
55375
55575
  });
55376
55576
  this.keepAliveProcess.on("error", (err) => {
55377
- log10.warn(`Windows keep-alive not available: ${err.message}`);
55577
+ log11.warn(`Windows keep-alive not available: ${err.message}`);
55378
55578
  this.keepAliveProcess = null;
55379
55579
  });
55380
55580
  this.keepAliveProcess.on("exit", (code) => {
55381
55581
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
55382
- log10.debug(`PowerShell keep-alive exited with code ${code}`);
55582
+ log11.debug(`PowerShell keep-alive exited with code ${code}`);
55383
55583
  }
55384
55584
  this.keepAliveProcess = null;
55385
55585
  });
55386
- log10.info("Sleep prevention active (SetThreadExecutionState)");
55586
+ log11.info("Sleep prevention active (SetThreadExecutionState)");
55387
55587
  } catch (err) {
55388
- log10.warn(`Windows keep-alive not available: ${err}`);
55588
+ log11.warn(`Windows keep-alive not available: ${err}`);
55389
55589
  }
55390
55590
  }
55391
55591
  }
@@ -55393,7 +55593,7 @@ var keepAlive = new KeepAliveManager;
55393
55593
 
55394
55594
  // src/utils/error-handler/index.ts
55395
55595
  init_logger();
55396
- var log11 = createLogger("error");
55596
+ var log12 = createLogger("error");
55397
55597
 
55398
55598
  class SessionError extends Error {
55399
55599
  sessionId;
@@ -55419,19 +55619,19 @@ async function handleError(error, context, severity = "recoverable") {
55419
55619
  const sessionPart = sessionId ? ` (${formatShortId(sessionId)})` : "";
55420
55620
  const logMessage = `${context.action}${sessionPart}: ${message}`;
55421
55621
  if (severity === "recoverable") {
55422
- log11.warn(logMessage);
55622
+ log12.warn(logMessage);
55423
55623
  } else {
55424
- log11.error(logMessage, error instanceof Error ? error : undefined);
55624
+ log12.error(logMessage, error instanceof Error ? error : undefined);
55425
55625
  }
55426
55626
  if (context.details) {
55427
- log11.debugJson("Error details", context.details);
55627
+ log12.debugJson("Error details", context.details);
55428
55628
  }
55429
55629
  if (context.notifyUser && context.session) {
55430
55630
  try {
55431
55631
  const fmt = context.session.platform.getFormatter();
55432
55632
  await context.session.platform.createPost(`⚠️ ${fmt.formatBold("Error")}: ${context.action} failed - ${message}`, context.session.threadId);
55433
55633
  } catch (notifyError) {
55434
- log11.warn(`Could not notify user: ${notifyError}`);
55634
+ log12.warn(`Could not notify user: ${notifyError}`);
55435
55635
  }
55436
55636
  }
55437
55637
  if (severity === "session-fatal" || severity === "system-fatal") {
@@ -55458,7 +55658,7 @@ async function logAndNotify(error, context) {
55458
55658
  }
55459
55659
  function logSilentError(context, error) {
55460
55660
  const message = error instanceof Error ? error.message : String(error);
55461
- log11.debug(`[${context}] Silently caught: ${message}`);
55661
+ log12.debug(`[${context}] Silently caught: ${message}`);
55462
55662
  }
55463
55663
 
55464
55664
  // src/session/lifecycle.ts
@@ -55478,8 +55678,8 @@ function createSessionLog(baseLog) {
55478
55678
  init_logger();
55479
55679
  init_emoji();
55480
55680
  init_worktree();
55481
- var log12 = createLogger("helpers");
55482
- var sessionLog = createSessionLog(log12);
55681
+ var log13 = createLogger("helpers");
55682
+ var sessionLog = createSessionLog(log13);
55483
55683
  var POST_TYPES = {
55484
55684
  info: "",
55485
55685
  success: "✅",
@@ -55563,7 +55763,7 @@ function updateLastMessage(session, post2) {
55563
55763
  // src/claude/quick-query.ts
55564
55764
  init_spawn();
55565
55765
  init_logger();
55566
- var log13 = createLogger("query");
55766
+ var log14 = createLogger("query");
55567
55767
  async function quickQuery(options2) {
55568
55768
  const {
55569
55769
  prompt,
@@ -55579,7 +55779,7 @@ async function quickQuery(options2) {
55579
55779
  args.push("--system-prompt", systemPrompt);
55580
55780
  }
55581
55781
  args.push(prompt);
55582
- log13.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
55782
+ log14.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
55583
55783
  return new Promise((resolve5) => {
55584
55784
  let stdout = "";
55585
55785
  let stderr = "";
@@ -55593,7 +55793,7 @@ async function quickQuery(options2) {
55593
55793
  if (!resolved) {
55594
55794
  resolved = true;
55595
55795
  proc.kill("SIGTERM");
55596
- log13.debug(`Quick query timed out after ${timeout}ms`);
55796
+ log14.debug(`Quick query timed out after ${timeout}ms`);
55597
55797
  resolve5({
55598
55798
  success: false,
55599
55799
  error: "timeout",
@@ -55611,7 +55811,7 @@ async function quickQuery(options2) {
55611
55811
  if (!resolved) {
55612
55812
  resolved = true;
55613
55813
  clearTimeout(timeoutId);
55614
- log13.debug(`Quick query error: ${err.message}`);
55814
+ log14.debug(`Quick query error: ${err.message}`);
55615
55815
  resolve5({
55616
55816
  success: false,
55617
55817
  error: err.message,
@@ -55625,14 +55825,14 @@ async function quickQuery(options2) {
55625
55825
  clearTimeout(timeoutId);
55626
55826
  const durationMs = Date.now() - startTime;
55627
55827
  if (code === 0 && stdout.trim()) {
55628
- log13.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
55828
+ log14.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
55629
55829
  resolve5({
55630
55830
  success: true,
55631
55831
  response: stdout.trim(),
55632
55832
  durationMs
55633
55833
  });
55634
55834
  } else {
55635
- log13.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
55835
+ log14.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
55636
55836
  resolve5({
55637
55837
  success: false,
55638
55838
  error: stderr || `exit code ${code}`,
@@ -55647,7 +55847,7 @@ async function quickQuery(options2) {
55647
55847
 
55648
55848
  // src/operations/suggestions/title.ts
55649
55849
  init_logger();
55650
- var log14 = createLogger("title");
55850
+ var log15 = createLogger("title");
55651
55851
  var SUGGESTION_TIMEOUT = 15000;
55652
55852
  var MIN_TITLE_LENGTH = 3;
55653
55853
  var MAX_TITLE_LENGTH = 50;
@@ -55711,32 +55911,32 @@ function parseMetadata(response) {
55711
55911
  const titleMatch = response.match(/TITLE:\s*(.+)/i);
55712
55912
  const descMatch = response.match(/DESC:\s*(.+)/i);
55713
55913
  if (!titleMatch || !descMatch) {
55714
- log14.debug("Failed to parse title/description from response");
55914
+ log15.debug("Failed to parse title/description from response");
55715
55915
  return null;
55716
55916
  }
55717
55917
  let title = titleMatch[1].trim();
55718
55918
  let description = descMatch[1].trim();
55719
55919
  if (title.length < MIN_TITLE_LENGTH) {
55720
- log14.debug(`Title too short: ${title.length} chars`);
55920
+ log15.debug(`Title too short: ${title.length} chars`);
55721
55921
  return null;
55722
55922
  }
55723
55923
  if (title.length > MAX_TITLE_LENGTH) {
55724
- log14.debug(`Title too long (${title.length} chars), truncating`);
55924
+ log15.debug(`Title too long (${title.length} chars), truncating`);
55725
55925
  title = truncateAtWord(title, MAX_TITLE_LENGTH);
55726
55926
  }
55727
55927
  if (description.length < MIN_DESC_LENGTH) {
55728
- log14.debug(`Description too short: ${description.length} chars`);
55928
+ log15.debug(`Description too short: ${description.length} chars`);
55729
55929
  return null;
55730
55930
  }
55731
55931
  if (description.length > MAX_DESC_LENGTH) {
55732
- log14.debug(`Description too long (${description.length} chars), truncating`);
55932
+ log15.debug(`Description too long (${description.length} chars), truncating`);
55733
55933
  description = truncateAtWord(description, MAX_DESC_LENGTH);
55734
55934
  }
55735
55935
  return { title, description };
55736
55936
  }
55737
55937
  async function suggestSessionMetadata(context) {
55738
55938
  const logContext = typeof context === "string" ? context.substring(0, 50) : context.originalTask.substring(0, 50);
55739
- log14.debug(`Suggesting title for: "${logContext}..."`);
55939
+ log15.debug(`Suggesting title for: "${logContext}..."`);
55740
55940
  try {
55741
55941
  const result = await quickQuery({
55742
55942
  prompt: buildTitlePrompt(context),
@@ -55744,23 +55944,23 @@ async function suggestSessionMetadata(context) {
55744
55944
  timeout: SUGGESTION_TIMEOUT
55745
55945
  });
55746
55946
  if (!result.success || !result.response) {
55747
- log14.debug(`Title suggestion failed: ${result.error || "no response"}`);
55947
+ log15.debug(`Title suggestion failed: ${result.error || "no response"}`);
55748
55948
  return null;
55749
55949
  }
55750
55950
  const metadata = parseMetadata(result.response);
55751
55951
  if (metadata) {
55752
- log14.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
55952
+ log15.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
55753
55953
  }
55754
55954
  return metadata;
55755
55955
  } catch (err) {
55756
- log14.debug(`Title suggestion error: ${err}`);
55956
+ log15.debug(`Title suggestion error: ${err}`);
55757
55957
  return null;
55758
55958
  }
55759
55959
  }
55760
55960
 
55761
55961
  // src/operations/suggestions/tag.ts
55762
55962
  init_logger();
55763
- var log15 = createLogger("tags");
55963
+ var log16 = createLogger("tags");
55764
55964
  var SUGGESTION_TIMEOUT2 = 15000;
55765
55965
  var MAX_TAGS = 3;
55766
55966
  var VALID_TAGS = [
@@ -55792,7 +55992,7 @@ function parseTags(response) {
55792
55992
  return [...new Set(tags)].slice(0, MAX_TAGS);
55793
55993
  }
55794
55994
  async function suggestSessionTags(userMessage) {
55795
- log15.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
55995
+ log16.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
55796
55996
  try {
55797
55997
  const result = await quickQuery({
55798
55998
  prompt: buildTagPrompt(userMessage),
@@ -55800,14 +56000,14 @@ async function suggestSessionTags(userMessage) {
55800
56000
  timeout: SUGGESTION_TIMEOUT2
55801
56001
  });
55802
56002
  if (!result.success || !result.response) {
55803
- log15.debug(`Tag suggestion failed: ${result.error || "no response"}`);
56003
+ log16.debug(`Tag suggestion failed: ${result.error || "no response"}`);
55804
56004
  return [];
55805
56005
  }
55806
56006
  const tags = parseTags(result.response);
55807
- log15.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
56007
+ log16.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
55808
56008
  return tags;
55809
56009
  } catch (err) {
55810
- log15.debug(`Tag suggestion error: ${err}`);
56010
+ log16.debug(`Tag suggestion error: ${err}`);
55811
56011
  return [];
55812
56012
  }
55813
56013
  }
@@ -59349,7 +59549,7 @@ class BugReportExecutor extends BaseExecutor {
59349
59549
  // src/operations/executors/worktree-prompt.ts
59350
59550
  init_emoji();
59351
59551
  init_logger();
59352
- var log16 = createLogger("wt-prompt");
59552
+ var log17 = createLogger("wt-prompt");
59353
59553
  // src/operations/message-manager.ts
59354
59554
  init_logger();
59355
59555
 
@@ -59386,7 +59586,7 @@ var import_yauzl = __toESM(require_yauzl(), 1);
59386
59586
  import { createGunzip } from "zlib";
59387
59587
  import { pipeline } from "stream/promises";
59388
59588
  import { Readable, Writable } from "stream";
59389
- var log17 = createLogger("streaming");
59589
+ var log18 = createLogger("streaming");
59390
59590
  var MAX_PDF_SIZE = 32 * 1024 * 1024;
59391
59591
  var MAX_TEXT_FILE_SIZE = 1 * 1024 * 1024;
59392
59592
  var MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024;
@@ -59420,6 +59620,8 @@ var TEXT_FILE_EXTENSIONS = [
59420
59620
  ".xml",
59421
59621
  ".yaml",
59422
59622
  ".yml",
59623
+ ".har",
59624
+ ".log",
59423
59625
  ".js",
59424
59626
  ".ts",
59425
59627
  ".jsx",
@@ -59510,7 +59712,7 @@ async function processImageFile(file, platform, debug = false) {
59510
59712
  const buffer = await platform.downloadFile(file.id);
59511
59713
  const base64 = buffer.toString("base64");
59512
59714
  if (debug) {
59513
- log17.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
59715
+ log18.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
59514
59716
  }
59515
59717
  return {
59516
59718
  block: {
@@ -59523,7 +59725,7 @@ async function processImageFile(file, platform, debug = false) {
59523
59725
  }
59524
59726
  };
59525
59727
  } catch (err) {
59526
- log17.error(`Failed to download image ${file.name}: ${err}`);
59728
+ log18.error(`Failed to download image ${file.name}: ${err}`);
59527
59729
  return {
59528
59730
  skipped: {
59529
59731
  name: file.name,
@@ -59554,7 +59756,7 @@ async function processPdfFile(file, platform, debug = false) {
59554
59756
  }
59555
59757
  const base64 = buffer.toString("base64");
59556
59758
  if (debug) {
59557
- log17.debug(`Attached PDF: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
59759
+ log18.debug(`Attached PDF: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
59558
59760
  }
59559
59761
  return {
59560
59762
  block: {
@@ -59568,7 +59770,7 @@ async function processPdfFile(file, platform, debug = false) {
59568
59770
  }
59569
59771
  };
59570
59772
  } catch (err) {
59571
- log17.error(`Failed to process PDF ${file.name}: ${err}`);
59773
+ log18.error(`Failed to process PDF ${file.name}: ${err}`);
59572
59774
  return {
59573
59775
  skipped: {
59574
59776
  name: file.name,
@@ -59599,7 +59801,7 @@ async function processTextFile(file, platform, debug = false) {
59599
59801
  }
59600
59802
  const content = buffer.toString("utf-8");
59601
59803
  if (debug) {
59602
- log17.debug(`Attached text file: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
59804
+ log18.debug(`Attached text file: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
59603
59805
  }
59604
59806
  const wrappedContent = formatTextFileContent(file.name, content);
59605
59807
  return {
@@ -59609,7 +59811,7 @@ async function processTextFile(file, platform, debug = false) {
59609
59811
  }
59610
59812
  };
59611
59813
  } catch (err) {
59612
- log17.error(`Failed to process text file ${file.name}: ${err}`);
59814
+ log18.error(`Failed to process text file ${file.name}: ${err}`);
59613
59815
  return {
59614
59816
  skipped: {
59615
59817
  name: file.name,
@@ -59667,7 +59869,7 @@ async function processGzipFile(file, platform, debug = false) {
59667
59869
  compressedBuffer = await platform.downloadFile(file.id);
59668
59870
  } catch (err) {
59669
59871
  const errorMessage = err instanceof Error ? err.message : String(err);
59670
- log17.error(`Failed to download gzip file ${file.name}: ${errorMessage}`);
59872
+ log18.error(`Failed to download gzip file ${file.name}: ${errorMessage}`);
59671
59873
  return {
59672
59874
  skipped: {
59673
59875
  name: file.name,
@@ -59677,7 +59879,7 @@ async function processGzipFile(file, platform, debug = false) {
59677
59879
  };
59678
59880
  }
59679
59881
  if (file.size && compressedBuffer.length !== file.size) {
59680
- log17.warn(`Downloaded size mismatch for ${file.name}: expected ${file.size}, got ${compressedBuffer.length}`);
59882
+ log18.warn(`Downloaded size mismatch for ${file.name}: expected ${file.size}, got ${compressedBuffer.length}`);
59681
59883
  }
59682
59884
  let decompressedBuffer;
59683
59885
  try {
@@ -59713,7 +59915,7 @@ async function processGzipFile(file, platform, debug = false) {
59713
59915
  const innerFilename = file.name.toLowerCase().endsWith(".gz") ? file.name.slice(0, -3) : file.name;
59714
59916
  const contentType = detectDecompressedContentType(decompressedBuffer, innerFilename);
59715
59917
  if (debug) {
59716
- log17.debug(`Decompressed ${file.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
59918
+ log18.debug(`Decompressed ${file.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
59717
59919
  }
59718
59920
  if (contentType === "pdf") {
59719
59921
  const base64 = decompressedBuffer.toString("base64");
@@ -59748,7 +59950,7 @@ async function processGzipFile(file, platform, debug = false) {
59748
59950
  }
59749
59951
  } catch (err) {
59750
59952
  const errorMessage = err instanceof Error ? err.message : String(err);
59751
- log17.error(`Failed to process gzip file ${file.name}: ${errorMessage}`);
59953
+ log18.error(`Failed to process gzip file ${file.name}: ${errorMessage}`);
59752
59954
  return {
59753
59955
  skipped: {
59754
59956
  name: file.name,
@@ -59800,7 +60002,7 @@ async function processZipFile(file, platform, debug = false) {
59800
60002
  }
59801
60003
  const zipBuffer = await platform.downloadFile(file.id);
59802
60004
  if (debug) {
59803
- log17.debug(`Processing zip file ${file.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
60005
+ log18.debug(`Processing zip file ${file.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
59804
60006
  }
59805
60007
  const zipfile = await new Promise((resolve5, reject) => {
59806
60008
  import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
@@ -59874,7 +60076,7 @@ async function processZipFile(file, platform, debug = false) {
59874
60076
  const buffer = await extractZipEntry(zipfile2, entry);
59875
60077
  const contentType = detectDecompressedContentType(buffer, entry.fileName);
59876
60078
  if (debug) {
59877
- log17.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
60079
+ log18.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
59878
60080
  }
59879
60081
  if (contentType === "pdf") {
59880
60082
  const base64 = buffer.toString("base64");
@@ -59917,11 +60119,11 @@ async function processZipFile(file, platform, debug = false) {
59917
60119
  });
59918
60120
  zipfile2.close();
59919
60121
  if (debug) {
59920
- log17.debug(`Zip ${file.name}: processed ${processedCount} files, skipped ${skipped.length}`);
60122
+ log18.debug(`Zip ${file.name}: processed ${processedCount} files, skipped ${skipped.length}`);
59921
60123
  }
59922
60124
  return { blocks, skipped };
59923
60125
  } catch (err) {
59924
- log17.error(`Failed to process zip file ${file.name}: ${err}`);
60126
+ log18.error(`Failed to process zip file ${file.name}: ${err}`);
59925
60127
  return {
59926
60128
  blocks: [],
59927
60129
  skipped: [{
@@ -59990,7 +60192,7 @@ function getUnsupportedFileSuggestion(file) {
59990
60192
  async function buildMessageContent(text, platform, files, debug = false) {
59991
60193
  const result = await processFiles(platform, files, debug);
59992
60194
  if (result.blocks.length === 0) {
59993
- return text;
60195
+ return { content: text, skipped: result.skipped };
59994
60196
  }
59995
60197
  if (text) {
59996
60198
  result.blocks.push({
@@ -59998,7 +60200,12 @@ async function buildMessageContent(text, platform, files, debug = false) {
59998
60200
  text
59999
60201
  });
60000
60202
  }
60001
- return result.blocks;
60203
+ return { content: result.blocks, skipped: result.skipped };
60204
+ }
60205
+ async function postSkippedFilesFeedback(platform, threadId, skipped) {
60206
+ if (skipped.length === 0)
60207
+ return;
60208
+ await platform.createPost(formatSkippedFilesFeedback(skipped), threadId);
60002
60209
  }
60003
60210
  async function processFiles(platform, files, debug = false) {
60004
60211
  const blocks = [];
@@ -60013,7 +60220,7 @@ async function processFiles(platform, files, debug = false) {
60013
60220
  blocks.push(...zipResult.blocks);
60014
60221
  for (const s of zipResult.skipped) {
60015
60222
  skipped.push(s);
60016
- log17.warn(`Skipped file ${s.name}: ${s.reason}`);
60223
+ log18.warn(`Skipped file ${s.name}: ${s.reason}`);
60017
60224
  }
60018
60225
  continue;
60019
60226
  }
@@ -60047,11 +60254,23 @@ async function processFiles(platform, files, debug = false) {
60047
60254
  }
60048
60255
  if (result.skipped) {
60049
60256
  skipped.push(result.skipped);
60050
- log17.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
60257
+ log18.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
60051
60258
  }
60052
60259
  }
60053
60260
  return { blocks, skipped };
60054
60261
  }
60262
+ function formatSkippedFilesFeedback(skippedFiles) {
60263
+ const lines = ["⚠️ **Some files could not be processed:**"];
60264
+ for (const file of skippedFiles) {
60265
+ let line = `- **${file.name}**: ${file.reason}`;
60266
+ if (file.suggestion) {
60267
+ line += ` _(${file.suggestion})_`;
60268
+ }
60269
+ lines.push(line);
60270
+ }
60271
+ return lines.join(`
60272
+ `);
60273
+ }
60055
60274
  function startTyping(session) {
60056
60275
  if (session.timers.typingTimer)
60057
60276
  return;
@@ -60068,7 +60287,7 @@ function stopTyping(session) {
60068
60287
  }
60069
60288
 
60070
60289
  // src/operations/message-manager.ts
60071
- var log18 = createLogger("msg-mgr");
60290
+ var log19 = createLogger("msg-mgr");
60072
60291
 
60073
60292
  class MessageManager {
60074
60293
  platform;
@@ -60156,7 +60375,7 @@ class MessageManager {
60156
60375
  });
60157
60376
  }
60158
60377
  async handleEvent(event) {
60159
- const logger = log18.forSession(this.sessionId);
60378
+ const logger = log19.forSession(this.sessionId);
60160
60379
  const transformCtx = {
60161
60380
  sessionId: this.sessionId,
60162
60381
  formatter: this.platform.getFormatter(),
@@ -60206,7 +60425,7 @@ class MessageManager {
60206
60425
  }
60207
60426
  }
60208
60427
  async executeOperation(op) {
60209
- const logger = log18.forSession(this.sessionId);
60428
+ const logger = log19.forSession(this.sessionId);
60210
60429
  const ctx = this.getExecutorContext();
60211
60430
  try {
60212
60431
  if (isContentOp(op)) {
@@ -60274,7 +60493,7 @@ class MessageManager {
60274
60493
  threadId: this.threadId,
60275
60494
  platform: this.platform,
60276
60495
  formatter: this.platform.getFormatter(),
60277
- logger: log18.forSession(this.sessionId),
60496
+ logger: log19.forSession(this.sessionId),
60278
60497
  postTracker: this.postTracker,
60279
60498
  contentBreaker: this.contentBreaker,
60280
60499
  threadLogger: this.session.threadLogger,
@@ -60485,33 +60704,28 @@ class MessageManager {
60485
60704
  return this.systemExecutor.postSuccess(message, this.getExecutorContext());
60486
60705
  }
60487
60706
  async prepareForUserMessage() {
60488
- const logger = log18.forSession(this.sessionId);
60707
+ const logger = log19.forSession(this.sessionId);
60489
60708
  logger.debug("Preparing for new user message");
60490
60709
  await this.closeCurrentPost();
60491
60710
  await this.bumpTaskList();
60492
60711
  }
60493
60712
  async handleUserMessage(message, files, username, displayName) {
60494
- const logger = log18.forSession(this.sessionId);
60713
+ const logger = log19.forSession(this.sessionId);
60495
60714
  if (!this.session.claude.isRunning()) {
60496
60715
  logger.debug("Claude not running, ignoring user message");
60497
60716
  return false;
60498
60717
  }
60499
60718
  this.session.threadLogger?.logUserMessage(username || this.session.startedBy, message, displayName, files && files.length > 0);
60500
60719
  await this.prepareForUserMessage();
60501
- let skippedFiles = [];
60502
- if (files && files.length > 0) {
60503
- const fileResult = await processFiles(this.platform, files);
60504
- skippedFiles = fileResult.skipped;
60505
- }
60506
60720
  let content = message;
60721
+ let skippedFiles = [];
60507
60722
  if (this.buildMessageContentCallback) {
60508
- content = await this.buildMessageContentCallback(message, this.platform, files);
60723
+ const built = await this.buildMessageContentCallback(message, this.platform, files);
60724
+ content = built.content;
60725
+ skippedFiles = built.skipped;
60509
60726
  }
60510
60727
  this.session.claude.sendMessage(content);
60511
- if (skippedFiles.length > 0) {
60512
- const feedback = this.formatSkippedFilesFeedback(skippedFiles);
60513
- await this.platform.createPost(feedback, this.threadId);
60514
- }
60728
+ await postSkippedFilesFeedback(this.platform, this.threadId, skippedFiles);
60515
60729
  this.session.lastActivityAt = new Date;
60516
60730
  this.session.isProcessing = true;
60517
60731
  this.emitSessionUpdateCallback?.({ status: "active", isTyping: true });
@@ -60519,23 +60733,11 @@ class MessageManager {
60519
60733
  logger.debug("User message sent to Claude");
60520
60734
  return true;
60521
60735
  }
60522
- formatSkippedFilesFeedback(skippedFiles) {
60523
- const lines = ["⚠️ **Some files could not be processed:**"];
60524
- for (const file of skippedFiles) {
60525
- let line = `- **${file.name}**: ${file.reason}`;
60526
- if (file.suggestion) {
60527
- line += ` _(${file.suggestion})_`;
60528
- }
60529
- lines.push(line);
60530
- }
60531
- return lines.join(`
60532
- `);
60533
- }
60534
60736
  getSession() {
60535
60737
  return this.session;
60536
60738
  }
60537
60739
  async handleReaction(postId, emoji, user, action) {
60538
- const logger = log18.forSession(this.sessionId);
60740
+ const logger = log19.forSession(this.sessionId);
60539
60741
  const ctx = this.getExecutorContext();
60540
60742
  logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
60541
60743
  if (await this.questionApprovalExecutor.handleReaction(postId, emoji, user, action, ctx)) {
@@ -60731,7 +60933,7 @@ function formatPullRequestLink(url, formatter) {
60731
60933
  }
60732
60934
 
60733
60935
  // src/operations/sticky-message/handler.ts
60734
- var log19 = createLogger("sticky");
60936
+ var log20 = createLogger("sticky");
60735
60937
  var botStartedAt = new Date;
60736
60938
  function getPendingPrompts(session) {
60737
60939
  const prompts2 = [];
@@ -60806,21 +61008,21 @@ function initialize(store) {
60806
61008
  stickyPostIds.set(platformId, postId);
60807
61009
  }
60808
61010
  if (persistedIds.size > 0) {
60809
- log19.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
61011
+ log20.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
60810
61012
  }
60811
61013
  }
60812
61014
  function setPlatformPaused(platformId, paused) {
60813
61015
  if (paused) {
60814
61016
  pausedPlatforms.set(platformId, true);
60815
- log19.debug(`Platform ${platformId} marked as paused`);
61017
+ log20.debug(`Platform ${platformId} marked as paused`);
60816
61018
  } else {
60817
61019
  pausedPlatforms.delete(platformId);
60818
- log19.debug(`Platform ${platformId} marked as active`);
61020
+ log20.debug(`Platform ${platformId} marked as active`);
60819
61021
  }
60820
61022
  }
60821
61023
  function setShuttingDown(shuttingDown) {
60822
61024
  isShuttingDown = shuttingDown;
60823
- log19.debug(`Bot shutdown state: ${shuttingDown}`);
61025
+ log20.debug(`Bot shutdown state: ${shuttingDown}`);
60824
61026
  }
60825
61027
  function getTaskContent(session) {
60826
61028
  const taskState = session.messageManager?.getTaskListState();
@@ -60917,6 +61119,13 @@ async function buildStatusBar(sessionCount, config, formatter, platformId) {
60917
61119
  }
60918
61120
  items.push(formatter.formatCode(formatVersionString()));
60919
61121
  items.push(formatter.formatCode(`${sessionCount}/${config.maxSessions} sessions`));
61122
+ if (config.accountPoolStatus && config.accountPoolStatus.length > 0) {
61123
+ const total = config.accountPoolStatus.length;
61124
+ const cooling = config.accountPoolStatus.filter((a) => a.coolingUntil !== null).length;
61125
+ const available = total - cooling;
61126
+ const label = cooling > 0 ? `\uD83D\uDD11 ${available}/${total} accounts (${cooling} cooling)` : `\uD83D\uDD11 ${total} account${total === 1 ? "" : "s"}`;
61127
+ items.push(formatter.formatCode(label));
61128
+ }
60920
61129
  const permMode = config.skipPermissions ? "⚡ Auto" : "\uD83D\uDD10 Interactive";
60921
61130
  items.push(formatter.formatCode(permMode));
60922
61131
  if (config.worktreeMode === "require") {
@@ -61125,12 +61334,12 @@ async function validateLastMessageIds(platform, sessions) {
61125
61334
  try {
61126
61335
  const post2 = await platform.getPost(lastMessageId);
61127
61336
  if (!post2) {
61128
- log19.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
61337
+ log20.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
61129
61338
  session.lastMessageId = undefined;
61130
61339
  session.lastMessageTs = undefined;
61131
61340
  }
61132
61341
  } catch (err) {
61133
- log19.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
61342
+ log20.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
61134
61343
  session.lastMessageId = undefined;
61135
61344
  session.lastMessageTs = undefined;
61136
61345
  }
@@ -61139,63 +61348,63 @@ async function validateLastMessageIds(platform, sessions) {
61139
61348
  }
61140
61349
  async function updateStickyMessageImpl(platform, sessions, config) {
61141
61350
  const platformSessions = [...sessions.values()].filter((s) => s.platformId === platform.platformId);
61142
- log19.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
61351
+ log20.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
61143
61352
  for (const s of platformSessions) {
61144
- log19.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
61353
+ log20.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
61145
61354
  }
61146
61355
  await validateLastMessageIds(platform, platformSessions);
61147
61356
  const formatter = platform.getFormatter();
61148
61357
  const content = await buildStickyMessage(sessions, platform.platformId, config, formatter, (threadId) => platform.getThreadLink(threadId));
61149
61358
  const existingPostId = stickyPostIds.get(platform.platformId);
61150
61359
  const shouldBump = needsBump.get(platform.platformId) ?? false;
61151
- log19.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
61360
+ log20.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
61152
61361
  try {
61153
61362
  if (existingPostId && !shouldBump) {
61154
- log19.debug(`Updating existing post in place...`);
61363
+ log20.debug(`Updating existing post in place...`);
61155
61364
  try {
61156
61365
  await platform.updatePost(existingPostId, content);
61157
61366
  try {
61158
61367
  await platform.pinPost(existingPostId);
61159
- log19.debug(`Re-pinned post`);
61368
+ log20.debug(`Re-pinned post`);
61160
61369
  } catch (pinErr) {
61161
- log19.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
61370
+ log20.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
61162
61371
  }
61163
- log19.debug(`Updated successfully`);
61372
+ log20.debug(`Updated successfully`);
61164
61373
  return;
61165
61374
  } catch (err) {
61166
- log19.debug(`Update failed, will create new: ${err}`);
61375
+ log20.debug(`Update failed, will create new: ${err}`);
61167
61376
  }
61168
61377
  }
61169
61378
  needsBump.set(platform.platformId, false);
61170
61379
  if (existingPostId) {
61171
- log19.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
61380
+ log20.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
61172
61381
  try {
61173
61382
  await platform.unpinPost(existingPostId);
61174
- log19.debug(`Unpinned successfully`);
61383
+ log20.debug(`Unpinned successfully`);
61175
61384
  } catch (err) {
61176
- log19.debug(`Unpin failed (probably already unpinned): ${err}`);
61385
+ log20.debug(`Unpin failed (probably already unpinned): ${err}`);
61177
61386
  }
61178
61387
  try {
61179
61388
  await platform.deletePost(existingPostId);
61180
- log19.debug(`Deleted successfully`);
61389
+ log20.debug(`Deleted successfully`);
61181
61390
  } catch (err) {
61182
- log19.debug(`Delete failed (probably already deleted): ${err}`);
61391
+ log20.debug(`Delete failed (probably already deleted): ${err}`);
61183
61392
  }
61184
61393
  stickyPostIds.delete(platform.platformId);
61185
61394
  }
61186
- log19.debug(`Creating new post...`);
61395
+ log20.debug(`Creating new post...`);
61187
61396
  const post2 = await platform.createPost(content);
61188
61397
  stickyPostIds.set(platform.platformId, post2.id);
61189
61398
  try {
61190
61399
  await platform.pinPost(post2.id);
61191
- log19.debug(`Pinned post successfully`);
61400
+ log20.debug(`Pinned post successfully`);
61192
61401
  } catch (err) {
61193
- log19.debug(`Failed to pin post: ${err}`);
61402
+ log20.debug(`Failed to pin post: ${err}`);
61194
61403
  }
61195
61404
  if (sessionStore) {
61196
61405
  sessionStore.saveStickyPostId(platform.platformId, post2.id);
61197
61406
  }
61198
- log19.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
61407
+ log20.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
61199
61408
  const excludePostIds = new Set;
61200
61409
  if (sessionStore) {
61201
61410
  for (const session of sessionStore.load().values()) {
@@ -61211,10 +61420,10 @@ async function updateStickyMessageImpl(platform, sessions, config) {
61211
61420
  }
61212
61421
  const botUser = await platform.getBotUser();
61213
61422
  cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
61214
- log19.debug(`Background cleanup failed: ${err}`);
61423
+ log20.debug(`Background cleanup failed: ${err}`);
61215
61424
  });
61216
61425
  } catch (err) {
61217
- log19.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
61426
+ log20.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
61218
61427
  }
61219
61428
  }
61220
61429
  async function updateAllStickyMessages(platforms, sessions, config) {
@@ -61239,7 +61448,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
61239
61448
  if (!forceRun) {
61240
61449
  const lastRun = lastCleanupTime.get(platformId) || 0;
61241
61450
  if (now - lastRun < CLEANUP_THROTTLE_MS) {
61242
- log19.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
61451
+ log20.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
61243
61452
  return;
61244
61453
  }
61245
61454
  }
@@ -61249,31 +61458,31 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
61249
61458
  const pinnedPostIds = await platform.getPinnedPosts();
61250
61459
  const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
61251
61460
  if (recentPinnedIds.length === 0) {
61252
- log19.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
61461
+ log20.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
61253
61462
  return;
61254
61463
  }
61255
- log19.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
61464
+ log20.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
61256
61465
  for (const postId of recentPinnedIds) {
61257
61466
  try {
61258
61467
  const post2 = await platform.getPost(postId);
61259
61468
  if (!post2)
61260
61469
  continue;
61261
61470
  if (post2.userId === botUserId) {
61262
- log19.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
61471
+ log20.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
61263
61472
  try {
61264
61473
  await platform.unpinPost(postId);
61265
61474
  await platform.deletePost(postId);
61266
- log19.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
61475
+ log20.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
61267
61476
  } catch (err) {
61268
- log19.debug(`Failed to cleanup ${postId}: ${err}`);
61477
+ log20.debug(`Failed to cleanup ${postId}: ${err}`);
61269
61478
  }
61270
61479
  }
61271
61480
  } catch (err) {
61272
- log19.debug(`Could not check post ${postId}: ${err}`);
61481
+ log20.debug(`Could not check post ${postId}: ${err}`);
61273
61482
  }
61274
61483
  }
61275
61484
  } catch (err) {
61276
- log19.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
61485
+ log20.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
61277
61486
  }
61278
61487
  }
61279
61488
  // src/operations/bug-report/handler.ts
@@ -65336,8 +65545,16 @@ function getUpdateInfo() {
65336
65545
  init_emoji();
65337
65546
  init_logger();
65338
65547
  init_worktree();
65339
- var log20 = createLogger("commands");
65340
- var sessionLog2 = createSessionLog(log20);
65548
+ var log21 = createLogger("commands");
65549
+ var sessionLog2 = createSessionLog(log21);
65550
+ function sessionAccountOption(session, ctx) {
65551
+ if (!session.claudeAccountId)
65552
+ return;
65553
+ const account = ctx.ops.getClaudeAccount(session.claudeAccountId);
65554
+ if (!account)
65555
+ return;
65556
+ return { id: account.id, home: account.home, apiKey: account.apiKey };
65557
+ }
65341
65558
  async function restartClaudeSession(session, cliOptions, ctx, actionName) {
65342
65559
  ctx.ops.stopTyping(session);
65343
65560
  transitionTo(session, "restarting");
@@ -65346,6 +65563,7 @@ async function restartClaudeSession(session, cliOptions, ctx, actionName) {
65346
65563
  session.claude = new ClaudeCli(cliOptions);
65347
65564
  session.claude.on("event", (e) => ctx.ops.handleEvent(session.sessionId, e));
65348
65565
  session.claude.on("exit", (code) => ctx.ops.handleExit(session.sessionId, code));
65566
+ session.claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
65349
65567
  try {
65350
65568
  session.claude.start();
65351
65569
  return true;
@@ -65498,7 +65716,8 @@ ${CHAT_PLATFORM_PROMPT}`;
65498
65716
  platformConfig: session.platform.getMcpConfig(),
65499
65717
  appendSystemPrompt,
65500
65718
  logSessionId: session.sessionId,
65501
- permissionTimeoutMs: ctx.config.permissionTimeoutMs
65719
+ permissionTimeoutMs: ctx.config.permissionTimeoutMs,
65720
+ account: sessionAccountOption(session, ctx)
65502
65721
  };
65503
65722
  const success = await restartClaudeSession(session, cliOptions, ctx, "Restart Claude for directory change");
65504
65723
  if (!success)
@@ -65593,7 +65812,8 @@ async function enableInteractivePermissions(session, username, ctx) {
65593
65812
  chrome: ctx.config.chromeEnabled,
65594
65813
  platformConfig: session.platform.getMcpConfig(),
65595
65814
  logSessionId: session.sessionId,
65596
- permissionTimeoutMs: ctx.config.permissionTimeoutMs
65815
+ permissionTimeoutMs: ctx.config.permissionTimeoutMs,
65816
+ account: sessionAccountOption(session, ctx)
65597
65817
  };
65598
65818
  const success = await restartClaudeSession(session, cliOptions, ctx, "Enable interactive permissions");
65599
65819
  if (!success)
@@ -65699,6 +65919,11 @@ async function updateSessionHeader(session, ctx) {
65699
65919
  if (otherParticipants) {
65700
65920
  items.push(["\uD83D\uDC65", "Participants", otherParticipants]);
65701
65921
  }
65922
+ if (session.claudeAccountId) {
65923
+ const account = ctx.ops.getClaudeAccount(session.claudeAccountId);
65924
+ const label = account?.displayName ?? session.claudeAccountId;
65925
+ items.push(["\uD83D\uDD11", "Claude account", formatter.formatCode(label)]);
65926
+ }
65702
65927
  items.push(["\uD83C\uDD94", "Session ID", formatter.formatCode(session.claudeSessionId.substring(0, 8))]);
65703
65928
  const logPath = getLogFilePath(session.platform.platformId, session.claudeSessionId);
65704
65929
  const shortLogPath = logPath.replace(process.env.HOME || "", "~");
@@ -65855,7 +66080,7 @@ init_logger();
65855
66080
  import { exec as exec3 } from "child_process";
65856
66081
  import { promisify as promisify3 } from "util";
65857
66082
  var execAsync2 = promisify3(exec3);
65858
- var log21 = createLogger("branch");
66083
+ var log22 = createLogger("branch");
65859
66084
  var SUGGESTION_TIMEOUT3 = 15000;
65860
66085
  var MAX_SUGGESTIONS = 3;
65861
66086
  async function getCurrentBranch3(workingDir) {
@@ -65904,7 +66129,7 @@ function parseBranchSuggestions(response) {
65904
66129
  return lines.slice(0, MAX_SUGGESTIONS);
65905
66130
  }
65906
66131
  async function suggestBranchNames(workingDir, userMessage) {
65907
- log21.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
66132
+ log22.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
65908
66133
  try {
65909
66134
  const [currentBranch, recentCommits] = await Promise.all([
65910
66135
  getCurrentBranch3(workingDir),
@@ -65918,14 +66143,14 @@ async function suggestBranchNames(workingDir, userMessage) {
65918
66143
  workingDir
65919
66144
  });
65920
66145
  if (!result.success || !result.response) {
65921
- log21.debug(`Branch suggestion failed: ${result.error || "no response"}`);
66146
+ log22.debug(`Branch suggestion failed: ${result.error || "no response"}`);
65922
66147
  return [];
65923
66148
  }
65924
66149
  const suggestions = parseBranchSuggestions(result.response);
65925
- log21.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
66150
+ log22.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
65926
66151
  return suggestions;
65927
66152
  } catch (err) {
65928
- log21.debug(`Branch suggestion error: ${err}`);
66153
+ log22.debug(`Branch suggestion error: ${err}`);
65929
66154
  return [];
65930
66155
  }
65931
66156
  }
@@ -65934,8 +66159,8 @@ async function suggestBranchNames(workingDir, userMessage) {
65934
66159
  init_worktree();
65935
66160
  import { randomUUID as randomUUID3 } from "crypto";
65936
66161
  init_logger();
65937
- var log22 = createLogger("worktree");
65938
- var sessionLog3 = createSessionLog(log22);
66162
+ var log23 = createLogger("worktree");
66163
+ var sessionLog3 = createSessionLog(log23);
65939
66164
  function parseWorktreeError(error) {
65940
66165
  const message = error instanceof Error ? error.message : String(error);
65941
66166
  const lowerMessage = message.toLowerCase();
@@ -66294,9 +66519,10 @@ ${fmt.formatItalic("Claude Code restarted in the new worktree")}`);
66294
66519
  const contextPrefix = options2.formatContextForClaude(threadMessages, workSummary);
66295
66520
  const messageToSend = contextPrefix + session.firstPrompt;
66296
66521
  session.messageCount++;
66297
- const content = await options2.buildMessageContent(messageToSend, session, undefined);
66522
+ const { content, skipped } = await options2.buildMessageContent(messageToSend, session, undefined);
66298
66523
  session.claude.sendMessage(content);
66299
66524
  options2.startTyping(session);
66525
+ await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
66300
66526
  sessionLog3(session).debug(`\uD83C\uDF3F Auto-included ${threadMessages.length} messages + work summary for mid-session worktree`);
66301
66527
  }
66302
66528
  session.worktreeResponsePostId = undefined;
@@ -66485,8 +66711,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
66485
66711
  }
66486
66712
  // src/operations/events/handler.ts
66487
66713
  init_logger();
66488
- var log23 = createLogger("events");
66489
- var sessionLog4 = createSessionLog(log23);
66714
+ var log24 = createLogger("events");
66715
+ var sessionLog4 = createSessionLog(log24);
66490
66716
  function detectAndExecuteClaudeCommands(text, session, ctx) {
66491
66717
  const parsed = parseClaudeCommand(text);
66492
66718
  if (parsed && isClaudeAllowedCommand(parsed.command)) {
@@ -66734,8 +66960,8 @@ function createSessionContext(config, state, ops) {
66734
66960
  // src/operations/context-prompt/handler.ts
66735
66961
  init_emoji();
66736
66962
  init_logger();
66737
- var log24 = createLogger("context");
66738
- var sessionLog5 = createSessionLog(log24);
66963
+ var log25 = createLogger("context");
66964
+ var sessionLog5 = createSessionLog(log25);
66739
66965
  var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
66740
66966
  var CONTEXT_OPTIONS = [3, 5, 10];
66741
66967
  var contextPromptTimeouts = new Map;
@@ -66899,11 +67125,12 @@ async function handleContextPromptTimeout(session, ctx) {
66899
67125
  }
66900
67126
  session.messageCount++;
66901
67127
  const messageToSend = ctx.injectMetadataReminder(queuedPrompt, session);
66902
- const content = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
67128
+ const { content, skipped } = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
66903
67129
  if (session.claude.isRunning()) {
66904
67130
  session.claude.sendMessage(content);
66905
67131
  ctx.startTyping(session);
66906
67132
  }
67133
+ await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
66907
67134
  ctx.persistSession(session);
66908
67135
  sessionLog5(session).debug(`\uD83E\uDDF5 Context prompt timed out, continuing without thread context`);
66909
67136
  }
@@ -66920,11 +67147,12 @@ async function offerContextPrompt(session, queuedPrompt, queuedFiles, ctx, exclu
66920
67147
  sessionLog5(session).debug(`\uD83E\uDDF5 Including work summary (no thread messages)`);
66921
67148
  }
66922
67149
  messageToSend = ctx.injectMetadataReminder(messageToSend, session);
66923
- const content = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
67150
+ const { content, skipped } = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
66924
67151
  if (session.claude.isRunning()) {
66925
67152
  session.claude.sendMessage(content);
66926
67153
  ctx.startTyping(session);
66927
67154
  }
67155
+ await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
66928
67156
  return false;
66929
67157
  }
66930
67158
  if (messageCount === 1) {
@@ -66938,11 +67166,12 @@ async function offerContextPrompt(session, queuedPrompt, queuedFiles, ctx, exclu
66938
67166
  }
66939
67167
  session.messageCount++;
66940
67168
  messageToSend = ctx.injectMetadataReminder(messageToSend, session);
66941
- const content = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
67169
+ const { content, skipped } = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
66942
67170
  if (session.claude.isRunning()) {
66943
67171
  session.claude.sendMessage(content);
66944
67172
  ctx.startTyping(session);
66945
67173
  }
67174
+ await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
66946
67175
  sessionLog5(session).debug(`\uD83E\uDDF5 Auto-included 1 message as context (thread starter)${previousWorkSummary ? " + work summary" : ""}`);
66947
67176
  return false;
66948
67177
  }
@@ -66982,8 +67211,8 @@ function formatRelativeTime(date) {
66982
67211
  }
66983
67212
  // src/session/lifecycle.ts
66984
67213
  init_worktree();
66985
- var log25 = createLogger("lifecycle");
66986
- var sessionLog6 = createSessionLog(log25);
67214
+ var log26 = createLogger("lifecycle");
67215
+ var sessionLog6 = createSessionLog(log26);
66987
67216
  function mutableSessions(ctx) {
66988
67217
  return ctx.state.sessions;
66989
67218
  }
@@ -67031,12 +67260,31 @@ async function cleanupSession(session, ctx, options2 = {}) {
67031
67260
  cleanupPostIndex(ctx, session.threadId);
67032
67261
  }
67033
67262
  keepAlive.sessionEnded();
67263
+ releaseAccountIfHeld(session, ctx);
67264
+ }
67265
+ function releaseAccountIfHeld(session, ctx) {
67266
+ if (session.claudeAccountId) {
67267
+ ctx.ops.releaseClaudeAccount(session.claudeAccountId);
67268
+ session.claudeAccountId = undefined;
67269
+ }
67034
67270
  }
67035
67271
  function removeFromRegistry(session, ctx) {
67036
67272
  ctx.ops.emitSessionRemove(session.sessionId);
67037
67273
  mutableSessions(ctx).delete(session.sessionId);
67038
67274
  cleanupPostIndex(ctx, session.threadId);
67039
67275
  keepAlive.sessionEnded();
67276
+ releaseAccountIfHeld(session, ctx);
67277
+ }
67278
+ function handleRateLimit(session, hit, ctx) {
67279
+ if (!session.claudeAccountId) {
67280
+ sessionLog6(session).warn(`Rate limit hit in single-account mode — cannot reroute`);
67281
+ return;
67282
+ }
67283
+ const deadline = cooldownDeadline(hit);
67284
+ ctx.ops.markClaudeAccountCooling(session.claudeAccountId, deadline);
67285
+ const minutes = Math.max(1, Math.ceil((deadline - Date.now()) / 60000));
67286
+ sessionLog6(session).warn(`Rate limit on account "${session.claudeAccountId}" — cooling for ~${minutes}min`);
67287
+ post(session, "warning", `⚠️ Claude account \`${session.claudeAccountId}\` hit a rate limit. ` + `New sessions will use another account until it resets (~${minutes}min).`);
67040
67288
  }
67041
67289
  function findPersistedByThreadId(persisted, threadId) {
67042
67290
  for (const session of persisted.values()) {
@@ -67119,7 +67367,7 @@ function createMessageManager(session, ctx) {
67119
67367
  }
67120
67368
  session.messageCount++;
67121
67369
  messageToSend = maybeInjectMetadataReminder(messageToSend, session, ctx, session);
67122
- const content = await ctx.ops.buildMessageContent(messageToSend, session.platform, undefined);
67370
+ const { content } = await ctx.ops.buildMessageContent(messageToSend, session.platform, undefined);
67123
67371
  if (session.claude.isRunning()) {
67124
67372
  session.claude.sendMessage(content);
67125
67373
  ctx.ops.startTyping(session);
@@ -67336,18 +67584,22 @@ async function startSession(options2, username, displayName, replyToPostId, plat
67336
67584
  return;
67337
67585
  }
67338
67586
  workingDir = resolvedDir;
67339
- log25.info(`Starting session in directory: ${workingDir} (from !cd command)`);
67587
+ log26.info(`Starting session in directory: ${workingDir} (from !cd command)`);
67340
67588
  }
67341
67589
  if (initialOptions?.forceInteractivePermissions) {
67342
67590
  forceInteractivePermissions = true;
67343
67591
  skipPermissions = false;
67344
- log25.info(`Starting session with interactive permissions (from !permissions command)`);
67592
+ log26.info(`Starting session with interactive permissions (from !permissions command)`);
67345
67593
  }
67346
67594
  const sessionContext = buildSessionContext(platform, workingDir);
67347
67595
  const systemPrompt = `${sessionContext}
67348
67596
 
67349
67597
  ${CHAT_PLATFORM_PROMPT}`;
67350
67598
  const platformMcpConfig = platform.getMcpConfig();
67599
+ const claudeAccount = ctx.ops.acquireClaudeAccount();
67600
+ if (claudeAccount) {
67601
+ log26.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
67602
+ }
67351
67603
  const cliOptions = {
67352
67604
  workingDir,
67353
67605
  threadId: actualThreadId,
@@ -67358,7 +67610,8 @@ ${CHAT_PLATFORM_PROMPT}`;
67358
67610
  platformConfig: platformMcpConfig,
67359
67611
  appendSystemPrompt: systemPrompt,
67360
67612
  logSessionId: sessionId,
67361
- permissionTimeoutMs: ctx.config.permissionTimeoutMs
67613
+ permissionTimeoutMs: ctx.config.permissionTimeoutMs,
67614
+ account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined
67362
67615
  };
67363
67616
  const claude = new ClaudeCli(cliOptions);
67364
67617
  const session = {
@@ -67367,6 +67620,7 @@ ${CHAT_PLATFORM_PROMPT}`;
67367
67620
  sessionId,
67368
67621
  platform,
67369
67622
  claudeSessionId,
67623
+ claudeAccountId: claudeAccount?.id,
67370
67624
  startedBy: username,
67371
67625
  startedByDisplayName: displayName,
67372
67626
  startedAt: new Date,
@@ -67405,6 +67659,7 @@ ${CHAT_PLATFORM_PROMPT}`;
67405
67659
  ctx.ops.startTyping(session);
67406
67660
  claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
67407
67661
  claude.on("exit", (code) => ctx.ops.handleExit(sessionId, code));
67662
+ claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
67408
67663
  try {
67409
67664
  claude.start();
67410
67665
  } catch (err) {
@@ -67412,6 +67667,7 @@ ${CHAT_PLATFORM_PROMPT}`;
67412
67667
  ctx.ops.stopTyping(session);
67413
67668
  ctx.ops.emitSessionRemove(session.sessionId);
67414
67669
  mutableSessions(ctx).delete(session.sessionId);
67670
+ releaseAccountIfHeld(session, ctx);
67415
67671
  await ctx.ops.updateStickyMessage();
67416
67672
  return;
67417
67673
  }
@@ -67425,17 +67681,19 @@ ${CHAT_PLATFORM_PROMPT}`;
67425
67681
  await ctx.ops.updateStickyMessage();
67426
67682
  return;
67427
67683
  }
67428
- const content = await ctx.ops.buildMessageContent(options2.prompt, session.platform, options2.files);
67684
+ const { content, skipped } = await ctx.ops.buildMessageContent(options2.prompt, session.platform, options2.files);
67429
67685
  const messageText = typeof content === "string" ? content : options2.prompt;
67430
67686
  if (replyToPostId) {
67431
67687
  const excludePostId = triggeringPostId || replyToPostId;
67432
67688
  const contextOffered = await ctx.ops.offerContextPrompt(session, messageText, options2.files, excludePostId);
67433
67689
  if (contextOffered) {
67690
+ await postSkippedFilesFeedback(session.platform, actualThreadId, skipped);
67434
67691
  return;
67435
67692
  }
67436
67693
  }
67437
67694
  session.messageCount++;
67438
67695
  claude.sendMessage(content);
67696
+ await postSkippedFilesFeedback(session.platform, actualThreadId, skipped);
67439
67697
  }
67440
67698
  async function resumeSession(state, ctx) {
67441
67699
  if (!state.threadId || !state.platformId || !state.claudeSessionId || !state.workingDir) {
@@ -67445,28 +67703,28 @@ async function resumeSession(state, ctx) {
67445
67703
  !state.claudeSessionId && "claudeSessionId",
67446
67704
  !state.workingDir && "workingDir"
67447
67705
  ].filter(Boolean).join(", ");
67448
- log25.warn(`Skipping session with missing required fields: ${missing}`);
67706
+ log26.warn(`Skipping session with missing required fields: ${missing}`);
67449
67707
  return;
67450
67708
  }
67451
67709
  const shortId = state.threadId.substring(0, 8);
67452
67710
  const platforms = ctx.state.platforms;
67453
67711
  const platform = platforms.get(state.platformId);
67454
67712
  if (!platform) {
67455
- log25.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
67713
+ log26.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
67456
67714
  return;
67457
67715
  }
67458
67716
  const threadPost = await platform.getPost(state.threadId);
67459
67717
  if (!threadPost) {
67460
- log25.warn(`Thread ${shortId}... deleted, skipping resume`);
67718
+ log26.warn(`Thread ${shortId}... deleted, skipping resume`);
67461
67719
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
67462
67720
  return;
67463
67721
  }
67464
67722
  if (ctx.state.sessions.size >= ctx.config.maxSessions) {
67465
- log25.warn(`Max sessions reached, skipping resume for ${shortId}...`);
67723
+ log26.warn(`Max sessions reached, skipping resume for ${shortId}...`);
67466
67724
  return;
67467
67725
  }
67468
67726
  if (!existsSync11(state.workingDir)) {
67469
- log25.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
67727
+ log26.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
67470
67728
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
67471
67729
  const resumeFormatter = platform.getFormatter();
67472
67730
  const tempSession = {
@@ -67488,6 +67746,10 @@ Please start a new session.`), { action: "Post resume failure notification" });
67488
67746
  const appendSystemPrompt = `${sessionContext}
67489
67747
 
67490
67748
  ${CHAT_PLATFORM_PROMPT}`;
67749
+ const claudeAccount = ctx.ops.acquireClaudeAccount(state.claudeAccountId);
67750
+ if (state.claudeAccountId && !claudeAccount) {
67751
+ log26.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
67752
+ }
67491
67753
  const cliOptions = {
67492
67754
  workingDir: state.workingDir,
67493
67755
  threadId: state.threadId,
@@ -67498,7 +67760,8 @@ ${CHAT_PLATFORM_PROMPT}`;
67498
67760
  platformConfig: platformMcpConfig,
67499
67761
  appendSystemPrompt,
67500
67762
  logSessionId: sessionId,
67501
- permissionTimeoutMs: ctx.config.permissionTimeoutMs
67763
+ permissionTimeoutMs: ctx.config.permissionTimeoutMs,
67764
+ account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined
67502
67765
  };
67503
67766
  const claude = new ClaudeCli(cliOptions);
67504
67767
  const session = {
@@ -67507,6 +67770,7 @@ ${CHAT_PLATFORM_PROMPT}`;
67507
67770
  sessionId,
67508
67771
  platform,
67509
67772
  claudeSessionId: state.claudeSessionId,
67773
+ claudeAccountId: claudeAccount?.id,
67510
67774
  startedBy: state.startedBy,
67511
67775
  startedByDisplayName: state.startedByDisplayName,
67512
67776
  startedAt: new Date(state.startedAt),
@@ -67549,7 +67813,7 @@ ${CHAT_PLATFORM_PROMPT}`;
67549
67813
  worktreePath: detected.worktreePath,
67550
67814
  branch: detected.branch
67551
67815
  };
67552
- log25.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
67816
+ log26.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
67553
67817
  }
67554
67818
  }
67555
67819
  session.messageManager = createMessageManager(session, ctx);
@@ -67584,6 +67848,7 @@ ${CHAT_PLATFORM_PROMPT}`;
67584
67848
  keepAlive.sessionStarted();
67585
67849
  claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
67586
67850
  claude.on("exit", (code) => ctx.ops.handleExit(sessionId, code));
67851
+ claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
67587
67852
  try {
67588
67853
  claude.start();
67589
67854
  sessionLog6(session).info(`\uD83D\uDD04 Session resumed (@${state.startedBy})`);
@@ -67604,10 +67869,11 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
67604
67869
  await ctx.ops.updateStickyMessage();
67605
67870
  ctx.ops.persistSession(session);
67606
67871
  } catch (err) {
67607
- log25.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
67872
+ log26.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
67608
67873
  ctx.ops.emitSessionRemove(sessionId);
67609
67874
  mutableSessions(ctx).delete(sessionId);
67610
67875
  ctx.state.sessionStore.remove(sessionId);
67876
+ releaseAccountIfHeld(session, ctx);
67611
67877
  const failFormatter = session.platform.getFormatter();
67612
67878
  await withErrorHandling(() => post(session, "warning", `${failFormatter.formatBold("Could not resume previous session.")} Starting fresh.
67613
67879
  ${failFormatter.formatItalic("Your previous conversation context is preserved, but Claude needs to re-read it.")}`), { action: "Post resume failure notification", session });
@@ -67620,9 +67886,7 @@ async function sendFollowUp(session, message, files, ctx, username, displayName)
67620
67886
  if (session.needsContextPromptOnNextMessage) {
67621
67887
  session.needsContextPromptOnNextMessage = false;
67622
67888
  await session.messageManager?.prepareForUserMessage();
67623
- const content = await ctx.ops.buildMessageContent(message, session.platform, files);
67624
- const messageText = typeof content === "string" ? content : message;
67625
- const contextOffered = await ctx.ops.offerContextPrompt(session, messageText, files);
67889
+ const contextOffered = await ctx.ops.offerContextPrompt(session, message, files);
67626
67890
  if (contextOffered) {
67627
67891
  session.lastActivityAt = new Date;
67628
67892
  return;
@@ -67645,18 +67909,18 @@ async function resumePausedSession(threadId, message, files, ctx) {
67645
67909
  const persisted = ctx.state.sessionStore.load();
67646
67910
  const state = findPersistedByThreadId(persisted, threadId);
67647
67911
  if (!state) {
67648
- log25.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
67912
+ log26.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
67649
67913
  return;
67650
67914
  }
67651
67915
  const shortId = threadId.substring(0, 8);
67652
- log25.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
67916
+ log26.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
67653
67917
  await resumeSession(state, ctx);
67654
67918
  const session = ctx.ops.findSessionByThreadId(threadId);
67655
67919
  if (session && session.claude.isRunning() && session.messageManager) {
67656
67920
  session.messageCount++;
67657
67921
  await session.messageManager.handleUserMessage(message, files, state.startedBy);
67658
67922
  } else {
67659
- log25.warn(`Failed to resume session ${shortId}..., could not send message`);
67923
+ log26.warn(`Failed to resume session ${shortId}..., could not send message`);
67660
67924
  }
67661
67925
  }
67662
67926
  async function handleExit(sessionId, code, ctx) {
@@ -67664,7 +67928,7 @@ async function handleExit(sessionId, code, ctx) {
67664
67928
  const shortId = sessionId.substring(0, 8);
67665
67929
  sessionLog6(session).debug(`handleExit called code=${code} isShuttingDown=${ctx.state.isShuttingDown}`);
67666
67930
  if (!session) {
67667
- log25.debug(`Session ${shortId}... not found (already cleaned up)`);
67931
+ log26.debug(`Session ${shortId}... not found (already cleaned up)`);
67668
67932
  return;
67669
67933
  }
67670
67934
  if (isSessionRestarting(session)) {
@@ -67857,7 +68121,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
67857
68121
  }
67858
68122
 
67859
68123
  // src/operations/monitor/handler.ts
67860
- var log26 = createLogger("monitor");
68124
+ var log27 = createLogger("monitor");
67861
68125
  var DEFAULT_INTERVAL_MS = 60 * 1000;
67862
68126
 
67863
68127
  class SessionMonitor {
@@ -67879,14 +68143,14 @@ class SessionMonitor {
67879
68143
  }
67880
68144
  start() {
67881
68145
  if (this.isRunning) {
67882
- log26.debug("Session monitor already running");
68146
+ log27.debug("Session monitor already running");
67883
68147
  return;
67884
68148
  }
67885
68149
  this.isRunning = true;
67886
- log26.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
68150
+ log27.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
67887
68151
  this.timer = setInterval(() => {
67888
68152
  this.runCheck().catch((err) => {
67889
- log26.error(`Error during session monitoring: ${err}`);
68153
+ log27.error(`Error during session monitoring: ${err}`);
67890
68154
  });
67891
68155
  }, this.intervalMs);
67892
68156
  }
@@ -67896,7 +68160,7 @@ class SessionMonitor {
67896
68160
  this.timer = null;
67897
68161
  }
67898
68162
  this.isRunning = false;
67899
- log26.debug("Session monitor stopped");
68163
+ log27.debug("Session monitor stopped");
67900
68164
  }
67901
68165
  async runCheck() {
67902
68166
  await cleanupIdleSessions(this.sessionTimeoutMs, this.sessionWarningMs, this.getContext());
@@ -67908,8 +68172,8 @@ class SessionMonitor {
67908
68172
  // src/operations/plugin/handler.ts
67909
68173
  init_spawn();
67910
68174
  init_logger();
67911
- var log27 = createLogger("plugin");
67912
- var sessionLog7 = createSessionLog(log27);
68175
+ var log28 = createLogger("plugin");
68176
+ var sessionLog7 = createSessionLog(log28);
67913
68177
  async function runPluginCommand(args, cwd, timeout2 = 60000) {
67914
68178
  return new Promise((resolve6) => {
67915
68179
  const claudePath = process.env.CLAUDE_PATH || "claude";
@@ -67930,7 +68194,7 @@ async function runPluginCommand(args, cwd, timeout2 = 60000) {
67930
68194
  });
67931
68195
  proc.on("error", (err) => {
67932
68196
  resolve6({ stdout, stderr, exitCode: 1 });
67933
- log27.error(`Plugin command error: ${err.message}`);
68197
+ log28.error(`Plugin command error: ${err.message}`);
67934
68198
  });
67935
68199
  });
67936
68200
  }
@@ -68129,7 +68393,7 @@ class SessionRegistry {
68129
68393
 
68130
68394
  // src/session/manager.ts
68131
68395
  init_logger();
68132
- var log28 = createLogger("manager");
68396
+ var log29 = createLogger("manager");
68133
68397
 
68134
68398
  class SessionManager extends EventEmitter4 {
68135
68399
  platforms = new Map;
@@ -68152,7 +68416,8 @@ class SessionManager extends EventEmitter4 {
68152
68416
  customDescription;
68153
68417
  customFooter;
68154
68418
  autoUpdateManager = null;
68155
- constructor(workingDir, skipPermissions = false, chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits) {
68419
+ accountPool;
68420
+ constructor(workingDir, skipPermissions = false, chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts) {
68156
68421
  super();
68157
68422
  this.workingDir = workingDir;
68158
68423
  this.skipPermissions = skipPermissions;
@@ -68163,6 +68428,7 @@ class SessionManager extends EventEmitter4 {
68163
68428
  this.limits = resolveLimits(limits);
68164
68429
  this.sessionStore = new SessionStore(sessionsPath);
68165
68430
  this.registry = new SessionRegistry(this.sessionStore);
68431
+ this.accountPool = new AccountPool(claudeAccounts);
68166
68432
  this.sessionMonitor = new SessionMonitor({
68167
68433
  sessionTimeoutMs: this.limits.sessionTimeoutMinutes * 60 * 1000,
68168
68434
  sessionWarningMs: this.limits.sessionWarningMinutes * 60 * 1000,
@@ -68196,7 +68462,7 @@ class SessionManager extends EventEmitter4 {
68196
68462
  markNeedsBump(platformId);
68197
68463
  this.updateStickyMessage();
68198
68464
  });
68199
- log28.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
68465
+ log29.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
68200
68466
  }
68201
68467
  removePlatform(platformId) {
68202
68468
  this.platforms.delete(platformId);
@@ -68212,7 +68478,7 @@ class SessionManager extends EventEmitter4 {
68212
68478
  if (users) {
68213
68479
  users.add(sessionId);
68214
68480
  }
68215
- log28.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
68481
+ log29.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
68216
68482
  }
68217
68483
  unregisterWorktreeUser(worktreePath, sessionId) {
68218
68484
  const users = this.worktreeUsers.get(worktreePath);
@@ -68278,7 +68544,12 @@ class SessionManager extends EventEmitter4 {
68278
68544
  offerContextPrompt: (s, q, f, e) => offerContextPrompt(s, q, f, this.getContextPromptHandler(), e),
68279
68545
  emitSessionAdd: (s) => this.emitSessionAdd(s),
68280
68546
  emitSessionUpdate: (sid, u) => this.emitSessionUpdate(sid, u),
68281
- emitSessionRemove: (sid) => this.emitSessionRemove(sid)
68547
+ emitSessionRemove: (sid) => this.emitSessionRemove(sid),
68548
+ acquireClaudeAccount: (preferredId) => this.accountPool.acquire(preferredId),
68549
+ getClaudeAccount: (id) => this.accountPool.get(id),
68550
+ releaseClaudeAccount: (id) => this.accountPool.release(id),
68551
+ markClaudeAccountCooling: (id, untilMs) => this.accountPool.markCooling(id, untilMs),
68552
+ getClaudeAccountPoolStatus: () => this.accountPool.status()
68282
68553
  };
68283
68554
  return createSessionContext(config, state, ops);
68284
68555
  }
@@ -68359,7 +68630,7 @@ class SessionManager extends EventEmitter4 {
68359
68630
  return false;
68360
68631
  }
68361
68632
  const shortId = persistedSession.threadId.substring(0, 8);
68362
- log28.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
68633
+ log29.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
68363
68634
  await resumeSession(persistedSession, this.getContext());
68364
68635
  return true;
68365
68636
  }
@@ -68389,7 +68660,7 @@ class SessionManager extends EventEmitter4 {
68389
68660
  }
68390
68661
  if (session.lastError?.postId === postId && isBugReportEmoji(emojiName)) {
68391
68662
  if (session.startedBy === username || session.platform.isUserAllowed(username) || session.sessionAllowedUsers.has(username)) {
68392
- log28.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
68663
+ log29.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
68393
68664
  await reportBug(session, undefined, username, this.getContext(), session.lastError);
68394
68665
  return;
68395
68666
  }
@@ -68505,7 +68776,8 @@ class SessionManager extends EventEmitter4 {
68505
68776
  sessionTags: session.sessionTags,
68506
68777
  pullRequestUrl: session.pullRequestUrl,
68507
68778
  messageCount: session.messageCount,
68508
- resumeFailCount: session.lifecycle.resumeFailCount
68779
+ resumeFailCount: session.lifecycle.resumeFailCount,
68780
+ claudeAccountId: session.claudeAccountId
68509
68781
  };
68510
68782
  this.sessionStore.save(session.sessionId, state);
68511
68783
  }
@@ -68530,7 +68802,8 @@ class SessionManager extends EventEmitter4 {
68530
68802
  workingDir: this.workingDir,
68531
68803
  debug: this.debug,
68532
68804
  description: this.customDescription,
68533
- footer: this.customFooter
68805
+ footer: this.customFooter,
68806
+ accountPoolStatus: this.accountPool.isEmpty ? undefined : this.accountPool.status()
68534
68807
  });
68535
68808
  }
68536
68809
  async updateAllStickyMessages() {
@@ -68555,11 +68828,11 @@ class SessionManager extends EventEmitter4 {
68555
68828
  }
68556
68829
  }
68557
68830
  if (sessionsToKill.length === 0) {
68558
- log28.info(`No active sessions to pause for platform ${platformId}`);
68831
+ log29.info(`No active sessions to pause for platform ${platformId}`);
68559
68832
  await this.updateStickyMessage();
68560
68833
  return;
68561
68834
  }
68562
- log28.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
68835
+ log29.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
68563
68836
  for (const session of sessionsToKill) {
68564
68837
  try {
68565
68838
  const fmt = session.platform.getFormatter();
@@ -68575,9 +68848,9 @@ class SessionManager extends EventEmitter4 {
68575
68848
  session.claude.kill();
68576
68849
  this.registry.unregister(session.sessionId);
68577
68850
  this.emitSessionRemove(session.sessionId);
68578
- log28.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
68851
+ log29.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
68579
68852
  } catch (err) {
68580
- log28.warn(`Failed to pause session ${session.threadId}: ${err}`);
68853
+ log29.warn(`Failed to pause session ${session.threadId}: ${err}`);
68581
68854
  }
68582
68855
  }
68583
68856
  for (const session of sessionsToKill) {
@@ -68598,17 +68871,17 @@ class SessionManager extends EventEmitter4 {
68598
68871
  sessionsToResume.push(state);
68599
68872
  }
68600
68873
  if (sessionsToResume.length === 0) {
68601
- log28.info(`No paused sessions to resume for platform ${platformId}`);
68874
+ log29.info(`No paused sessions to resume for platform ${platformId}`);
68602
68875
  await this.updateStickyMessage();
68603
68876
  return;
68604
68877
  }
68605
- log28.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
68878
+ log29.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
68606
68879
  for (const state of sessionsToResume) {
68607
68880
  try {
68608
68881
  await resumeSession(state, this.getContext());
68609
- log28.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
68882
+ log29.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
68610
68883
  } catch (err) {
68611
- log28.warn(`Failed to resume session ${state.threadId}: ${err}`);
68884
+ log29.warn(`Failed to resume session ${state.threadId}: ${err}`);
68612
68885
  }
68613
68886
  }
68614
68887
  await this.updateStickyMessage();
@@ -68620,14 +68893,14 @@ class SessionManager extends EventEmitter4 {
68620
68893
  const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
68621
68894
  const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
68622
68895
  if (staleIds.length > 0) {
68623
- log28.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
68896
+ log29.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
68624
68897
  }
68625
68898
  const removedCount = this.sessionStore.cleanHistory();
68626
68899
  if (removedCount > 0) {
68627
- log28.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
68900
+ log29.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
68628
68901
  }
68629
68902
  const persisted = this.sessionStore.load();
68630
- log28.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
68903
+ log29.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
68631
68904
  const excludePostIdsByPlatform = new Map;
68632
68905
  for (const session of persisted.values()) {
68633
68906
  const platformId = session.platformId;
@@ -68647,10 +68920,10 @@ class SessionManager extends EventEmitter4 {
68647
68920
  const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
68648
68921
  platform.getBotUser().then((botUser) => {
68649
68922
  cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
68650
- log28.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
68923
+ log29.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
68651
68924
  });
68652
68925
  }).catch((err) => {
68653
- log28.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
68926
+ log29.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
68654
68927
  });
68655
68928
  }
68656
68929
  if (persisted.size > 0) {
@@ -68664,10 +68937,10 @@ class SessionManager extends EventEmitter4 {
68664
68937
  }
68665
68938
  }
68666
68939
  if (pausedToSkip.length > 0) {
68667
- log28.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
68940
+ log29.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
68668
68941
  }
68669
68942
  if (activeToResume.length > 0) {
68670
- log28.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
68943
+ log29.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
68671
68944
  for (const state of activeToResume) {
68672
68945
  await resumeSession(state, this.getContext());
68673
68946
  }
@@ -69086,7 +69359,7 @@ Mention me to start a session in this worktree.`, threadId);
69086
69359
  const message = messageBuilder(formatter);
69087
69360
  await post(session, "info", message);
69088
69361
  } catch (err) {
69089
- log28.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
69362
+ log29.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
69090
69363
  }
69091
69364
  }
69092
69365
  }
@@ -69105,7 +69378,7 @@ Mention me to start a session in this worktree.`, threadId);
69105
69378
  session.messageManager?.setPendingUpdatePrompt({ postId: post2.id });
69106
69379
  this.registerPost(post2.id, session.threadId);
69107
69380
  } catch (err) {
69108
- log28.warn(`Failed to post ask message to ${threadId}: ${err}`);
69381
+ log29.warn(`Failed to post ask message to ${threadId}: ${err}`);
69109
69382
  }
69110
69383
  }
69111
69384
  }
@@ -76700,29 +76973,29 @@ function SessionLog({ logs, maxLines = 20 }) {
76700
76973
  return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
76701
76974
  flexDirection: "column",
76702
76975
  flexShrink: 0,
76703
- children: displayLogs.map((log29) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
76976
+ children: displayLogs.map((log30) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
76704
76977
  flexShrink: 0,
76705
76978
  children: [
76706
76979
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
76707
- color: getColorForLevel(log29.level),
76980
+ color: getColorForLevel(log30.level),
76708
76981
  dimColor: true,
76709
76982
  wrap: "truncate",
76710
76983
  children: [
76711
76984
  "[",
76712
- padComponent(log29.component),
76985
+ padComponent(log30.component),
76713
76986
  "]"
76714
76987
  ]
76715
76988
  }, undefined, true, undefined, this),
76716
76989
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
76717
- color: getColorForLevel(log29.level),
76990
+ color: getColorForLevel(log30.level),
76718
76991
  wrap: "truncate",
76719
76992
  children: [
76720
76993
  " ",
76721
- log29.message
76994
+ log30.message
76722
76995
  ]
76723
76996
  }, undefined, true, undefined, this)
76724
76997
  ]
76725
- }, log29.id, true, undefined, this))
76998
+ }, log30.id, true, undefined, this))
76726
76999
  }, undefined, false, undefined, this);
76727
77000
  }
76728
77001
  // src/ui/components/Footer.tsx
@@ -77220,7 +77493,7 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
77220
77493
  const scrollRef = import_react59.default.useRef(null);
77221
77494
  const { stdout } = use_stdout_default();
77222
77495
  const isDebug = process.env.DEBUG === "1";
77223
- const displayLogs = logs.filter((log29) => isDebug || log29.level !== "debug");
77496
+ const displayLogs = logs.filter((log30) => isDebug || log30.level !== "debug");
77224
77497
  const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
77225
77498
  import_react59.default.useEffect(() => {
77226
77499
  const handleResize = () => scrollRef.current?.remeasure();
@@ -77260,25 +77533,25 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
77260
77533
  overflow: "hidden",
77261
77534
  children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ScrollView, {
77262
77535
  ref: scrollRef,
77263
- children: visibleLogs.map((log29) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
77536
+ children: visibleLogs.map((log30) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
77264
77537
  children: [
77265
77538
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
77266
77539
  dimColor: true,
77267
77540
  children: [
77268
77541
  "[",
77269
- padComponent2(log29.component),
77542
+ padComponent2(log30.component),
77270
77543
  "]"
77271
77544
  ]
77272
77545
  }, undefined, true, undefined, this),
77273
77546
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
77274
- color: getLevelColor(log29.level),
77547
+ color: getLevelColor(log30.level),
77275
77548
  children: [
77276
77549
  " ",
77277
- log29.message
77550
+ log30.message
77278
77551
  ]
77279
77552
  }, undefined, true, undefined, this)
77280
77553
  ]
77281
- }, log29.id, true, undefined, this))
77554
+ }, log30.id, true, undefined, this))
77282
77555
  }, undefined, false, undefined, this)
77283
77556
  }, undefined, false, undefined, this);
77284
77557
  }
@@ -77795,10 +78068,10 @@ function useAppState(initialConfig) {
77795
78068
  });
77796
78069
  }, []);
77797
78070
  const getLogsForSession = import_react60.useCallback((sessionId) => {
77798
- return state.logs.filter((log29) => log29.sessionId === sessionId);
78071
+ return state.logs.filter((log30) => log30.sessionId === sessionId);
77799
78072
  }, [state.logs]);
77800
78073
  const getGlobalLogs = import_react60.useCallback(() => {
77801
- return state.logs.filter((log29) => !log29.sessionId);
78074
+ return state.logs.filter((log30) => !log30.sessionId);
77802
78075
  }, [state.logs]);
77803
78076
  const togglePlatformEnabled = import_react60.useCallback((platformId) => {
77804
78077
  let newEnabled = false;
@@ -78792,7 +79065,7 @@ import { EventEmitter as EventEmitter9 } from "events";
78792
79065
  // src/auto-update/checker.ts
78793
79066
  init_logger();
78794
79067
  import { EventEmitter as EventEmitter7 } from "events";
78795
- var log29 = createLogger("checker");
79068
+ var log30 = createLogger("checker");
78796
79069
  var PACKAGE_NAME = "claude-threads";
78797
79070
  function compareVersions(a, b) {
78798
79071
  const partsA = a.replace(/^v/, "").split(".").map(Number);
@@ -78815,13 +79088,13 @@ async function fetchLatestVersion() {
78815
79088
  }
78816
79089
  });
78817
79090
  if (!response.ok) {
78818
- log29.warn(`Failed to fetch latest version: HTTP ${response.status}`);
79091
+ log30.warn(`Failed to fetch latest version: HTTP ${response.status}`);
78819
79092
  return null;
78820
79093
  }
78821
79094
  const data = await response.json();
78822
79095
  return data.version ?? null;
78823
79096
  } catch (err) {
78824
- log29.warn(`Failed to fetch latest version: ${err}`);
79097
+ log30.warn(`Failed to fetch latest version: ${err}`);
78825
79098
  return null;
78826
79099
  }
78827
79100
  }
@@ -78838,38 +79111,38 @@ class UpdateChecker extends EventEmitter7 {
78838
79111
  }
78839
79112
  start() {
78840
79113
  if (!this.config.enabled) {
78841
- log29.debug("Auto-update disabled, not starting checker");
79114
+ log30.debug("Auto-update disabled, not starting checker");
78842
79115
  return;
78843
79116
  }
78844
79117
  setTimeout(() => {
78845
79118
  this.check().catch((err) => {
78846
- log29.warn(`Initial update check failed: ${err}`);
79119
+ log30.warn(`Initial update check failed: ${err}`);
78847
79120
  });
78848
79121
  }, 5000);
78849
79122
  const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
78850
79123
  this.checkInterval = setInterval(() => {
78851
79124
  this.check().catch((err) => {
78852
- log29.warn(`Periodic update check failed: ${err}`);
79125
+ log30.warn(`Periodic update check failed: ${err}`);
78853
79126
  });
78854
79127
  }, intervalMs);
78855
- log29.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
79128
+ log30.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
78856
79129
  }
78857
79130
  stop() {
78858
79131
  if (this.checkInterval) {
78859
79132
  clearInterval(this.checkInterval);
78860
79133
  this.checkInterval = null;
78861
79134
  }
78862
- log29.debug("Update checker stopped");
79135
+ log30.debug("Update checker stopped");
78863
79136
  }
78864
79137
  async check() {
78865
79138
  if (this.isChecking) {
78866
- log29.debug("Check already in progress, skipping");
79139
+ log30.debug("Check already in progress, skipping");
78867
79140
  return this.lastUpdateInfo;
78868
79141
  }
78869
79142
  this.isChecking = true;
78870
79143
  this.emit("check:start");
78871
79144
  try {
78872
- log29.debug("Checking for updates...");
79145
+ log30.debug("Checking for updates...");
78873
79146
  const latestVersion2 = await fetchLatestVersion();
78874
79147
  if (!latestVersion2) {
78875
79148
  this.emit("check:complete", false);
@@ -78886,18 +79159,18 @@ class UpdateChecker extends EventEmitter7 {
78886
79159
  detectedAt: new Date
78887
79160
  };
78888
79161
  if (!this.lastUpdateInfo || this.lastUpdateInfo.latestVersion !== latestVersion2) {
78889
- log29.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
79162
+ log30.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
78890
79163
  this.lastUpdateInfo = updateInfo;
78891
79164
  this.emit("update", updateInfo);
78892
79165
  }
78893
79166
  this.emit("check:complete", true);
78894
79167
  return updateInfo;
78895
79168
  }
78896
- log29.debug(`Up to date (v${currentVersion})`);
79169
+ log30.debug(`Up to date (v${currentVersion})`);
78897
79170
  this.emit("check:complete", false);
78898
79171
  return null;
78899
79172
  } catch (err) {
78900
- log29.warn(`Update check failed: ${err}`);
79173
+ log30.warn(`Update check failed: ${err}`);
78901
79174
  this.emit("check:error", err);
78902
79175
  return null;
78903
79176
  } finally {
@@ -78968,7 +79241,7 @@ function isInScheduledWindow(window2) {
78968
79241
  }
78969
79242
 
78970
79243
  // src/auto-update/scheduler.ts
78971
- var log30 = createLogger("scheduler");
79244
+ var log31 = createLogger("scheduler");
78972
79245
 
78973
79246
  class UpdateScheduler extends EventEmitter8 {
78974
79247
  config;
@@ -78992,7 +79265,7 @@ class UpdateScheduler extends EventEmitter8 {
78992
79265
  scheduleUpdate(updateInfo) {
78993
79266
  this.pendingUpdate = updateInfo;
78994
79267
  if (this.config.autoRestartMode === "immediate") {
78995
- log30.info("Immediate mode: triggering update now");
79268
+ log31.info("Immediate mode: triggering update now");
78996
79269
  this.emit("ready", updateInfo);
78997
79270
  return;
78998
79271
  }
@@ -79005,19 +79278,19 @@ class UpdateScheduler extends EventEmitter8 {
79005
79278
  this.scheduledRestartAt = null;
79006
79279
  this.askApprovals.clear();
79007
79280
  this.askStartTime = null;
79008
- log30.debug("Update schedule cancelled");
79281
+ log31.debug("Update schedule cancelled");
79009
79282
  }
79010
79283
  deferUpdate(minutes) {
79011
79284
  const deferUntil = new Date(Date.now() + minutes * 60 * 1000);
79012
79285
  this.scheduledRestartAt = null;
79013
79286
  this.idleStartTime = null;
79014
79287
  this.emit("deferred", deferUntil);
79015
- log30.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
79288
+ log31.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
79016
79289
  return deferUntil;
79017
79290
  }
79018
79291
  recordAskResponse(threadId, approved) {
79019
79292
  this.askApprovals.set(threadId, approved);
79020
- log30.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
79293
+ log31.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
79021
79294
  this.checkAskCondition();
79022
79295
  }
79023
79296
  getScheduledRestartAt() {
@@ -79038,7 +79311,7 @@ class UpdateScheduler extends EventEmitter8 {
79038
79311
  return;
79039
79312
  this.checkCondition();
79040
79313
  this.checkTimer = setInterval(() => this.checkCondition(), 1e4);
79041
- log30.debug(`Started checking for ${this.config.autoRestartMode} condition`);
79314
+ log31.debug(`Started checking for ${this.config.autoRestartMode} condition`);
79042
79315
  }
79043
79316
  stopChecking() {
79044
79317
  if (this.checkTimer) {
@@ -79069,17 +79342,17 @@ class UpdateScheduler extends EventEmitter8 {
79069
79342
  if (activity.activeSessionCount === 0) {
79070
79343
  if (!this.idleStartTime) {
79071
79344
  this.idleStartTime = new Date;
79072
- log30.debug("No active sessions, starting idle timer");
79345
+ log31.debug("No active sessions, starting idle timer");
79073
79346
  }
79074
79347
  const idleMs = Date.now() - this.idleStartTime.getTime();
79075
79348
  const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
79076
79349
  if (idleMs >= requiredMs) {
79077
- log30.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
79350
+ log31.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
79078
79351
  this.triggerCountdown();
79079
79352
  }
79080
79353
  } else {
79081
79354
  if (this.idleStartTime) {
79082
- log30.debug("Sessions became active, resetting idle timer");
79355
+ log31.debug("Sessions became active, resetting idle timer");
79083
79356
  this.idleStartTime = null;
79084
79357
  }
79085
79358
  }
@@ -79090,7 +79363,7 @@ class UpdateScheduler extends EventEmitter8 {
79090
79363
  const quietMs = Date.now() - activity.lastActivityAt.getTime();
79091
79364
  const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
79092
79365
  if (quietMs >= requiredMs && !activity.anySessionBusy) {
79093
- log30.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
79366
+ log31.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
79094
79367
  this.triggerCountdown();
79095
79368
  }
79096
79369
  } else if (activity.activeSessionCount === 0) {
@@ -79100,7 +79373,7 @@ class UpdateScheduler extends EventEmitter8 {
79100
79373
  const idleMs = Date.now() - this.idleStartTime.getTime();
79101
79374
  const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
79102
79375
  if (idleMs >= requiredMs) {
79103
- log30.info("No sessions and quiet timeout reached, triggering update");
79376
+ log31.info("No sessions and quiet timeout reached, triggering update");
79104
79377
  this.triggerCountdown();
79105
79378
  }
79106
79379
  }
@@ -79111,13 +79384,13 @@ class UpdateScheduler extends EventEmitter8 {
79111
79384
  }
79112
79385
  const activity = this.getSessionActivity();
79113
79386
  if (activity.activeSessionCount === 0) {
79114
- log30.info("Within scheduled window and no active sessions, triggering update");
79387
+ log31.info("Within scheduled window and no active sessions, triggering update");
79115
79388
  this.triggerCountdown();
79116
79389
  } else if (activity.lastActivityAt) {
79117
79390
  const quietMs = Date.now() - activity.lastActivityAt.getTime();
79118
79391
  const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
79119
79392
  if (quietMs >= requiredMs && !activity.anySessionBusy) {
79120
- log30.info("Within scheduled window and sessions quiet, triggering update");
79393
+ log31.info("Within scheduled window and sessions quiet, triggering update");
79121
79394
  this.triggerCountdown();
79122
79395
  }
79123
79396
  }
@@ -79125,14 +79398,14 @@ class UpdateScheduler extends EventEmitter8 {
79125
79398
  checkAskCondition() {
79126
79399
  const threadIds = this.getActiveThreadIds();
79127
79400
  if (threadIds.length === 0) {
79128
- log30.info("No active threads, proceeding with update");
79401
+ log31.info("No active threads, proceeding with update");
79129
79402
  this.triggerCountdown();
79130
79403
  return;
79131
79404
  }
79132
79405
  if (!this.askStartTime && this.pendingUpdate) {
79133
79406
  this.askStartTime = new Date;
79134
79407
  this.postAskMessage(threadIds, this.pendingUpdate.latestVersion).catch((err) => {
79135
- log30.warn(`Failed to post ask message: ${err}`);
79408
+ log31.warn(`Failed to post ask message: ${err}`);
79136
79409
  });
79137
79410
  return;
79138
79411
  }
@@ -79145,12 +79418,12 @@ class UpdateScheduler extends EventEmitter8 {
79145
79418
  denials++;
79146
79419
  }
79147
79420
  if (approvals > threadIds.length / 2) {
79148
- log30.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
79421
+ log31.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
79149
79422
  this.triggerCountdown();
79150
79423
  return;
79151
79424
  }
79152
79425
  if (denials > threadIds.length / 2) {
79153
- log30.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
79426
+ log31.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
79154
79427
  this.deferUpdate(60);
79155
79428
  return;
79156
79429
  }
@@ -79158,7 +79431,7 @@ class UpdateScheduler extends EventEmitter8 {
79158
79431
  const elapsedMs = Date.now() - this.askStartTime.getTime();
79159
79432
  const timeoutMs = this.config.askTimeoutMinutes * 60 * 1000;
79160
79433
  if (elapsedMs >= timeoutMs) {
79161
- log30.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
79434
+ log31.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
79162
79435
  this.triggerCountdown();
79163
79436
  }
79164
79437
  }
@@ -79178,7 +79451,7 @@ class UpdateScheduler extends EventEmitter8 {
79178
79451
  this.emit("ready", this.pendingUpdate);
79179
79452
  }
79180
79453
  }, 1000);
79181
- log30.info("Update countdown started (60 seconds)");
79454
+ log31.info("Update countdown started (60 seconds)");
79182
79455
  }
79183
79456
  stopCountdown() {
79184
79457
  if (this.countdownTimer) {
@@ -79194,24 +79467,24 @@ import { spawn as spawn4, spawnSync } from "child_process";
79194
79467
  import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
79195
79468
  import { dirname as dirname8, resolve as resolve6 } from "path";
79196
79469
  import { homedir as homedir5 } from "os";
79197
- var log31 = createLogger("installer");
79470
+ var log32 = createLogger("installer");
79198
79471
  function detectPackageManager() {
79199
79472
  const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
79200
79473
  const originalInstaller = detectOriginalInstaller();
79201
79474
  if (originalInstaller) {
79202
- log31.debug(`Detected original installer: ${originalInstaller}`);
79475
+ log32.debug(`Detected original installer: ${originalInstaller}`);
79203
79476
  if (originalInstaller === "bun") {
79204
79477
  const bunCheck2 = spawnSync("bun", ["--version"], { stdio: "ignore" });
79205
79478
  if (bunCheck2.status === 0) {
79206
79479
  return { cmd: "bun", isBun: true };
79207
79480
  }
79208
- log31.warn("Originally installed with bun, but bun not found. Falling back to npm.");
79481
+ log32.warn("Originally installed with bun, but bun not found. Falling back to npm.");
79209
79482
  } else {
79210
79483
  const npmCheck2 = spawnSync(npmCmd, ["--version"], { stdio: "ignore" });
79211
79484
  if (npmCheck2.status === 0) {
79212
79485
  return { cmd: npmCmd, isBun: false };
79213
79486
  }
79214
- log31.warn("Originally installed with npm, but npm not found. Falling back to bun.");
79487
+ log32.warn("Originally installed with npm, but npm not found. Falling back to bun.");
79215
79488
  }
79216
79489
  }
79217
79490
  const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
@@ -79262,7 +79535,7 @@ function loadUpdateState() {
79262
79535
  return JSON.parse(content);
79263
79536
  }
79264
79537
  } catch (err) {
79265
- log31.warn(`Failed to load update state: ${err}`);
79538
+ log32.warn(`Failed to load update state: ${err}`);
79266
79539
  }
79267
79540
  return {};
79268
79541
  }
@@ -79273,9 +79546,9 @@ function saveUpdateState(state) {
79273
79546
  mkdirSync4(dir, { recursive: true });
79274
79547
  }
79275
79548
  writeFileSync5(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
79276
- log31.debug("Update state saved");
79549
+ log32.debug("Update state saved");
79277
79550
  } catch (err) {
79278
- log31.warn(`Failed to save update state: ${err}`);
79551
+ log32.warn(`Failed to save update state: ${err}`);
79279
79552
  }
79280
79553
  }
79281
79554
  function clearUpdateState() {
@@ -79284,7 +79557,7 @@ function clearUpdateState() {
79284
79557
  writeFileSync5(STATE_PATH, "{}", "utf-8");
79285
79558
  }
79286
79559
  } catch (err) {
79287
- log31.warn(`Failed to clear update state: ${err}`);
79560
+ log32.warn(`Failed to clear update state: ${err}`);
79288
79561
  }
79289
79562
  }
79290
79563
  function checkJustUpdated() {
@@ -79316,11 +79589,11 @@ function clearRuntimeSettings() {
79316
79589
  }
79317
79590
  }
79318
79591
  async function installVersion(version) {
79319
- log31.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
79592
+ log32.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
79320
79593
  const pm = detectPackageManager();
79321
79594
  if (!pm) {
79322
79595
  const error = "Neither bun nor npm found in PATH. Cannot install update.";
79323
- log31.error(`❌ ${error}`);
79596
+ log32.error(`❌ ${error}`);
79324
79597
  return { success: false, error };
79325
79598
  }
79326
79599
  saveUpdateState({
@@ -79332,7 +79605,7 @@ async function installVersion(version) {
79332
79605
  return new Promise((resolve7) => {
79333
79606
  const { cmd, isBun: isBun3 } = pm;
79334
79607
  const args = ["install", "-g", `${PACKAGE_NAME2}@${version}`];
79335
- log31.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
79608
+ log32.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
79336
79609
  const child = spawn4(cmd, args, {
79337
79610
  stdio: ["ignore", "pipe", "pipe"],
79338
79611
  env: {
@@ -79350,7 +79623,7 @@ async function installVersion(version) {
79350
79623
  });
79351
79624
  child.on("close", (code) => {
79352
79625
  if (code === 0) {
79353
- log31.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
79626
+ log32.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
79354
79627
  saveUpdateState({
79355
79628
  previousVersion: VERSION,
79356
79629
  targetVersion: version,
@@ -79360,20 +79633,20 @@ async function installVersion(version) {
79360
79633
  resolve7({ success: true });
79361
79634
  } else {
79362
79635
  const errorMsg = stderr || stdout || `Exit code: ${code}`;
79363
- log31.error(`❌ Installation failed: ${errorMsg}`);
79636
+ log32.error(`❌ Installation failed: ${errorMsg}`);
79364
79637
  clearUpdateState();
79365
79638
  resolve7({ success: false, error: errorMsg });
79366
79639
  }
79367
79640
  });
79368
79641
  child.on("error", (err) => {
79369
- log31.error(`❌ Failed to spawn npm: ${err}`);
79642
+ log32.error(`❌ Failed to spawn npm: ${err}`);
79370
79643
  clearUpdateState();
79371
79644
  resolve7({ success: false, error: err.message });
79372
79645
  });
79373
79646
  setTimeout(() => {
79374
79647
  if (child.exitCode === null) {
79375
79648
  child.kill();
79376
- log31.error("❌ Installation timed out");
79649
+ log32.error("❌ Installation timed out");
79377
79650
  clearUpdateState();
79378
79651
  resolve7({ success: false, error: "Installation timed out" });
79379
79652
  }
@@ -79415,7 +79688,7 @@ class UpdateInstaller {
79415
79688
  }
79416
79689
 
79417
79690
  // src/auto-update/manager.ts
79418
- var log32 = createLogger("updater");
79691
+ var log33 = createLogger("updater");
79419
79692
 
79420
79693
  class AutoUpdateManager extends EventEmitter9 {
79421
79694
  config;
@@ -79438,23 +79711,23 @@ class AutoUpdateManager extends EventEmitter9 {
79438
79711
  }
79439
79712
  start() {
79440
79713
  if (!this.config.enabled) {
79441
- log32.info("Auto-update is disabled");
79714
+ log33.info("Auto-update is disabled");
79442
79715
  return;
79443
79716
  }
79444
79717
  const updateResult = this.installer.checkJustUpdated();
79445
79718
  if (updateResult) {
79446
- log32.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
79719
+ log33.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
79447
79720
  this.callbacks.broadcastUpdate((fmt) => `\uD83C\uDF89 ${fmt.formatBold("Bot updated")} from v${updateResult.previousVersion} to v${updateResult.currentVersion}`).catch((err) => {
79448
- log32.warn(`Failed to broadcast update notification: ${err}`);
79721
+ log33.warn(`Failed to broadcast update notification: ${err}`);
79449
79722
  });
79450
79723
  }
79451
79724
  this.checker.start();
79452
- log32.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
79725
+ log33.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
79453
79726
  }
79454
79727
  stop() {
79455
79728
  this.checker.stop();
79456
79729
  this.scheduler.stop();
79457
- log32.debug("Auto-update manager stopped");
79730
+ log33.debug("Auto-update manager stopped");
79458
79731
  }
79459
79732
  getState() {
79460
79733
  return { ...this.state };
@@ -79468,10 +79741,10 @@ class AutoUpdateManager extends EventEmitter9 {
79468
79741
  async forceUpdate() {
79469
79742
  const updateInfo = this.state.updateInfo || await this.checker.check();
79470
79743
  if (!updateInfo) {
79471
- log32.info("No update available");
79744
+ log33.info("No update available");
79472
79745
  return;
79473
79746
  }
79474
- log32.info("Forcing immediate update");
79747
+ log33.info("Forcing immediate update");
79475
79748
  await this.performUpdate(updateInfo);
79476
79749
  }
79477
79750
  deferUpdate(minutes = 60) {
@@ -79526,7 +79799,7 @@ class AutoUpdateManager extends EventEmitter9 {
79526
79799
  await this.callbacks.broadcastUpdate((fmt) => `✅ ${fmt.formatBold("Update installed")} - restarting now. ${fmt.formatItalic("Sessions will resume automatically.")}`).catch(() => {});
79527
79800
  await new Promise((resolve7) => setTimeout(resolve7, 1000));
79528
79801
  await this.callbacks.prepareForRestart();
79529
- log32.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
79802
+ log33.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
79530
79803
  process.stdout.write("\x1B[2J\x1B[H");
79531
79804
  process.stdout.write("\x1B[?25h");
79532
79805
  process.exit(RESTART_EXIT_CODE);
@@ -79821,7 +80094,7 @@ async function startWithoutDaemon() {
79821
80094
  keepAlive.setEnabled(keepAliveEnabled);
79822
80095
  const threadLogsEnabled = config.threadLogs?.enabled ?? true;
79823
80096
  const threadLogsRetentionDays = config.threadLogs?.retentionDays ?? 30;
79824
- const session = new SessionManager(workingDir, initialSkipPermissions, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits);
80097
+ const session = new SessionManager(workingDir, initialSkipPermissions, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits, config.claudeAccounts);
79825
80098
  if (config.stickyMessage) {
79826
80099
  session.setStickyMessageCustomization(config.stickyMessage.description, config.stickyMessage.footer);
79827
80100
  }