claude-threads 1.0.2 → 1.0.4

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/index.js +643 -623
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14758,7 +14758,7 @@ var require_react_reconciler_development = __commonJS((exports, module) => {
14758
14758
  return hook.checkDCE ? true : false;
14759
14759
  }
14760
14760
  function setIsStrictModeForDevtools(newIsStrictMode) {
14761
- typeof log28 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
14761
+ typeof log29 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
14762
14762
  if (injectedHook && typeof injectedHook.setStrictMode === "function")
14763
14763
  try {
14764
14764
  injectedHook.setStrictMode(rendererID, newIsStrictMode);
@@ -22842,7 +22842,7 @@ Check the render method of %s.`, getComponentNameFromFiber(current) || "Unknown"
22842
22842
  var fiberStack = [];
22843
22843
  var index$jscomp$0 = -1, emptyContextObject = {};
22844
22844
  Object.freeze(emptyContextObject);
22845
- 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, log28 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
22845
+ 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, log29 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
22846
22846
  if (typeof performance === "object" && typeof performance.now === "function") {
22847
22847
  var localPerformance = performance;
22848
22848
  var getCurrentTime = function() {
@@ -27449,12 +27449,12 @@ var require_backend = __commonJS((exports, module) => {
27449
27449
  }
27450
27450
  return (r === "string" ? String : Number)(t);
27451
27451
  }
27452
- var EventEmitter6 = /* @__PURE__ */ function() {
27453
- function EventEmitter7() {
27454
- _classCallCheck(this, EventEmitter7);
27452
+ var EventEmitter5 = /* @__PURE__ */ function() {
27453
+ function EventEmitter6() {
27454
+ _classCallCheck(this, EventEmitter6);
27455
27455
  _defineProperty(this, "listenersMap", new Map);
27456
27456
  }
27457
- return _createClass(EventEmitter7, [{
27457
+ return _createClass(EventEmitter6, [{
27458
27458
  key: "addListener",
27459
27459
  value: function addListener(event, listener) {
27460
27460
  var listeners = this.listenersMap.get(event);
@@ -30882,7 +30882,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
30882
30882
  } while (this._messageQueue.length);
30883
30883
  }
30884
30884
  }]);
30885
- }(EventEmitter6);
30885
+ }(EventEmitter5);
30886
30886
  const src_bridge = Bridge;
30887
30887
  function storage_localStorageGetItem(key) {
30888
30888
  try {
@@ -31736,7 +31736,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
31736
31736
  this._bridge.send("unsupportedRendererVersion");
31737
31737
  }
31738
31738
  }]);
31739
- }(EventEmitter6);
31739
+ }(EventEmitter5);
31740
31740
  function DevToolsConsolePatching_typeof(o) {
31741
31741
  "@babel/helpers - typeof";
31742
31742
  return DevToolsConsolePatching_typeof = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(o2) {
@@ -46392,7 +46392,7 @@ async function setupSlackPlatform(id, existing) {
46392
46392
  }
46393
46393
  }
46394
46394
 
46395
- // src/platform/mattermost/client.ts
46395
+ // src/platform/base-client.ts
46396
46396
  import { EventEmitter } from "events";
46397
46397
 
46398
46398
  // src/utils/logger.ts
@@ -46468,6 +46468,114 @@ ${err.stack || err.message}` : msg;
46468
46468
  var mcpLogger = createLogger("MCP", true);
46469
46469
  var wsLogger = createLogger("ws", false);
46470
46470
 
46471
+ // src/platform/base-client.ts
46472
+ var log = createLogger("base-client");
46473
+
46474
+ class BasePlatformClient extends EventEmitter {
46475
+ allowedUsers = [];
46476
+ botName = "";
46477
+ isIntentionalDisconnect = false;
46478
+ isReconnecting = false;
46479
+ heartbeatInterval = null;
46480
+ lastMessageAt = Date.now();
46481
+ HEARTBEAT_INTERVAL_MS = 30000;
46482
+ HEARTBEAT_TIMEOUT_MS = 60000;
46483
+ reconnectAttempts = 0;
46484
+ maxReconnectAttempts = 10;
46485
+ reconnectDelay = 1000;
46486
+ isUserAllowed(username) {
46487
+ if (this.allowedUsers.length === 0) {
46488
+ return true;
46489
+ }
46490
+ return this.allowedUsers.includes(username);
46491
+ }
46492
+ getBotName() {
46493
+ return this.botName;
46494
+ }
46495
+ async createInteractivePost(message, reactions, threadId) {
46496
+ const post = await this.createPost(message, threadId);
46497
+ for (const emoji of reactions) {
46498
+ try {
46499
+ await this.addReaction(post.id, emoji);
46500
+ } catch (err) {
46501
+ log.warn(`Failed to add reaction ${emoji}: ${err}`);
46502
+ }
46503
+ }
46504
+ return post;
46505
+ }
46506
+ disconnect() {
46507
+ wsLogger.info("Disconnecting (intentional)");
46508
+ this.isIntentionalDisconnect = true;
46509
+ this.stopHeartbeat();
46510
+ this.forceCloseConnection();
46511
+ }
46512
+ prepareForReconnect() {
46513
+ wsLogger.debug("Preparing for reconnect (resetting intentional disconnect flag)");
46514
+ this.isIntentionalDisconnect = false;
46515
+ this.reconnectAttempts = 0;
46516
+ }
46517
+ startHeartbeat() {
46518
+ this.stopHeartbeat();
46519
+ this.lastMessageAt = Date.now();
46520
+ this.heartbeatInterval = setInterval(() => {
46521
+ const silentFor = Date.now() - this.lastMessageAt;
46522
+ if (silentFor > this.HEARTBEAT_TIMEOUT_MS) {
46523
+ log.warn(`Connection dead (no activity for ${Math.round(silentFor / 1000)}s), reconnecting...`);
46524
+ this.stopHeartbeat();
46525
+ this.forceCloseConnection();
46526
+ return;
46527
+ }
46528
+ wsLogger.debug(`Heartbeat check (last activity ${Math.round(silentFor / 1000)}s ago)`);
46529
+ }, this.HEARTBEAT_INTERVAL_MS);
46530
+ }
46531
+ stopHeartbeat() {
46532
+ if (this.heartbeatInterval) {
46533
+ clearInterval(this.heartbeatInterval);
46534
+ this.heartbeatInterval = null;
46535
+ }
46536
+ }
46537
+ scheduleReconnect() {
46538
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
46539
+ log.error("Max reconnection attempts reached");
46540
+ return;
46541
+ }
46542
+ this.forceCloseConnection();
46543
+ this.isReconnecting = true;
46544
+ this.reconnectAttempts++;
46545
+ const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
46546
+ log.info(`Reconnecting... (attempt ${this.reconnectAttempts})`);
46547
+ this.emit("reconnecting", this.reconnectAttempts);
46548
+ setTimeout(() => {
46549
+ this.connect().catch((err) => {
46550
+ log.error(`Reconnection failed: ${err}`);
46551
+ });
46552
+ }, delay);
46553
+ }
46554
+ onConnectionEstablished() {
46555
+ this.reconnectAttempts = 0;
46556
+ this.startHeartbeat();
46557
+ this.emit("connected");
46558
+ if (this.isReconnecting) {
46559
+ this.recoverMissedMessages().catch((err) => {
46560
+ log.warn(`Failed to recover missed messages: ${err}`);
46561
+ });
46562
+ }
46563
+ this.isReconnecting = false;
46564
+ }
46565
+ onConnectionClosed() {
46566
+ this.stopHeartbeat();
46567
+ this.emit("disconnected");
46568
+ if (!this.isIntentionalDisconnect) {
46569
+ wsLogger.debug("Scheduling reconnect...");
46570
+ this.scheduleReconnect();
46571
+ } else {
46572
+ wsLogger.debug("Intentional disconnect, not reconnecting");
46573
+ }
46574
+ }
46575
+ updateLastMessageTime() {
46576
+ this.lastMessageAt = Date.now();
46577
+ }
46578
+ }
46471
46579
  // src/utils/format.ts
46472
46580
  function extractThreadId(sessionId) {
46473
46581
  const colonIndex = sessionId.indexOf(":");
@@ -46731,9 +46839,9 @@ ${code}
46731
46839
  }
46732
46840
 
46733
46841
  // src/platform/mattermost/client.ts
46734
- var log = createLogger("mattermost");
46842
+ var log2 = createLogger("mattermost");
46735
46843
 
46736
- class MattermostClient extends EventEmitter {
46844
+ class MattermostClient extends BasePlatformClient {
46737
46845
  platformId;
46738
46846
  platformType = "mattermost";
46739
46847
  displayName;
@@ -46741,21 +46849,10 @@ class MattermostClient extends EventEmitter {
46741
46849
  url;
46742
46850
  token;
46743
46851
  channelId;
46744
- botName;
46745
- allowedUsers;
46746
- reconnectAttempts = 0;
46747
- maxReconnectAttempts = 10;
46748
- reconnectDelay = 1000;
46749
46852
  userCache = new Map;
46750
46853
  botUserId = null;
46751
46854
  formatter = new MattermostFormatter;
46752
- heartbeatInterval = null;
46753
- lastMessageAt = Date.now();
46754
- HEARTBEAT_INTERVAL_MS = 30000;
46755
- HEARTBEAT_TIMEOUT_MS = 60000;
46756
46855
  lastProcessedPostId = null;
46757
- isReconnecting = false;
46758
- isIntentionalDisconnect = false;
46759
46856
  constructor(platformConfig) {
46760
46857
  super();
46761
46858
  this.platformId = platformConfig.id;
@@ -46812,7 +46909,7 @@ class MattermostClient extends EventEmitter {
46812
46909
  const hasFileIds = post.file_ids && post.file_ids.length > 0;
46813
46910
  const hasFileMetadata = post.metadata?.files && post.metadata.files.length > 0;
46814
46911
  if (hasFileIds && !hasFileMetadata) {
46815
- log.debug(`Post ${formatShortId(post.id)} has ${post.file_ids.length} file(s), fetching metadata`);
46912
+ log2.debug(`Post ${formatShortId(post.id)} has ${post.file_ids.length} file(s), fetching metadata`);
46816
46913
  try {
46817
46914
  const files = [];
46818
46915
  for (const fileId of post.file_ids) {
@@ -46820,7 +46917,7 @@ class MattermostClient extends EventEmitter {
46820
46917
  const file = await this.api("GET", `/files/${fileId}/info`);
46821
46918
  files.push(file);
46822
46919
  } catch (err) {
46823
- log.warn(`Failed to fetch file info for ${fileId}: ${err}`);
46920
+ log2.warn(`Failed to fetch file info for ${fileId}: ${err}`);
46824
46921
  }
46825
46922
  }
46826
46923
  if (files.length > 0) {
@@ -46828,10 +46925,10 @@ class MattermostClient extends EventEmitter {
46828
46925
  ...post.metadata,
46829
46926
  files
46830
46927
  };
46831
- log.debug(`Enriched post ${formatShortId(post.id)} with ${files.length} file(s)`);
46928
+ log2.debug(`Enriched post ${formatShortId(post.id)} with ${files.length} file(s)`);
46832
46929
  }
46833
46930
  } catch (err) {
46834
- log.warn(`Failed to fetch file metadata for post ${formatShortId(post.id)}: ${err}`);
46931
+ log2.warn(`Failed to fetch file metadata for post ${formatShortId(post.id)}: ${err}`);
46835
46932
  }
46836
46933
  }
46837
46934
  const user = await this.getUser(post.user_id);
@@ -46845,7 +46942,7 @@ class MattermostClient extends EventEmitter {
46845
46942
  RETRY_DELAY_MS = 500;
46846
46943
  async api(method, path, body, retryCount = 0, options) {
46847
46944
  const url = `${this.url}/api/v4${path}`;
46848
- log.debug(`API ${method} ${path}`);
46945
+ log2.debug(`API ${method} ${path}`);
46849
46946
  const response = await fetch(url, {
46850
46947
  method,
46851
46948
  headers: {
@@ -46858,19 +46955,19 @@ class MattermostClient extends EventEmitter {
46858
46955
  const text = await response.text();
46859
46956
  if (response.status === 500 && retryCount < this.MAX_RETRIES) {
46860
46957
  const delay = this.RETRY_DELAY_MS * Math.pow(2, retryCount);
46861
- log.warn(`API ${method} ${path} failed with 500, retrying in ${delay}ms (attempt ${retryCount + 1}/${this.MAX_RETRIES})`);
46958
+ log2.warn(`API ${method} ${path} failed with 500, retrying in ${delay}ms (attempt ${retryCount + 1}/${this.MAX_RETRIES})`);
46862
46959
  await new Promise((resolve2) => setTimeout(resolve2, delay));
46863
46960
  return this.api(method, path, body, retryCount + 1, options);
46864
46961
  }
46865
46962
  const isSilent = options?.silent?.includes(response.status);
46866
46963
  if (isSilent) {
46867
- log.debug(`API ${method} ${path} failed: ${response.status} (expected)`);
46964
+ log2.debug(`API ${method} ${path} failed: ${response.status} (expected)`);
46868
46965
  } else {
46869
- log.warn(`API ${method} ${path} failed: ${response.status} ${text.substring(0, 100)}`);
46966
+ log2.warn(`API ${method} ${path} failed: ${response.status} ${text.substring(0, 100)}`);
46870
46967
  }
46871
46968
  throw new Error(`Mattermost API error ${response.status}: ${text}`);
46872
46969
  }
46873
- log.debug(`API ${method} ${path} \u2192 ${response.status}`);
46970
+ log2.debug(`API ${method} ${path} \u2192 ${response.status}`);
46874
46971
  return response.json();
46875
46972
  }
46876
46973
  async getBotUser() {
@@ -46881,28 +46978,28 @@ class MattermostClient extends EventEmitter {
46881
46978
  async getUser(userId) {
46882
46979
  const cached = this.userCache.get(userId);
46883
46980
  if (cached) {
46884
- log.debug(`User ${userId} found in cache: @${cached.username}`);
46981
+ log2.debug(`User ${userId} found in cache: @${cached.username}`);
46885
46982
  return this.normalizePlatformUser(cached);
46886
46983
  }
46887
46984
  try {
46888
46985
  const user = await this.api("GET", `/users/${userId}`);
46889
46986
  this.userCache.set(userId, user);
46890
- log.debug(`User ${userId} fetched: @${user.username}`);
46987
+ log2.debug(`User ${userId} fetched: @${user.username}`);
46891
46988
  return this.normalizePlatformUser(user);
46892
46989
  } catch (err) {
46893
- log.warn(`Failed to get user ${userId}: ${err}`);
46990
+ log2.warn(`Failed to get user ${userId}: ${err}`);
46894
46991
  return null;
46895
46992
  }
46896
46993
  }
46897
46994
  async getUserByUsername(username) {
46898
46995
  try {
46899
- log.debug(`Looking up user by username: @${username}`);
46996
+ log2.debug(`Looking up user by username: @${username}`);
46900
46997
  const user = await this.api("GET", `/users/username/${username}`);
46901
46998
  this.userCache.set(user.id, user);
46902
- log.debug(`User @${username} found: ${user.id}`);
46999
+ log2.debug(`User @${username} found: ${user.id}`);
46903
47000
  return this.normalizePlatformUser(user);
46904
47001
  } catch (err) {
46905
- log.warn(`User @${username} not found: ${err}`);
47002
+ log2.warn(`User @${username} not found: ${err}`);
46906
47003
  return null;
46907
47004
  }
46908
47005
  }
@@ -46924,7 +47021,7 @@ class MattermostClient extends EventEmitter {
46924
47021
  return this.normalizePlatformPost(post);
46925
47022
  }
46926
47023
  async addReaction(postId, emojiName) {
46927
- log.debug(`Adding reaction :${emojiName}: to post ${postId.substring(0, 8)}`);
47024
+ log2.debug(`Adding reaction :${emojiName}: to post ${postId.substring(0, 8)}`);
46928
47025
  await this.api("POST", "/reactions", {
46929
47026
  user_id: this.botUserId,
46930
47027
  post_id: postId,
@@ -46932,22 +47029,11 @@ class MattermostClient extends EventEmitter {
46932
47029
  });
46933
47030
  }
46934
47031
  async removeReaction(postId, emojiName) {
46935
- log.debug(`Removing reaction :${emojiName}: from post ${postId.substring(0, 8)}`);
47032
+ log2.debug(`Removing reaction :${emojiName}: from post ${postId.substring(0, 8)}`);
46936
47033
  await this.api("DELETE", `/users/${this.botUserId}/posts/${postId}/reactions/${emojiName}`);
46937
47034
  }
46938
- async createInteractivePost(message, reactions, threadId) {
46939
- const post = await this.createPost(message, threadId);
46940
- for (const emoji of reactions) {
46941
- try {
46942
- await this.addReaction(post.id, emoji);
46943
- } catch (err) {
46944
- log.warn(`Failed to add reaction ${emoji}: ${err}`);
46945
- }
46946
- }
46947
- return post;
46948
- }
46949
47035
  async downloadFile(fileId) {
46950
- log.debug(`Downloading file ${fileId}`);
47036
+ log2.debug(`Downloading file ${fileId}`);
46951
47037
  const url = `${this.url}/api/v4/files/${fileId}`;
46952
47038
  const response = await fetch(url, {
46953
47039
  headers: {
@@ -46955,11 +47041,11 @@ class MattermostClient extends EventEmitter {
46955
47041
  }
46956
47042
  });
46957
47043
  if (!response.ok) {
46958
- log.warn(`Failed to download file ${fileId}: ${response.status}`);
47044
+ log2.warn(`Failed to download file ${fileId}: ${response.status}`);
46959
47045
  throw new Error(`Failed to download file ${fileId}: ${response.status}`);
46960
47046
  }
46961
47047
  const arrayBuffer = await response.arrayBuffer();
46962
- log.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
47048
+ log2.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
46963
47049
  return Buffer.from(arrayBuffer);
46964
47050
  }
46965
47051
  async getFileInfo(fileId) {
@@ -46968,24 +47054,24 @@ class MattermostClient extends EventEmitter {
46968
47054
  }
46969
47055
  async getPost(postId) {
46970
47056
  try {
46971
- log.debug(`Fetching post ${postId.substring(0, 8)}`);
47057
+ log2.debug(`Fetching post ${postId.substring(0, 8)}`);
46972
47058
  const post = await this.api("GET", `/posts/${postId}`);
46973
47059
  return this.normalizePlatformPost(post);
46974
47060
  } catch (err) {
46975
- log.debug(`Post ${postId.substring(0, 8)} not found: ${err}`);
47061
+ log2.debug(`Post ${postId.substring(0, 8)} not found: ${err}`);
46976
47062
  return null;
46977
47063
  }
46978
47064
  }
46979
47065
  async deletePost(postId) {
46980
- log.debug(`Deleting post ${postId.substring(0, 8)}`);
47066
+ log2.debug(`Deleting post ${postId.substring(0, 8)}`);
46981
47067
  await this.api("DELETE", `/posts/${postId}`);
46982
47068
  }
46983
47069
  async pinPost(postId) {
46984
- log.debug(`Pinning post ${postId.substring(0, 8)}`);
47070
+ log2.debug(`Pinning post ${postId.substring(0, 8)}`);
46985
47071
  await this.api("POST", `/posts/${postId}/pin`);
46986
47072
  }
46987
47073
  async unpinPost(postId) {
46988
- log.debug(`Unpinning post ${postId.substring(0, 8)}`);
47074
+ log2.debug(`Unpinning post ${postId.substring(0, 8)}`);
46989
47075
  try {
46990
47076
  await this.api("POST", `/posts/${postId}/unpin`, undefined, 0, { silent: [403, 404] });
46991
47077
  } catch (err) {
@@ -47029,7 +47115,7 @@ class MattermostClient extends EventEmitter {
47029
47115
  }
47030
47116
  return messages;
47031
47117
  } catch (err) {
47032
- log.warn(`Failed to get thread history for ${threadId}: ${err}`);
47118
+ log2.warn(`Failed to get thread history for ${threadId}: ${err}`);
47033
47119
  return [];
47034
47120
  }
47035
47121
  }
@@ -47048,7 +47134,7 @@ class MattermostClient extends EventEmitter {
47048
47134
  posts.sort((a, b) => (a.createAt ?? 0) - (b.createAt ?? 0));
47049
47135
  return posts;
47050
47136
  } catch (err) {
47051
- log.warn(`Failed to get channel posts after ${afterPostId}: ${err}`);
47137
+ log2.warn(`Failed to get channel posts after ${afterPostId}: ${err}`);
47052
47138
  return [];
47053
47139
  }
47054
47140
  }
@@ -47070,21 +47156,13 @@ class MattermostClient extends EventEmitter {
47070
47156
  }
47071
47157
  };
47072
47158
  this.ws.onmessage = (event) => {
47073
- this.lastMessageAt = Date.now();
47159
+ this.updateLastMessageTime();
47074
47160
  try {
47075
47161
  const data = typeof event.data === "string" ? event.data : event.data.toString();
47076
47162
  const wsEvent = JSON.parse(data);
47077
47163
  this.handleEvent(wsEvent);
47078
47164
  if (wsEvent.event === "hello") {
47079
- this.reconnectAttempts = 0;
47080
- this.startHeartbeat();
47081
- this.emit("connected");
47082
- if (this.isReconnecting && this.lastProcessedPostId) {
47083
- this.recoverMissedMessages().catch((err) => {
47084
- log.warn(`Failed to recover missed messages: ${err}`);
47085
- });
47086
- }
47087
- this.isReconnecting = false;
47165
+ this.onConnectionEstablished();
47088
47166
  resolve2();
47089
47167
  }
47090
47168
  } catch (err) {
@@ -47093,14 +47171,7 @@ class MattermostClient extends EventEmitter {
47093
47171
  };
47094
47172
  this.ws.onclose = (event) => {
47095
47173
  wsLogger.info(`WebSocket disconnected (code: ${event.code}, reason: ${event.reason || "none"}, clean: ${event.wasClean})`);
47096
- this.stopHeartbeat();
47097
- this.emit("disconnected");
47098
- if (!this.isIntentionalDisconnect) {
47099
- wsLogger.debug("Scheduling reconnect...");
47100
- this.scheduleReconnect();
47101
- } else {
47102
- wsLogger.debug("Intentional disconnect, not reconnecting");
47103
- }
47174
+ this.onConnectionClosed();
47104
47175
  };
47105
47176
  this.ws.onerror = (event) => {
47106
47177
  wsLogger.warn(`WebSocket error: ${event}`);
@@ -47158,55 +47229,31 @@ class MattermostClient extends EventEmitter {
47158
47229
  }
47159
47230
  }
47160
47231
  }
47161
- scheduleReconnect() {
47162
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
47163
- log.error("Max reconnection attempts reached");
47164
- return;
47165
- }
47166
- this.isReconnecting = true;
47167
- this.reconnectAttempts++;
47168
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
47169
- log.info(`Reconnecting... (attempt ${this.reconnectAttempts})`);
47170
- this.emit("reconnecting", this.reconnectAttempts);
47171
- setTimeout(() => {
47172
- this.connect().catch((err) => {
47173
- log.error(`Reconnection failed: ${err}`);
47174
- });
47175
- }, delay);
47176
- }
47177
- startHeartbeat() {
47178
- this.stopHeartbeat();
47179
- this.lastMessageAt = Date.now();
47180
- this.heartbeatInterval = setInterval(() => {
47181
- const silentFor = Date.now() - this.lastMessageAt;
47182
- if (silentFor > this.HEARTBEAT_TIMEOUT_MS) {
47183
- log.warn(`Connection dead (no activity for ${Math.round(silentFor / 1000)}s), reconnecting...`);
47184
- this.stopHeartbeat();
47185
- if (this.ws) {
47232
+ forceCloseConnection() {
47233
+ if (this.ws) {
47234
+ this.ws.onopen = null;
47235
+ this.ws.onmessage = null;
47236
+ this.ws.onclose = null;
47237
+ this.ws.onerror = null;
47238
+ if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
47239
+ try {
47186
47240
  this.ws.close();
47187
- }
47188
- return;
47241
+ } catch {}
47189
47242
  }
47190
- wsLogger.debug(`Heartbeat check (last activity ${Math.round(silentFor / 1000)}s ago)`);
47191
- }, this.HEARTBEAT_INTERVAL_MS);
47192
- }
47193
- stopHeartbeat() {
47194
- if (this.heartbeatInterval) {
47195
- clearInterval(this.heartbeatInterval);
47196
- this.heartbeatInterval = null;
47243
+ this.ws = null;
47197
47244
  }
47198
47245
  }
47199
47246
  async recoverMissedMessages() {
47200
47247
  if (!this.lastProcessedPostId) {
47201
47248
  return;
47202
47249
  }
47203
- log.info(`Recovering missed messages after post ${this.lastProcessedPostId}...`);
47250
+ log2.info(`Recovering missed messages after post ${this.lastProcessedPostId}...`);
47204
47251
  const missedPosts = await this.getChannelPostsAfter(this.lastProcessedPostId);
47205
47252
  if (missedPosts.length === 0) {
47206
- log.info("No missed messages to recover");
47253
+ log2.info("No missed messages to recover");
47207
47254
  return;
47208
47255
  }
47209
- log.info(`Recovered ${missedPosts.length} missed message(s)`);
47256
+ log2.info(`Recovered ${missedPosts.length} missed message(s)`);
47210
47257
  for (const post of missedPosts) {
47211
47258
  this.lastProcessedPostId = post.id;
47212
47259
  const user = await this.getUser(post.userId);
@@ -47216,12 +47263,6 @@ class MattermostClient extends EventEmitter {
47216
47263
  }
47217
47264
  }
47218
47265
  }
47219
- isUserAllowed(username) {
47220
- if (this.allowedUsers.length === 0) {
47221
- return true;
47222
- }
47223
- return this.allowedUsers.includes(username);
47224
- }
47225
47266
  isBotMentioned(message) {
47226
47267
  const botName = escapeRegExp(this.botName);
47227
47268
  const mentionPattern = new RegExp(`(^|\\s)@${botName}\\b`, "i");
@@ -47231,9 +47272,6 @@ class MattermostClient extends EventEmitter {
47231
47272
  const botName = escapeRegExp(this.botName);
47232
47273
  return message.replace(new RegExp(`(^|\\s)@${botName}\\b`, "gi"), " ").trim();
47233
47274
  }
47234
- getBotName() {
47235
- return this.botName;
47236
- }
47237
47275
  getMcpConfig() {
47238
47276
  return {
47239
47277
  type: "mattermost",
@@ -47264,24 +47302,7 @@ class MattermostClient extends EventEmitter {
47264
47302
  }
47265
47303
  }));
47266
47304
  }
47267
- disconnect() {
47268
- wsLogger.info("Disconnecting WebSocket (intentional)");
47269
- this.isIntentionalDisconnect = true;
47270
- this.stopHeartbeat();
47271
- if (this.ws) {
47272
- this.ws.close();
47273
- this.ws = null;
47274
- }
47275
- }
47276
- prepareForReconnect() {
47277
- wsLogger.debug("Preparing for reconnect (resetting intentional disconnect flag)");
47278
- this.isIntentionalDisconnect = false;
47279
- this.reconnectAttempts = 0;
47280
- }
47281
47305
  }
47282
- // src/platform/slack/client.ts
47283
- import { EventEmitter as EventEmitter2 } from "events";
47284
-
47285
47306
  // src/platform/slack/formatter.ts
47286
47307
  class SlackFormatter {
47287
47308
  formatBold(text) {
@@ -47352,9 +47373,9 @@ ${code}
47352
47373
  }
47353
47374
 
47354
47375
  // src/platform/slack/client.ts
47355
- var log2 = createLogger("slack");
47376
+ var log3 = createLogger("slack");
47356
47377
 
47357
- class SlackClient extends EventEmitter2 {
47378
+ class SlackClient extends BasePlatformClient {
47358
47379
  platformId;
47359
47380
  platformType = "slack";
47360
47381
  displayName;
@@ -47362,25 +47383,14 @@ class SlackClient extends EventEmitter2 {
47362
47383
  botToken;
47363
47384
  appToken;
47364
47385
  channelId;
47365
- botName;
47366
- allowedUsers;
47367
47386
  skipPermissions;
47368
47387
  apiUrl;
47369
- reconnectAttempts = 0;
47370
- maxReconnectAttempts = 10;
47371
- reconnectDelay = 1000;
47372
- isIntentionalDisconnect = false;
47373
- isReconnecting = false;
47374
47388
  reconnectTimeout = null;
47375
47389
  userCache = new Map;
47376
47390
  usernameToIdCache = new Map;
47377
47391
  botUserId = null;
47378
47392
  botUser = null;
47379
47393
  teamUrl = null;
47380
- heartbeatInterval = null;
47381
- lastMessageAt = Date.now();
47382
- HEARTBEAT_INTERVAL_MS = 30000;
47383
- HEARTBEAT_TIMEOUT_MS = 60000;
47384
47394
  lastProcessedTs = null;
47385
47395
  processedMessages = new Set;
47386
47396
  MAX_PROCESSED_MESSAGES = 1000;
@@ -47437,13 +47447,13 @@ class SlackClient extends EventEmitter2 {
47437
47447
  const now = Date.now();
47438
47448
  if (now < this.rateLimitRetryAfter) {
47439
47449
  const waitTime = this.rateLimitRetryAfter - now;
47440
- log2.debug(`Rate limited, waiting ${waitTime}ms`);
47450
+ log3.debug(`Rate limited, waiting ${waitTime}ms`);
47441
47451
  await new Promise((resolve2) => setTimeout(resolve2, waitTime));
47442
47452
  }
47443
47453
  this.rateLimitDelay = 0;
47444
47454
  }
47445
47455
  const url = `${this.apiUrl}/${endpoint}`;
47446
- log2.debug(`API ${method} ${endpoint}`);
47456
+ log3.debug(`API ${method} ${endpoint}`);
47447
47457
  const headers = {
47448
47458
  Authorization: `Bearer ${this.botToken}`,
47449
47459
  "Content-Type": "application/json; charset=utf-8"
@@ -47455,25 +47465,25 @@ class SlackClient extends EventEmitter2 {
47455
47465
  });
47456
47466
  if (response.status === 429) {
47457
47467
  if (retryCount >= this.MAX_RATE_LIMIT_RETRIES) {
47458
- log2.error(`Rate limit max retries (${this.MAX_RATE_LIMIT_RETRIES}) exceeded for ${endpoint}`);
47468
+ log3.error(`Rate limit max retries (${this.MAX_RATE_LIMIT_RETRIES}) exceeded for ${endpoint}`);
47459
47469
  throw new Error(`Slack API rate limit exceeded after ${this.MAX_RATE_LIMIT_RETRIES} retries`);
47460
47470
  }
47461
47471
  const retryAfter = parseInt(response.headers.get("Retry-After") || "5", 10);
47462
47472
  this.rateLimitDelay = retryAfter * 1000;
47463
47473
  this.rateLimitRetryAfter = Date.now() + this.rateLimitDelay;
47464
- log2.warn(`Rate limited by Slack, retrying after ${retryAfter}s (attempt ${retryCount + 1}/${this.MAX_RATE_LIMIT_RETRIES})`);
47474
+ log3.warn(`Rate limited by Slack, retrying after ${retryAfter}s (attempt ${retryCount + 1}/${this.MAX_RATE_LIMIT_RETRIES})`);
47465
47475
  await new Promise((resolve2) => setTimeout(resolve2, this.rateLimitDelay));
47466
47476
  return this.api(method, endpoint, body, retryCount + 1);
47467
47477
  }
47468
47478
  if (!response.ok) {
47469
47479
  const text = await response.text();
47470
- log2.warn(`API ${method} ${endpoint} failed: ${response.status} ${text.substring(0, 100)}`);
47480
+ log3.warn(`API ${method} ${endpoint} failed: ${response.status} ${text.substring(0, 100)}`);
47471
47481
  throw new Error(`Slack API error ${response.status}: ${text}`);
47472
47482
  }
47473
47483
  const data = await response.json();
47474
47484
  if (!data.ok) {
47475
47485
  if (!expectedErrors.includes(data.error || "")) {
47476
- log2.warn(`API ${method} ${endpoint} error: ${data.error}`);
47486
+ log3.warn(`API ${method} ${endpoint} error: ${data.error}`);
47477
47487
  }
47478
47488
  throw new Error(`Slack API error: ${data.error}`);
47479
47489
  }
@@ -47481,7 +47491,7 @@ class SlackClient extends EventEmitter2 {
47481
47491
  }
47482
47492
  async appApi(method, endpoint, body) {
47483
47493
  const url = `${this.apiUrl}/${endpoint}`;
47484
- log2.debug(`App API ${method} ${endpoint}`);
47494
+ log3.debug(`App API ${method} ${endpoint}`);
47485
47495
  const headers = {
47486
47496
  Authorization: `Bearer ${this.appToken}`,
47487
47497
  "Content-Type": "application/json; charset=utf-8"
@@ -47534,22 +47544,19 @@ class SlackClient extends EventEmitter2 {
47534
47544
  wsLogger.info("Socket Mode: WebSocket connected, waiting for hello...");
47535
47545
  };
47536
47546
  this.ws.onmessage = (event) => {
47537
- this.lastMessageAt = Date.now();
47547
+ this.updateLastMessageTime();
47538
47548
  try {
47539
47549
  const data = typeof event.data === "string" ? event.data : event.data.toString();
47540
47550
  const envelope = JSON.parse(data);
47541
47551
  this.handleSocketModeEvent(envelope);
47542
47552
  if (envelope.type === "hello") {
47543
47553
  clearTimeout(connectionTimeout);
47544
- this.reconnectAttempts = 0;
47545
- this.startHeartbeat();
47546
- this.emit("connected");
47554
+ this.onConnectionEstablished();
47547
47555
  if (this.isReconnecting && this.lastProcessedTs) {
47548
47556
  this.recoverMissedMessages().catch((err) => {
47549
- log2.warn(`Failed to recover missed messages: ${err}`);
47557
+ log3.warn(`Failed to recover missed messages: ${err}`);
47550
47558
  });
47551
47559
  }
47552
- this.isReconnecting = false;
47553
47560
  doResolve();
47554
47561
  }
47555
47562
  } catch (err) {
@@ -47559,8 +47566,6 @@ class SlackClient extends EventEmitter2 {
47559
47566
  this.ws.onclose = (event) => {
47560
47567
  clearTimeout(connectionTimeout);
47561
47568
  wsLogger.info(`Socket Mode: WebSocket disconnected (code: ${event.code}, reason: ${event.reason || "none"}, clean: ${event.wasClean})`);
47562
- this.stopHeartbeat();
47563
- this.emit("disconnected");
47564
47569
  if (!settled) {
47565
47570
  wsLogger.warn(`WebSocket closed before hello event (code: ${event.code}, reason: ${event.reason || "none"})`);
47566
47571
  }
@@ -47568,9 +47573,10 @@ class SlackClient extends EventEmitter2 {
47568
47573
  const serverShutdown = event.reason?.toLowerCase().includes("server shutting down");
47569
47574
  const connectionReplaced = event.reason?.toLowerCase().includes("new connection replacing");
47570
47575
  if (!this.isIntentionalDisconnect && !serverShutdown && !connectionReplaced) {
47571
- wsLogger.debug("Scheduling reconnect...");
47572
- this.scheduleReconnect();
47576
+ this.onConnectionClosed();
47573
47577
  } else {
47578
+ this.stopHeartbeat();
47579
+ this.emit("disconnected");
47574
47580
  if (serverShutdown) {
47575
47581
  wsLogger.debug("Server shutdown detected, not reconnecting");
47576
47582
  } else if (connectionReplaced) {
@@ -47644,7 +47650,7 @@ class SlackClient extends EventEmitter2 {
47644
47650
  this.emit("channel_post", post, user);
47645
47651
  }
47646
47652
  }).catch((err) => {
47647
- log2.warn(`Failed to get user for message event: ${err}`);
47653
+ log3.warn(`Failed to get user for message event: ${err}`);
47648
47654
  this.emit("message", post, null);
47649
47655
  });
47650
47656
  }
@@ -47664,7 +47670,7 @@ class SlackClient extends EventEmitter2 {
47664
47670
  this.getUser(event.user || "").then((user) => {
47665
47671
  this.emit("reaction", reaction, user);
47666
47672
  }).catch((err) => {
47667
- log2.warn(`Failed to get user for reaction event: ${err}`);
47673
+ log3.warn(`Failed to get user for reaction event: ${err}`);
47668
47674
  this.emit("reaction", reaction, null);
47669
47675
  });
47670
47676
  }
@@ -47684,24 +47690,39 @@ class SlackClient extends EventEmitter2 {
47684
47690
  this.getUser(event.user || "").then((user) => {
47685
47691
  this.emit("reaction_removed", reaction, user);
47686
47692
  }).catch((err) => {
47687
- log2.warn(`Failed to get user for reaction_removed event: ${err}`);
47693
+ log3.warn(`Failed to get user for reaction_removed event: ${err}`);
47688
47694
  this.emit("reaction_removed", reaction, null);
47689
47695
  });
47690
47696
  }
47691
47697
  }
47692
- scheduleReconnect() {
47693
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
47694
- log2.error("Max reconnection attempts reached");
47695
- return;
47698
+ forceCloseConnection() {
47699
+ if (this.ws) {
47700
+ this.ws.onopen = null;
47701
+ this.ws.onmessage = null;
47702
+ this.ws.onclose = null;
47703
+ this.ws.onerror = null;
47704
+ if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
47705
+ try {
47706
+ this.ws.close();
47707
+ } catch {}
47708
+ }
47709
+ this.ws = null;
47696
47710
  }
47711
+ }
47712
+ scheduleReconnect() {
47697
47713
  if (this.reconnectTimeout) {
47698
47714
  clearTimeout(this.reconnectTimeout);
47699
47715
  this.reconnectTimeout = null;
47700
47716
  }
47717
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
47718
+ log3.error("Max reconnection attempts reached");
47719
+ return;
47720
+ }
47721
+ this.forceCloseConnection();
47701
47722
  this.isReconnecting = true;
47702
47723
  this.reconnectAttempts++;
47703
47724
  const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
47704
- log2.info(`Reconnecting... (attempt ${this.reconnectAttempts})`);
47725
+ wsLogger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
47705
47726
  this.emit("reconnecting", this.reconnectAttempts);
47706
47727
  this.reconnectTimeout = setTimeout(() => {
47707
47728
  this.reconnectTimeout = null;
@@ -47710,45 +47731,24 @@ class SlackClient extends EventEmitter2 {
47710
47731
  return;
47711
47732
  }
47712
47733
  this.connect().catch((err) => {
47713
- log2.error(`Reconnection failed: ${err}`);
47734
+ wsLogger.error(`Reconnection failed: ${err}`);
47735
+ this.scheduleReconnect();
47714
47736
  });
47715
47737
  }, delay);
47716
47738
  }
47717
- startHeartbeat() {
47718
- this.stopHeartbeat();
47719
- this.lastMessageAt = Date.now();
47720
- this.heartbeatInterval = setInterval(() => {
47721
- const silentFor = Date.now() - this.lastMessageAt;
47722
- if (silentFor > this.HEARTBEAT_TIMEOUT_MS) {
47723
- log2.warn(`Connection dead (no activity for ${Math.round(silentFor / 1000)}s), reconnecting...`);
47724
- this.stopHeartbeat();
47725
- if (this.ws) {
47726
- this.ws.close();
47727
- }
47728
- return;
47729
- }
47730
- wsLogger.debug(`Heartbeat check (last activity ${Math.round(silentFor / 1000)}s ago)`);
47731
- }, this.HEARTBEAT_INTERVAL_MS);
47732
- }
47733
- stopHeartbeat() {
47734
- if (this.heartbeatInterval) {
47735
- clearInterval(this.heartbeatInterval);
47736
- this.heartbeatInterval = null;
47737
- }
47738
- }
47739
47739
  async recoverMissedMessages() {
47740
47740
  if (!this.lastProcessedTs) {
47741
47741
  return;
47742
47742
  }
47743
- log2.info(`Recovering missed messages after ts ${this.lastProcessedTs}...`);
47743
+ log3.info(`Recovering missed messages after ts ${this.lastProcessedTs}...`);
47744
47744
  try {
47745
47745
  const response = await this.api("GET", `conversations.history?channel=${this.channelId}&oldest=${this.lastProcessedTs}&inclusive=false&limit=100`);
47746
47746
  const messages = response.messages || [];
47747
47747
  if (messages.length === 0) {
47748
- log2.info("No missed messages to recover");
47748
+ log3.info("No missed messages to recover");
47749
47749
  return;
47750
47750
  }
47751
- log2.info(`Recovered ${messages.length} missed message(s)`);
47751
+ log3.info(`Recovered ${messages.length} missed message(s)`);
47752
47752
  const sortedMessages = messages.sort((a, b) => parseFloat(a.ts) - parseFloat(b.ts));
47753
47753
  for (const message of sortedMessages) {
47754
47754
  if (message.user === this.botUserId || message.bot_id) {
@@ -47763,7 +47763,7 @@ class SlackClient extends EventEmitter2 {
47763
47763
  }
47764
47764
  }
47765
47765
  } catch (err) {
47766
- log2.warn(`Failed to recover missed messages: ${err}`);
47766
+ log3.warn(`Failed to recover missed messages: ${err}`);
47767
47767
  }
47768
47768
  }
47769
47769
  disconnect() {
@@ -47774,15 +47774,7 @@ class SlackClient extends EventEmitter2 {
47774
47774
  clearTimeout(this.reconnectTimeout);
47775
47775
  this.reconnectTimeout = null;
47776
47776
  }
47777
- if (this.ws) {
47778
- this.ws.close();
47779
- this.ws = null;
47780
- }
47781
- }
47782
- prepareForReconnect() {
47783
- wsLogger.debug("Preparing for reconnect (resetting intentional disconnect flag)");
47784
- this.isIntentionalDisconnect = false;
47785
- this.reconnectAttempts = 0;
47777
+ this.forceCloseConnection();
47786
47778
  }
47787
47779
  async fetchBotUser() {
47788
47780
  const response = await this.api("POST", "auth.test");
@@ -47805,17 +47797,17 @@ class SlackClient extends EventEmitter2 {
47805
47797
  }
47806
47798
  const cached = this.userCache.get(userId);
47807
47799
  if (cached) {
47808
- log2.debug(`User ${userId} found in cache: @${cached.name}`);
47800
+ log3.debug(`User ${userId} found in cache: @${cached.name}`);
47809
47801
  return this.normalizePlatformUser(cached);
47810
47802
  }
47811
47803
  try {
47812
47804
  const response = await this.api("GET", `users.info?user=${userId}`);
47813
47805
  this.userCache.set(userId, response.user);
47814
47806
  this.usernameToIdCache.set(response.user.name, userId);
47815
- log2.debug(`User ${userId} fetched: @${response.user.name}`);
47807
+ log3.debug(`User ${userId} fetched: @${response.user.name}`);
47816
47808
  return this.normalizePlatformUser(response.user);
47817
47809
  } catch (err) {
47818
- log2.warn(`Failed to get user ${userId}: ${err}`);
47810
+ log3.warn(`Failed to get user ${userId}: ${err}`);
47819
47811
  return null;
47820
47812
  }
47821
47813
  }
@@ -47825,7 +47817,7 @@ class SlackClient extends EventEmitter2 {
47825
47817
  return this.getUser(cachedId);
47826
47818
  }
47827
47819
  try {
47828
- log2.debug(`Looking up user by username: @${username}`);
47820
+ log3.debug(`Looking up user by username: @${username}`);
47829
47821
  let cursor;
47830
47822
  do {
47831
47823
  const params = cursor ? `cursor=${cursor}&limit=200` : "limit=200";
@@ -47834,28 +47826,19 @@ class SlackClient extends EventEmitter2 {
47834
47826
  this.userCache.set(user.id, user);
47835
47827
  this.usernameToIdCache.set(user.name, user.id);
47836
47828
  if (user.name === username) {
47837
- log2.debug(`User @${username} found: ${user.id}`);
47829
+ log3.debug(`User @${username} found: ${user.id}`);
47838
47830
  return this.normalizePlatformUser(user);
47839
47831
  }
47840
47832
  }
47841
47833
  cursor = response.response_metadata?.next_cursor;
47842
47834
  } while (cursor);
47843
- log2.warn(`User @${username} not found`);
47835
+ log3.warn(`User @${username} not found`);
47844
47836
  return null;
47845
47837
  } catch (err) {
47846
- log2.warn(`Failed to lookup user @${username}: ${err}`);
47838
+ log3.warn(`Failed to lookup user @${username}: ${err}`);
47847
47839
  return null;
47848
47840
  }
47849
47841
  }
47850
- isUserAllowed(username) {
47851
- if (this.allowedUsers.length === 0) {
47852
- return true;
47853
- }
47854
- return this.allowedUsers.includes(username);
47855
- }
47856
- getBotName() {
47857
- return this.botName;
47858
- }
47859
47842
  getMcpConfig() {
47860
47843
  return {
47861
47844
  type: "slack",
@@ -47919,17 +47902,6 @@ class SlackClient extends EventEmitter2 {
47919
47902
  createAt: Math.floor(parseFloat(response.ts) * 1000)
47920
47903
  };
47921
47904
  }
47922
- async createInteractivePost(message, reactions, threadId) {
47923
- const post = await this.createPost(message, threadId, { unfurl: false });
47924
- for (const emoji of reactions) {
47925
- try {
47926
- await this.addReaction(post.id, emoji);
47927
- } catch (err) {
47928
- log2.warn(`Failed to add reaction ${emoji}: ${err}`);
47929
- }
47930
- }
47931
- return post;
47932
- }
47933
47905
  async getPost(postId) {
47934
47906
  try {
47935
47907
  const response = await this.api("GET", `conversations.history?channel=${this.channelId}&latest=${postId}&oldest=${postId}&inclusive=true&limit=1`);
@@ -47938,19 +47910,19 @@ class SlackClient extends EventEmitter2 {
47938
47910
  }
47939
47911
  return null;
47940
47912
  } catch (err) {
47941
- log2.debug(`Post ${postId.substring(0, 12)} not found: ${err}`);
47913
+ log3.debug(`Post ${postId.substring(0, 12)} not found: ${err}`);
47942
47914
  return null;
47943
47915
  }
47944
47916
  }
47945
47917
  async deletePost(postId) {
47946
- log2.debug(`Deleting post ${postId.substring(0, 12)}`);
47918
+ log3.debug(`Deleting post ${postId.substring(0, 12)}`);
47947
47919
  await this.api("POST", "chat.delete", {
47948
47920
  channel: this.channelId,
47949
47921
  ts: postId
47950
47922
  });
47951
47923
  }
47952
47924
  async pinPost(postId) {
47953
- log2.debug(`Pinning post ${postId.substring(0, 12)}`);
47925
+ log3.debug(`Pinning post ${postId.substring(0, 12)}`);
47954
47926
  try {
47955
47927
  await this.api("POST", "pins.add", {
47956
47928
  channel: this.channelId,
@@ -47958,14 +47930,14 @@ class SlackClient extends EventEmitter2 {
47958
47930
  }, 0, ["already_pinned"]);
47959
47931
  } catch (err) {
47960
47932
  if (err instanceof Error && err.message.includes("already_pinned")) {
47961
- log2.debug(`Post ${postId.substring(0, 12)} already pinned`);
47933
+ log3.debug(`Post ${postId.substring(0, 12)} already pinned`);
47962
47934
  return;
47963
47935
  }
47964
47936
  throw err;
47965
47937
  }
47966
47938
  }
47967
47939
  async unpinPost(postId) {
47968
- log2.debug(`Unpinning post ${postId.substring(0, 12)}`);
47940
+ log3.debug(`Unpinning post ${postId.substring(0, 12)}`);
47969
47941
  try {
47970
47942
  await this.api("POST", "pins.remove", {
47971
47943
  channel: this.channelId,
@@ -47973,7 +47945,7 @@ class SlackClient extends EventEmitter2 {
47973
47945
  }, 0, ["no_pin"]);
47974
47946
  } catch (err) {
47975
47947
  if (err instanceof Error && err.message.includes("no_pin")) {
47976
- log2.debug(`Post ${postId.substring(0, 12)} was not pinned`);
47948
+ log3.debug(`Post ${postId.substring(0, 12)} was not pinned`);
47977
47949
  return;
47978
47950
  }
47979
47951
  throw err;
@@ -47991,7 +47963,7 @@ class SlackClient extends EventEmitter2 {
47991
47963
  if (message.length <= maxLength) {
47992
47964
  return message;
47993
47965
  }
47994
- log2.warn(`Truncating message from ${message.length} to ~${maxLength} chars`);
47966
+ log3.warn(`Truncating message from ${message.length} to ~${maxLength} chars`);
47995
47967
  return truncateMessageSafely(message, maxLength, "_... (truncated)_");
47996
47968
  }
47997
47969
  async getThreadHistory(threadId, options) {
@@ -48016,13 +47988,13 @@ class SlackClient extends EventEmitter2 {
48016
47988
  messages.sort((a, b) => a.createAt - b.createAt);
48017
47989
  return messages;
48018
47990
  } catch (err) {
48019
- log2.warn(`Failed to get thread history for ${threadId}: ${err}`);
47991
+ log3.warn(`Failed to get thread history for ${threadId}: ${err}`);
48020
47992
  return [];
48021
47993
  }
48022
47994
  }
48023
47995
  async addReaction(postId, emojiName) {
48024
47996
  const name = getEmojiName(emojiName);
48025
- log2.debug(`Adding reaction :${name}: to post ${postId.substring(0, 12)}`);
47997
+ log3.debug(`Adding reaction :${name}: to post ${postId.substring(0, 12)}`);
48026
47998
  await this.api("POST", "reactions.add", {
48027
47999
  channel: this.channelId,
48028
48000
  timestamp: postId,
@@ -48031,7 +48003,7 @@ class SlackClient extends EventEmitter2 {
48031
48003
  }
48032
48004
  async removeReaction(postId, emojiName) {
48033
48005
  const name = getEmojiName(emojiName);
48034
- log2.debug(`Removing reaction :${name}: from post ${postId.substring(0, 12)}`);
48006
+ log3.debug(`Removing reaction :${name}: from post ${postId.substring(0, 12)}`);
48035
48007
  await this.api("POST", "reactions.remove", {
48036
48008
  channel: this.channelId,
48037
48009
  timestamp: postId,
@@ -48057,7 +48029,7 @@ class SlackClient extends EventEmitter2 {
48057
48029
  }
48058
48030
  sendTyping(_threadId) {}
48059
48031
  async downloadFile(fileId) {
48060
- log2.debug(`Downloading file ${fileId}`);
48032
+ log3.debug(`Downloading file ${fileId}`);
48061
48033
  const fileInfo = await this.api("GET", `files.info?file=${fileId}`);
48062
48034
  const downloadUrl = fileInfo.file.url_private_download || fileInfo.file.url_private;
48063
48035
  if (!downloadUrl) {
@@ -48069,11 +48041,11 @@ class SlackClient extends EventEmitter2 {
48069
48041
  }
48070
48042
  });
48071
48043
  if (!response.ok) {
48072
- log2.warn(`Failed to download file ${fileId}: ${response.status}`);
48044
+ log3.warn(`Failed to download file ${fileId}: ${response.status}`);
48073
48045
  throw new Error(`Failed to download file ${fileId}: ${response.status}`);
48074
48046
  }
48075
48047
  const arrayBuffer = await response.arrayBuffer();
48076
- log2.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
48048
+ log3.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
48077
48049
  return Buffer.from(arrayBuffer);
48078
48050
  }
48079
48051
  async getFileInfo(fileId) {
@@ -48082,10 +48054,10 @@ class SlackClient extends EventEmitter2 {
48082
48054
  }
48083
48055
  }
48084
48056
  // src/mattermost/api.ts
48085
- var log3 = createLogger("mm-api");
48057
+ var log4 = createLogger("mm-api");
48086
48058
  async function mattermostApi(config, method, path, body) {
48087
48059
  const url = `${config.url}/api/v4${path}`;
48088
- log3.debug(`API ${method} ${path}`);
48060
+ log4.debug(`API ${method} ${path}`);
48089
48061
  const response = await fetch(url, {
48090
48062
  method,
48091
48063
  headers: {
@@ -48096,10 +48068,10 @@ async function mattermostApi(config, method, path, body) {
48096
48068
  });
48097
48069
  if (!response.ok) {
48098
48070
  const text = await response.text();
48099
- log3.warn(`API ${method} ${path} failed: ${response.status} ${text.substring(0, 100)}`);
48071
+ log4.warn(`API ${method} ${path} failed: ${response.status} ${text.substring(0, 100)}`);
48100
48072
  throw new Error(`Mattermost API error ${response.status}: ${text}`);
48101
48073
  }
48102
- log3.debug(`API ${method} ${path} \u2192 ${response.status}`);
48074
+ log4.debug(`API ${method} ${path} \u2192 ${response.status}`);
48103
48075
  return response.json();
48104
48076
  }
48105
48077
  async function getMe(config) {
@@ -48109,7 +48081,7 @@ async function getUser(config, userId) {
48109
48081
  try {
48110
48082
  return await mattermostApi(config, "GET", `/users/${userId}`);
48111
48083
  } catch (err) {
48112
- log3.debug(`Failed to get user ${userId}: ${err}`);
48084
+ log4.debug(`Failed to get user ${userId}: ${err}`);
48113
48085
  return null;
48114
48086
  }
48115
48087
  }
@@ -48144,7 +48116,7 @@ async function createInteractivePost(config, channelId, message, reactions, root
48144
48116
  try {
48145
48117
  await addReaction(config, post.id, botUserId, emoji);
48146
48118
  } catch (err) {
48147
- log3.warn(`Failed to add reaction ${emoji}: ${err}`);
48119
+ log4.warn(`Failed to add reaction ${emoji}: ${err}`);
48148
48120
  }
48149
48121
  }
48150
48122
  return post;
@@ -48488,13 +48460,13 @@ class SlackPermissionApi {
48488
48460
  }
48489
48461
  }
48490
48462
  // src/session/manager.ts
48491
- import { EventEmitter as EventEmitter5 } from "events";
48463
+ import { EventEmitter as EventEmitter4 } from "events";
48492
48464
 
48493
48465
  // src/persistence/session-store.ts
48494
48466
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync } from "fs";
48495
48467
  import { homedir as homedir2 } from "os";
48496
48468
  import { join as join2 } from "path";
48497
- var log4 = createLogger("persist");
48469
+ var log5 = createLogger("persist");
48498
48470
  var STORE_VERSION = 2;
48499
48471
  var DEFAULT_CONFIG_DIR = join2(homedir2(), ".config", "claude-threads");
48500
48472
  var DEFAULT_SESSIONS_FILE = join2(DEFAULT_CONFIG_DIR, "sessions.json");
@@ -48519,13 +48491,13 @@ class SessionStore {
48519
48491
  load() {
48520
48492
  const sessions = new Map;
48521
48493
  if (!existsSync3(this.sessionsFile)) {
48522
- log4.debug("No sessions file found");
48494
+ log5.debug("No sessions file found");
48523
48495
  return sessions;
48524
48496
  }
48525
48497
  try {
48526
48498
  const data = JSON.parse(readFileSync3(this.sessionsFile, "utf-8"));
48527
48499
  if (data.version === 1) {
48528
- log4.info("Migrating sessions from v1 to v2 (adding platformId)");
48500
+ log5.info("Migrating sessions from v1 to v2 (adding platformId)");
48529
48501
  const newSessions = {};
48530
48502
  for (const [_oldKey, session] of Object.entries(data.sessions)) {
48531
48503
  const v1Session = session;
@@ -48539,7 +48511,7 @@ class SessionStore {
48539
48511
  data.version = 2;
48540
48512
  this.writeAtomic(data);
48541
48513
  } else if (data.version !== STORE_VERSION) {
48542
- log4.warn(`Sessions file version ${data.version} not supported, starting fresh`);
48514
+ log5.warn(`Sessions file version ${data.version} not supported, starting fresh`);
48543
48515
  return sessions;
48544
48516
  }
48545
48517
  for (const session of Object.values(data.sessions)) {
@@ -48548,9 +48520,9 @@ class SessionStore {
48548
48520
  const sessionId = `${session.platformId}:${session.threadId}`;
48549
48521
  sessions.set(sessionId, session);
48550
48522
  }
48551
- log4.debug(`Loaded ${sessions.size} active session(s)`);
48523
+ log5.debug(`Loaded ${sessions.size} active session(s)`);
48552
48524
  } catch (err) {
48553
- log4.error(`Failed to load sessions: ${err}`);
48525
+ log5.error(`Failed to load sessions: ${err}`);
48554
48526
  }
48555
48527
  return sessions;
48556
48528
  }
@@ -48559,7 +48531,7 @@ class SessionStore {
48559
48531
  data.sessions[sessionId] = session;
48560
48532
  this.writeAtomic(data);
48561
48533
  const shortId = sessionId.substring(0, 20);
48562
- log4.debug(`Saved session ${shortId}...`);
48534
+ log5.debug(`Saved session ${shortId}...`);
48563
48535
  }
48564
48536
  remove(sessionId) {
48565
48537
  const data = this.loadRaw();
@@ -48567,7 +48539,7 @@ class SessionStore {
48567
48539
  delete data.sessions[sessionId];
48568
48540
  this.writeAtomic(data);
48569
48541
  const shortId = sessionId.substring(0, 20);
48570
- log4.debug(`Removed session ${shortId}...`);
48542
+ log5.debug(`Removed session ${shortId}...`);
48571
48543
  }
48572
48544
  }
48573
48545
  softDelete(sessionId) {
@@ -48576,7 +48548,7 @@ class SessionStore {
48576
48548
  data.sessions[sessionId].cleanedAt = new Date().toISOString();
48577
48549
  this.writeAtomic(data);
48578
48550
  const shortId = sessionId.substring(0, 20);
48579
- log4.debug(`Soft-deleted session ${shortId}...`);
48551
+ log5.debug(`Soft-deleted session ${shortId}...`);
48580
48552
  }
48581
48553
  }
48582
48554
  cleanStale(maxAgeMs) {
@@ -48594,7 +48566,7 @@ class SessionStore {
48594
48566
  }
48595
48567
  if (staleIds.length > 0) {
48596
48568
  this.writeAtomic(data);
48597
- log4.debug(`Soft-deleted ${staleIds.length} stale session(s)`);
48569
+ log5.debug(`Soft-deleted ${staleIds.length} stale session(s)`);
48598
48570
  }
48599
48571
  return staleIds;
48600
48572
  }
@@ -48613,7 +48585,7 @@ class SessionStore {
48613
48585
  }
48614
48586
  if (removedCount > 0) {
48615
48587
  this.writeAtomic(data);
48616
- log4.debug(`Permanently removed ${removedCount} old session(s) from history`);
48588
+ log5.debug(`Permanently removed ${removedCount} old session(s) from history`);
48617
48589
  }
48618
48590
  return removedCount;
48619
48591
  }
@@ -48640,7 +48612,7 @@ class SessionStore {
48640
48612
  clear() {
48641
48613
  const data = this.loadRaw();
48642
48614
  this.writeAtomic({ version: STORE_VERSION, sessions: {}, stickyPostIds: data.stickyPostIds });
48643
- log4.debug("Cleared all sessions");
48615
+ log5.debug("Cleared all sessions");
48644
48616
  }
48645
48617
  saveStickyPostId(platformId, postId) {
48646
48618
  const data = this.loadRaw();
@@ -48649,7 +48621,7 @@ class SessionStore {
48649
48621
  }
48650
48622
  data.stickyPostIds[platformId] = postId;
48651
48623
  this.writeAtomic(data);
48652
- log4.debug(`Saved sticky post ID for ${platformId}: ${postId.substring(0, 8)}...`);
48624
+ log5.debug(`Saved sticky post ID for ${platformId}: ${postId.substring(0, 8)}...`);
48653
48625
  }
48654
48626
  getStickyPostIds() {
48655
48627
  const data = this.loadRaw();
@@ -48660,7 +48632,7 @@ class SessionStore {
48660
48632
  if (data.stickyPostIds && data.stickyPostIds[platformId]) {
48661
48633
  delete data.stickyPostIds[platformId];
48662
48634
  this.writeAtomic(data);
48663
- log4.debug(`Removed sticky post ID for ${platformId}`);
48635
+ log5.debug(`Removed sticky post ID for ${platformId}`);
48664
48636
  }
48665
48637
  }
48666
48638
  getPlatformEnabledState() {
@@ -48678,7 +48650,7 @@ class SessionStore {
48678
48650
  }
48679
48651
  data.platformEnabledState[platformId] = enabled;
48680
48652
  this.writeAtomic(data);
48681
- log4.debug(`Set platform ${platformId} enabled state to ${enabled}`);
48653
+ log5.debug(`Set platform ${platformId} enabled state to ${enabled}`);
48682
48654
  }
48683
48655
  findByThread(platformId, threadId) {
48684
48656
  const sessionId = `${platformId}:${threadId}`;
@@ -48724,7 +48696,7 @@ import { join as join5 } from "path";
48724
48696
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync, readFileSync as readFileSync4 } from "fs";
48725
48697
  import { homedir as homedir3 } from "os";
48726
48698
  import { join as join3, dirname as dirname3 } from "path";
48727
- var log5 = createLogger("thread-log");
48699
+ var log6 = createLogger("thread-log");
48728
48700
  var LOGS_BASE_DIR = join3(homedir3(), ".claude-threads", "logs");
48729
48701
 
48730
48702
  class ThreadLoggerImpl {
@@ -48754,7 +48726,7 @@ class ThreadLoggerImpl {
48754
48726
  this.flushTimer = setInterval(() => {
48755
48727
  this.flushSync();
48756
48728
  }, this.flushIntervalMs);
48757
- log5.debug(`Thread logger initialized: ${this.logPath}`);
48729
+ log6.debug(`Thread logger initialized: ${this.logPath}`);
48758
48730
  }
48759
48731
  }
48760
48732
  isEnabled() {
@@ -48868,7 +48840,7 @@ class ThreadLoggerImpl {
48868
48840
  this.flushTimer = null;
48869
48841
  }
48870
48842
  this.flushSync();
48871
- log5.debug(`Thread logger closed: ${this.logPath}`);
48843
+ log6.debug(`Thread logger closed: ${this.logPath}`);
48872
48844
  }
48873
48845
  addEntry(entry) {
48874
48846
  this.buffer.push(entry);
@@ -48886,7 +48858,7 @@ class ThreadLoggerImpl {
48886
48858
  appendFileSync(this.logPath, lines, "utf8");
48887
48859
  this.buffer = [];
48888
48860
  } catch (err) {
48889
- log5.error(`Failed to flush thread log: ${err}`);
48861
+ log6.error(`Failed to flush thread log: ${err}`);
48890
48862
  }
48891
48863
  }
48892
48864
  }
@@ -48937,25 +48909,25 @@ function cleanupOldLogs(retentionDays = 30) {
48937
48909
  if (fileStat.mtimeMs < cutoffMs) {
48938
48910
  unlinkSync(filePath);
48939
48911
  deletedCount++;
48940
- log5.debug(`Deleted old log file: ${filePath}`);
48912
+ log6.debug(`Deleted old log file: ${filePath}`);
48941
48913
  }
48942
48914
  } catch (err) {
48943
- log5.warn(`Failed to check/delete log file ${filePath}: ${err}`);
48915
+ log6.warn(`Failed to check/delete log file ${filePath}: ${err}`);
48944
48916
  }
48945
48917
  }
48946
48918
  try {
48947
48919
  const remaining = readdirSync(platformDir);
48948
48920
  if (remaining.length === 0) {
48949
48921
  rmdirSync(platformDir);
48950
- log5.debug(`Removed empty platform log directory: ${platformDir}`);
48922
+ log6.debug(`Removed empty platform log directory: ${platformDir}`);
48951
48923
  }
48952
48924
  } catch {}
48953
48925
  }
48954
48926
  if (deletedCount > 0) {
48955
- log5.info(`Cleaned up ${deletedCount} old log file(s)`);
48927
+ log6.info(`Cleaned up ${deletedCount} old log file(s)`);
48956
48928
  }
48957
48929
  } catch (err) {
48958
- log5.error(`Failed to clean up old logs: ${err}`);
48930
+ log6.error(`Failed to clean up old logs: ${err}`);
48959
48931
  }
48960
48932
  return deletedCount;
48961
48933
  }
@@ -48964,16 +48936,16 @@ function getLogFilePath(platformId, sessionId) {
48964
48936
  }
48965
48937
  function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
48966
48938
  const logPath = getLogFilePath(platformId, sessionId);
48967
- log5.debug(`Reading log entries from: ${logPath}`);
48939
+ log6.debug(`Reading log entries from: ${logPath}`);
48968
48940
  if (!existsSync4(logPath)) {
48969
- log5.debug(`Log file does not exist: ${logPath}`);
48941
+ log6.debug(`Log file does not exist: ${logPath}`);
48970
48942
  return [];
48971
48943
  }
48972
48944
  try {
48973
48945
  const content = readFileSync4(logPath, "utf8");
48974
48946
  const lines = content.trim().split(`
48975
48947
  `);
48976
- log5.debug(`Log file has ${lines.length} lines`);
48948
+ log6.debug(`Log file has ${lines.length} lines`);
48977
48949
  const recentLines = lines.slice(-maxLines);
48978
48950
  const entries = [];
48979
48951
  for (const line of recentLines) {
@@ -48983,10 +48955,10 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
48983
48955
  entries.push(JSON.parse(line));
48984
48956
  } catch {}
48985
48957
  }
48986
- log5.debug(`Parsed ${entries.length} log entries`);
48958
+ log6.debug(`Parsed ${entries.length} log entries`);
48987
48959
  return entries;
48988
48960
  } catch (err) {
48989
- log5.error(`Failed to read log file: ${err}`);
48961
+ log6.error(`Failed to read log file: ${err}`);
48990
48962
  return [];
48991
48963
  }
48992
48964
  }
@@ -48997,11 +48969,11 @@ import { randomUUID } from "crypto";
48997
48969
  import * as path from "path";
48998
48970
  import * as fs from "fs/promises";
48999
48971
  import { homedir as homedir4 } from "os";
49000
- var log6 = createLogger("git-wt");
48972
+ var log7 = createLogger("git-wt");
49001
48973
  var WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
49002
48974
  async function execGit(args, cwd) {
49003
48975
  const cmd = `git ${args.join(" ")}`;
49004
- log6.debug(`Executing: ${cmd}`);
48976
+ log7.debug(`Executing: ${cmd}`);
49005
48977
  return new Promise((resolve2, reject) => {
49006
48978
  const proc = spawn("git", args, { cwd });
49007
48979
  let stdout = "";
@@ -49014,15 +48986,15 @@ async function execGit(args, cwd) {
49014
48986
  });
49015
48987
  proc.on("close", (code) => {
49016
48988
  if (code === 0) {
49017
- log6.debug(`${cmd} \u2192 success`);
48989
+ log7.debug(`${cmd} \u2192 success`);
49018
48990
  resolve2(stdout.trim());
49019
48991
  } else {
49020
- log6.debug(`${cmd} \u2192 failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
48992
+ log7.debug(`${cmd} \u2192 failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
49021
48993
  reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
49022
48994
  }
49023
48995
  });
49024
48996
  proc.on("error", (err) => {
49025
- log6.warn(`${cmd} \u2192 error: ${err}`);
48997
+ log7.warn(`${cmd} \u2192 error: ${err}`);
49026
48998
  reject(err);
49027
48999
  });
49028
49000
  });
@@ -49032,7 +49004,7 @@ async function isGitRepository(dir) {
49032
49004
  await execGit(["rev-parse", "--git-dir"], dir);
49033
49005
  return true;
49034
49006
  } catch (err) {
49035
- log6.debug(`Not a git repository: ${dir} (${err})`);
49007
+ log7.debug(`Not a git repository: ${dir} (${err})`);
49036
49008
  return false;
49037
49009
  }
49038
49010
  }
@@ -49167,7 +49139,7 @@ async function detectWorktreeInfo(workingDir) {
49167
49139
  const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
49168
49140
  const branch = branchOutput?.trim();
49169
49141
  if (!branch) {
49170
- log6.debug(`Could not detect branch for worktree at ${workingDir}`);
49142
+ log7.debug(`Could not detect branch for worktree at ${workingDir}`);
49171
49143
  return null;
49172
49144
  }
49173
49145
  const gitDirOutput = await execGit(["rev-parse", "--git-common-dir"], workingDir);
@@ -49179,45 +49151,45 @@ async function detectWorktreeInfo(workingDir) {
49179
49151
  repoRoot = repoRoot.slice(0, -4);
49180
49152
  }
49181
49153
  }
49182
- log6.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
49154
+ log7.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
49183
49155
  return {
49184
49156
  worktreePath: workingDir,
49185
49157
  branch,
49186
49158
  repoRoot: repoRoot || workingDir
49187
49159
  };
49188
49160
  } catch (err) {
49189
- log6.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
49161
+ log7.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
49190
49162
  return null;
49191
49163
  }
49192
49164
  }
49193
49165
  async function createWorktree(repoRoot, branch, targetDir) {
49194
- log6.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
49166
+ log7.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
49195
49167
  const parentDir = path.dirname(targetDir);
49196
- log6.debug(`Creating parent directory: ${parentDir}`);
49168
+ log7.debug(`Creating parent directory: ${parentDir}`);
49197
49169
  await fs.mkdir(parentDir, { recursive: true });
49198
49170
  const exists = await branchExists(repoRoot, branch);
49199
49171
  if (exists) {
49200
- log6.debug(`Branch '${branch}' exists, adding worktree`);
49172
+ log7.debug(`Branch '${branch}' exists, adding worktree`);
49201
49173
  await execGit(["worktree", "add", targetDir, branch], repoRoot);
49202
49174
  } else {
49203
- log6.debug(`Branch '${branch}' does not exist, creating with worktree`);
49175
+ log7.debug(`Branch '${branch}' does not exist, creating with worktree`);
49204
49176
  await execGit(["worktree", "add", "-b", branch, targetDir], repoRoot);
49205
49177
  }
49206
- log6.info(`Worktree created successfully: ${targetDir}`);
49178
+ log7.info(`Worktree created successfully: ${targetDir}`);
49207
49179
  return targetDir;
49208
49180
  }
49209
49181
  async function removeWorktree(repoRoot, worktreePath) {
49210
- log6.info(`Removing worktree: ${worktreePath}`);
49182
+ log7.info(`Removing worktree: ${worktreePath}`);
49211
49183
  try {
49212
49184
  await execGit(["worktree", "remove", worktreePath], repoRoot);
49213
- log6.debug("Worktree removed cleanly");
49185
+ log7.debug("Worktree removed cleanly");
49214
49186
  } catch (err) {
49215
- log6.debug(`Clean remove failed (${err}), trying force remove`);
49187
+ log7.debug(`Clean remove failed (${err}), trying force remove`);
49216
49188
  await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
49217
49189
  }
49218
- log6.debug("Pruning stale worktree references");
49190
+ log7.debug("Pruning stale worktree references");
49219
49191
  await execGit(["worktree", "prune"], repoRoot);
49220
- log6.info("Worktree removed and pruned successfully");
49192
+ log7.info("Worktree removed and pruned successfully");
49221
49193
  }
49222
49194
  async function findWorktreeByBranch(repoRoot, branch) {
49223
49195
  const worktrees = await listWorktrees(repoRoot);
@@ -49258,14 +49230,14 @@ async function writeMetadataStore(store) {
49258
49230
  await fs.mkdir(path.dirname(METADATA_STORE_PATH), { recursive: true });
49259
49231
  await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), "utf-8");
49260
49232
  } catch (err) {
49261
- log6.warn(`Failed to write worktree metadata store: ${err}`);
49233
+ log7.warn(`Failed to write worktree metadata store: ${err}`);
49262
49234
  }
49263
49235
  }
49264
49236
  async function writeWorktreeMetadata(worktreePath, metadata) {
49265
49237
  const store = await readMetadataStore();
49266
49238
  store[worktreePath] = metadata;
49267
49239
  await writeMetadataStore(store);
49268
- log6.debug(`Wrote worktree metadata for: ${worktreePath}`);
49240
+ log7.debug(`Wrote worktree metadata for: ${worktreePath}`);
49269
49241
  }
49270
49242
  async function readWorktreeMetadata(worktreePath) {
49271
49243
  const store = await readMetadataStore();
@@ -49288,12 +49260,12 @@ async function removeWorktreeMetadata(worktreePath) {
49288
49260
  if (store[worktreePath]) {
49289
49261
  delete store[worktreePath];
49290
49262
  await writeMetadataStore(store);
49291
- log6.debug(`Removed worktree metadata for: ${worktreePath}`);
49263
+ log7.debug(`Removed worktree metadata for: ${worktreePath}`);
49292
49264
  }
49293
49265
  }
49294
49266
 
49295
49267
  // src/cleanup/scheduler.ts
49296
- var log7 = createLogger("cleanup");
49268
+ var log8 = createLogger("cleanup");
49297
49269
  var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
49298
49270
  var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
49299
49271
 
@@ -49316,17 +49288,17 @@ class CleanupScheduler {
49316
49288
  }
49317
49289
  start() {
49318
49290
  if (this.isRunning) {
49319
- log7.debug("Cleanup scheduler already running");
49291
+ log8.debug("Cleanup scheduler already running");
49320
49292
  return;
49321
49293
  }
49322
49294
  this.isRunning = true;
49323
- log7.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
49295
+ log8.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
49324
49296
  this.runCleanup().catch((err) => {
49325
- log7.warn(`Initial cleanup failed: ${err}`);
49297
+ log8.warn(`Initial cleanup failed: ${err}`);
49326
49298
  });
49327
49299
  this.timer = setInterval(() => {
49328
49300
  this.runCleanup().catch((err) => {
49329
- log7.warn(`Periodic cleanup failed: ${err}`);
49301
+ log8.warn(`Periodic cleanup failed: ${err}`);
49330
49302
  });
49331
49303
  }, this.intervalMs);
49332
49304
  }
@@ -49336,11 +49308,11 @@ class CleanupScheduler {
49336
49308
  this.timer = null;
49337
49309
  }
49338
49310
  this.isRunning = false;
49339
- log7.debug("Cleanup scheduler stopped");
49311
+ log8.debug("Cleanup scheduler stopped");
49340
49312
  }
49341
49313
  async runCleanup() {
49342
49314
  const startTime = Date.now();
49343
- log7.debug("Running background cleanup...");
49315
+ log8.debug("Running background cleanup...");
49344
49316
  const stats = {
49345
49317
  logsDeleted: 0,
49346
49318
  worktreesCleaned: 0,
@@ -49366,9 +49338,9 @@ class CleanupScheduler {
49366
49338
  const elapsed = Date.now() - startTime;
49367
49339
  const totalCleaned = stats.logsDeleted + stats.worktreesCleaned + stats.metadataCleaned;
49368
49340
  if (totalCleaned > 0 || stats.errors.length > 0) {
49369
- log7.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
49341
+ log8.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
49370
49342
  } else {
49371
- log7.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
49343
+ log8.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
49372
49344
  }
49373
49345
  return stats;
49374
49346
  }
@@ -49381,7 +49353,7 @@ class CleanupScheduler {
49381
49353
  const deleted = cleanupOldLogs(this.logRetentionDays);
49382
49354
  resolve2(deleted);
49383
49355
  } catch (err) {
49384
- log7.warn(`Log cleanup error: ${err}`);
49356
+ log8.warn(`Log cleanup error: ${err}`);
49385
49357
  resolve2(0);
49386
49358
  }
49387
49359
  });
@@ -49390,7 +49362,7 @@ class CleanupScheduler {
49390
49362
  const worktreesDir = getWorktreesDir();
49391
49363
  const result = { cleaned: 0, metadata: 0 };
49392
49364
  if (!existsSync5(worktreesDir)) {
49393
- log7.debug("No worktrees directory exists, nothing to clean");
49365
+ log8.debug("No worktrees directory exists, nothing to clean");
49394
49366
  return result;
49395
49367
  }
49396
49368
  const persisted = this.sessionStore.load();
@@ -49408,7 +49380,7 @@ class CleanupScheduler {
49408
49380
  continue;
49409
49381
  const worktreePath = join5(worktreesDir, entry.name);
49410
49382
  if (activeWorktrees.has(worktreePath)) {
49411
- log7.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
49383
+ log8.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
49412
49384
  continue;
49413
49385
  }
49414
49386
  const meta = await readWorktreeMetadata(worktreePath);
@@ -49418,7 +49390,7 @@ class CleanupScheduler {
49418
49390
  const lastActivity = new Date(meta.lastActivityAt).getTime();
49419
49391
  const age = now - lastActivity;
49420
49392
  if (meta.sessionId && age < this.maxWorktreeAgeMs) {
49421
- log7.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
49393
+ log8.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
49422
49394
  continue;
49423
49395
  }
49424
49396
  const merged = age >= this.maxWorktreeAgeMs ? await isBranchMerged(meta.repoRoot, meta.branch).catch(() => false) : false;
@@ -49429,7 +49401,7 @@ class CleanupScheduler {
49429
49401
  shouldCleanup = true;
49430
49402
  cleanupReason = `inactive for ${Math.round(age / 3600000)}h`;
49431
49403
  } else {
49432
- log7.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
49404
+ log8.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
49433
49405
  continue;
49434
49406
  }
49435
49407
  } else {
@@ -49438,7 +49410,7 @@ class CleanupScheduler {
49438
49410
  }
49439
49411
  if (!shouldCleanup)
49440
49412
  continue;
49441
- log7.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
49413
+ log8.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
49442
49414
  try {
49443
49415
  if (meta?.repoRoot) {
49444
49416
  await removeWorktree(meta.repoRoot, worktreePath);
@@ -49449,19 +49421,19 @@ class CleanupScheduler {
49449
49421
  await removeWorktreeMetadata(worktreePath);
49450
49422
  result.metadata++;
49451
49423
  } catch (err) {
49452
- log7.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
49424
+ log8.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
49453
49425
  try {
49454
49426
  await rm(worktreePath, { recursive: true, force: true });
49455
49427
  result.cleaned++;
49456
49428
  await removeWorktreeMetadata(worktreePath);
49457
49429
  result.metadata++;
49458
49430
  } catch (rmErr) {
49459
- log7.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
49431
+ log8.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
49460
49432
  }
49461
49433
  }
49462
49434
  }
49463
49435
  } catch (err) {
49464
- log7.warn(`Failed to scan worktrees directory: ${err}`);
49436
+ log8.warn(`Failed to scan worktrees directory: ${err}`);
49465
49437
  }
49466
49438
  return result;
49467
49439
  }
@@ -49528,13 +49500,13 @@ function getSessionStatus(session) {
49528
49500
 
49529
49501
  // src/claude/cli.ts
49530
49502
  import { spawn as spawn2 } from "child_process";
49531
- import { EventEmitter as EventEmitter3 } from "events";
49503
+ import { EventEmitter as EventEmitter2 } from "events";
49532
49504
  import { resolve as resolve2, dirname as dirname5 } from "path";
49533
49505
  import { fileURLToPath as fileURLToPath2 } from "url";
49534
49506
  import { existsSync as existsSync6, readFileSync as readFileSync5, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
49535
49507
  import { tmpdir } from "os";
49536
49508
  import { join as join6 } from "path";
49537
- var log8 = createLogger("claude");
49509
+ var log9 = createLogger("claude");
49538
49510
  function cleanupBrowserBridgeSockets() {
49539
49511
  try {
49540
49512
  const tempDir = tmpdir();
@@ -49546,17 +49518,17 @@ function cleanupBrowserBridgeSockets() {
49546
49518
  const stats = statSync2(filePath);
49547
49519
  if (stats.isSocket()) {
49548
49520
  unlinkSync2(filePath);
49549
- log8.debug(`Removed stale browser bridge socket: ${file}`);
49521
+ log9.debug(`Removed stale browser bridge socket: ${file}`);
49550
49522
  }
49551
49523
  } catch {}
49552
49524
  }
49553
49525
  }
49554
49526
  } catch (err) {
49555
- log8.debug(`Browser bridge cleanup failed: ${err}`);
49527
+ log9.debug(`Browser bridge cleanup failed: ${err}`);
49556
49528
  }
49557
49529
  }
49558
49530
 
49559
- class ClaudeCli extends EventEmitter3 {
49531
+ class ClaudeCli extends EventEmitter2 {
49560
49532
  process = null;
49561
49533
  options;
49562
49534
  buffer = "";
@@ -50207,7 +50179,7 @@ import { existsSync as existsSync10 } from "fs";
50207
50179
 
50208
50180
  // src/utils/keep-alive.ts
50209
50181
  import { spawn as spawn3 } from "child_process";
50210
- var log9 = createLogger("keepalive");
50182
+ var log10 = createLogger("keepalive");
50211
50183
 
50212
50184
  class KeepAliveManager {
50213
50185
  activeSessionCount = 0;
@@ -50222,7 +50194,7 @@ class KeepAliveManager {
50222
50194
  if (!enabled && this.keepAliveProcess) {
50223
50195
  this.stopKeepAlive();
50224
50196
  }
50225
- log9.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
50197
+ log10.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
50226
50198
  }
50227
50199
  isEnabled() {
50228
50200
  return this.enabled;
@@ -50232,7 +50204,7 @@ class KeepAliveManager {
50232
50204
  }
50233
50205
  sessionStarted() {
50234
50206
  this.activeSessionCount++;
50235
- log9.debug(`Session started (${this.activeSessionCount} active)`);
50207
+ log10.debug(`Session started (${this.activeSessionCount} active)`);
50236
50208
  if (this.activeSessionCount === 1) {
50237
50209
  this.startKeepAlive();
50238
50210
  }
@@ -50241,7 +50213,7 @@ class KeepAliveManager {
50241
50213
  if (this.activeSessionCount > 0) {
50242
50214
  this.activeSessionCount--;
50243
50215
  }
50244
- log9.debug(`Session ended (${this.activeSessionCount} active)`);
50216
+ log10.debug(`Session ended (${this.activeSessionCount} active)`);
50245
50217
  if (this.activeSessionCount === 0) {
50246
50218
  this.stopKeepAlive();
50247
50219
  }
@@ -50255,11 +50227,11 @@ class KeepAliveManager {
50255
50227
  }
50256
50228
  startKeepAlive() {
50257
50229
  if (!this.enabled) {
50258
- log9.debug("Keep-alive disabled, skipping");
50230
+ log10.debug("Keep-alive disabled, skipping");
50259
50231
  return;
50260
50232
  }
50261
50233
  if (this.keepAliveProcess) {
50262
- log9.debug("Keep-alive already running");
50234
+ log10.debug("Keep-alive already running");
50263
50235
  return;
50264
50236
  }
50265
50237
  switch (this.platform) {
@@ -50273,12 +50245,12 @@ class KeepAliveManager {
50273
50245
  this.startWindowsKeepAlive();
50274
50246
  break;
50275
50247
  default:
50276
- log9.warn(`Keep-alive not supported on ${this.platform}`);
50248
+ log10.warn(`Keep-alive not supported on ${this.platform}`);
50277
50249
  }
50278
50250
  }
50279
50251
  stopKeepAlive() {
50280
50252
  if (this.keepAliveProcess) {
50281
- log9.debug("Stopping keep-alive");
50253
+ log10.debug("Stopping keep-alive");
50282
50254
  this.keepAliveProcess.kill();
50283
50255
  this.keepAliveProcess = null;
50284
50256
  }
@@ -50290,18 +50262,18 @@ class KeepAliveManager {
50290
50262
  detached: false
50291
50263
  });
50292
50264
  this.keepAliveProcess.on("error", (err) => {
50293
- log9.error(`Failed to start caffeinate: ${err.message}`);
50265
+ log10.error(`Failed to start caffeinate: ${err.message}`);
50294
50266
  this.keepAliveProcess = null;
50295
50267
  });
50296
50268
  this.keepAliveProcess.on("exit", (code) => {
50297
50269
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
50298
- log9.debug(`caffeinate exited with code ${code}`);
50270
+ log10.debug(`caffeinate exited with code ${code}`);
50299
50271
  }
50300
50272
  this.keepAliveProcess = null;
50301
50273
  });
50302
- log9.info("Sleep prevention active (caffeinate)");
50274
+ log10.info("Sleep prevention active (caffeinate)");
50303
50275
  } catch (err) {
50304
- log9.error(`Failed to start caffeinate: ${err}`);
50276
+ log10.error(`Failed to start caffeinate: ${err}`);
50305
50277
  }
50306
50278
  }
50307
50279
  startLinuxKeepAlive() {
@@ -50317,19 +50289,19 @@ class KeepAliveManager {
50317
50289
  detached: false
50318
50290
  });
50319
50291
  this.keepAliveProcess.on("error", (err) => {
50320
- log9.debug(`systemd-inhibit not available: ${err.message}`);
50292
+ log10.debug(`systemd-inhibit not available: ${err.message}`);
50321
50293
  this.keepAliveProcess = null;
50322
50294
  this.startLinuxKeepAliveFallback();
50323
50295
  });
50324
50296
  this.keepAliveProcess.on("exit", (code) => {
50325
50297
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
50326
- log9.debug(`systemd-inhibit exited with code ${code}`);
50298
+ log10.debug(`systemd-inhibit exited with code ${code}`);
50327
50299
  }
50328
50300
  this.keepAliveProcess = null;
50329
50301
  });
50330
- log9.info("Sleep prevention active (systemd-inhibit)");
50302
+ log10.info("Sleep prevention active (systemd-inhibit)");
50331
50303
  } catch (err) {
50332
- log9.debug(`Failed to start systemd-inhibit: ${err}`);
50304
+ log10.debug(`Failed to start systemd-inhibit: ${err}`);
50333
50305
  this.startLinuxKeepAliveFallback();
50334
50306
  }
50335
50307
  }
@@ -50343,15 +50315,15 @@ class KeepAliveManager {
50343
50315
  detached: false
50344
50316
  });
50345
50317
  this.keepAliveProcess.on("error", (err) => {
50346
- log9.warn(`Linux keep-alive fallback not available: ${err.message}`);
50318
+ log10.warn(`Linux keep-alive fallback not available: ${err.message}`);
50347
50319
  this.keepAliveProcess = null;
50348
50320
  });
50349
50321
  this.keepAliveProcess.on("exit", () => {
50350
50322
  this.keepAliveProcess = null;
50351
50323
  });
50352
- log9.info("Sleep prevention active (xdg-screensaver)");
50324
+ log10.info("Sleep prevention active (xdg-screensaver)");
50353
50325
  } catch (err) {
50354
- log9.warn(`Linux keep-alive not available: ${err}`);
50326
+ log10.warn(`Linux keep-alive not available: ${err}`);
50355
50327
  }
50356
50328
  }
50357
50329
  startWindowsKeepAlive() {
@@ -50376,25 +50348,25 @@ class KeepAliveManager {
50376
50348
  windowsHide: true
50377
50349
  });
50378
50350
  this.keepAliveProcess.on("error", (err) => {
50379
- log9.warn(`Windows keep-alive not available: ${err.message}`);
50351
+ log10.warn(`Windows keep-alive not available: ${err.message}`);
50380
50352
  this.keepAliveProcess = null;
50381
50353
  });
50382
50354
  this.keepAliveProcess.on("exit", (code) => {
50383
50355
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
50384
- log9.debug(`PowerShell keep-alive exited with code ${code}`);
50356
+ log10.debug(`PowerShell keep-alive exited with code ${code}`);
50385
50357
  }
50386
50358
  this.keepAliveProcess = null;
50387
50359
  });
50388
- log9.info("Sleep prevention active (SetThreadExecutionState)");
50360
+ log10.info("Sleep prevention active (SetThreadExecutionState)");
50389
50361
  } catch (err) {
50390
- log9.warn(`Windows keep-alive not available: ${err}`);
50362
+ log10.warn(`Windows keep-alive not available: ${err}`);
50391
50363
  }
50392
50364
  }
50393
50365
  }
50394
50366
  var keepAlive = new KeepAliveManager;
50395
50367
 
50396
50368
  // src/utils/error-handler/index.ts
50397
- var log10 = createLogger("error");
50369
+ var log11 = createLogger("error");
50398
50370
 
50399
50371
  class SessionError extends Error {
50400
50372
  sessionId;
@@ -50420,19 +50392,19 @@ async function handleError(error, context, severity = "recoverable") {
50420
50392
  const sessionPart = sessionId ? ` (${formatShortId(sessionId)})` : "";
50421
50393
  const logMessage = `${context.action}${sessionPart}: ${message}`;
50422
50394
  if (severity === "recoverable") {
50423
- log10.warn(logMessage);
50395
+ log11.warn(logMessage);
50424
50396
  } else {
50425
- log10.error(logMessage, error instanceof Error ? error : undefined);
50397
+ log11.error(logMessage, error instanceof Error ? error : undefined);
50426
50398
  }
50427
50399
  if (context.details) {
50428
- log10.debugJson("Error details", context.details);
50400
+ log11.debugJson("Error details", context.details);
50429
50401
  }
50430
50402
  if (context.notifyUser && context.session) {
50431
50403
  try {
50432
50404
  const fmt = context.session.platform.getFormatter();
50433
50405
  await context.session.platform.createPost(`\u26A0\uFE0F ${fmt.formatBold("Error")}: ${context.action} failed. Please try again.`, context.session.threadId);
50434
50406
  } catch (notifyError) {
50435
- log10.warn(`Could not notify user: ${notifyError}`);
50407
+ log11.warn(`Could not notify user: ${notifyError}`);
50436
50408
  }
50437
50409
  }
50438
50410
  if (severity === "session-fatal" || severity === "system-fatal") {
@@ -50459,7 +50431,7 @@ async function logAndNotify(error, context) {
50459
50431
  }
50460
50432
  function logSilentError(context, error) {
50461
50433
  const message = error instanceof Error ? error.message : String(error);
50462
- log10.debug(`[${context}] Silently caught: ${message}`);
50434
+ log11.debug(`[${context}] Silently caught: ${message}`);
50463
50435
  }
50464
50436
 
50465
50437
  // src/utils/session-log.ts
@@ -50474,8 +50446,8 @@ function createSessionLog(baseLog) {
50474
50446
 
50475
50447
  // src/operations/post-helpers/index.ts
50476
50448
  init_emoji();
50477
- var log11 = createLogger("helpers");
50478
- var sessionLog = createSessionLog(log11);
50449
+ var log12 = createLogger("helpers");
50450
+ var sessionLog = createSessionLog(log12);
50479
50451
  var POST_TYPES = {
50480
50452
  info: "",
50481
50453
  success: "\u2705",
@@ -50558,7 +50530,7 @@ function updateLastMessage(session, post2) {
50558
50530
 
50559
50531
  // src/claude/quick-query.ts
50560
50532
  import { spawn as spawn4 } from "child_process";
50561
- var log12 = createLogger("query");
50533
+ var log13 = createLogger("query");
50562
50534
  async function quickQuery(options) {
50563
50535
  const {
50564
50536
  prompt,
@@ -50574,7 +50546,7 @@ async function quickQuery(options) {
50574
50546
  args.push("--system-prompt", systemPrompt);
50575
50547
  }
50576
50548
  args.push(prompt);
50577
- log12.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
50549
+ log13.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
50578
50550
  return new Promise((resolve4) => {
50579
50551
  let stdout = "";
50580
50552
  let stderr = "";
@@ -50588,7 +50560,7 @@ async function quickQuery(options) {
50588
50560
  if (!resolved) {
50589
50561
  resolved = true;
50590
50562
  proc.kill("SIGTERM");
50591
- log12.debug(`Quick query timed out after ${timeout}ms`);
50563
+ log13.debug(`Quick query timed out after ${timeout}ms`);
50592
50564
  resolve4({
50593
50565
  success: false,
50594
50566
  error: "timeout",
@@ -50606,7 +50578,7 @@ async function quickQuery(options) {
50606
50578
  if (!resolved) {
50607
50579
  resolved = true;
50608
50580
  clearTimeout(timeoutId);
50609
- log12.debug(`Quick query error: ${err.message}`);
50581
+ log13.debug(`Quick query error: ${err.message}`);
50610
50582
  resolve4({
50611
50583
  success: false,
50612
50584
  error: err.message,
@@ -50620,14 +50592,14 @@ async function quickQuery(options) {
50620
50592
  clearTimeout(timeoutId);
50621
50593
  const durationMs = Date.now() - startTime;
50622
50594
  if (code === 0 && stdout.trim()) {
50623
- log12.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
50595
+ log13.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
50624
50596
  resolve4({
50625
50597
  success: true,
50626
50598
  response: stdout.trim(),
50627
50599
  durationMs
50628
50600
  });
50629
50601
  } else {
50630
- log12.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
50602
+ log13.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
50631
50603
  resolve4({
50632
50604
  success: false,
50633
50605
  error: stderr || `exit code ${code}`,
@@ -50641,7 +50613,7 @@ async function quickQuery(options) {
50641
50613
  }
50642
50614
 
50643
50615
  // src/operations/suggestions/title.ts
50644
- var log13 = createLogger("title");
50616
+ var log14 = createLogger("title");
50645
50617
  var SUGGESTION_TIMEOUT = 15000;
50646
50618
  var MIN_TITLE_LENGTH = 3;
50647
50619
  var MAX_TITLE_LENGTH = 50;
@@ -50705,32 +50677,32 @@ function parseMetadata(response) {
50705
50677
  const titleMatch = response.match(/TITLE:\s*(.+)/i);
50706
50678
  const descMatch = response.match(/DESC:\s*(.+)/i);
50707
50679
  if (!titleMatch || !descMatch) {
50708
- log13.debug("Failed to parse title/description from response");
50680
+ log14.debug("Failed to parse title/description from response");
50709
50681
  return null;
50710
50682
  }
50711
50683
  let title = titleMatch[1].trim();
50712
50684
  let description = descMatch[1].trim();
50713
50685
  if (title.length < MIN_TITLE_LENGTH) {
50714
- log13.debug(`Title too short: ${title.length} chars`);
50686
+ log14.debug(`Title too short: ${title.length} chars`);
50715
50687
  return null;
50716
50688
  }
50717
50689
  if (title.length > MAX_TITLE_LENGTH) {
50718
- log13.debug(`Title too long (${title.length} chars), truncating`);
50690
+ log14.debug(`Title too long (${title.length} chars), truncating`);
50719
50691
  title = truncateAtWord(title, MAX_TITLE_LENGTH);
50720
50692
  }
50721
50693
  if (description.length < MIN_DESC_LENGTH) {
50722
- log13.debug(`Description too short: ${description.length} chars`);
50694
+ log14.debug(`Description too short: ${description.length} chars`);
50723
50695
  return null;
50724
50696
  }
50725
50697
  if (description.length > MAX_DESC_LENGTH) {
50726
- log13.debug(`Description too long (${description.length} chars), truncating`);
50698
+ log14.debug(`Description too long (${description.length} chars), truncating`);
50727
50699
  description = truncateAtWord(description, MAX_DESC_LENGTH);
50728
50700
  }
50729
50701
  return { title, description };
50730
50702
  }
50731
50703
  async function suggestSessionMetadata(context) {
50732
50704
  const logContext = typeof context === "string" ? context.substring(0, 50) : context.originalTask.substring(0, 50);
50733
- log13.debug(`Suggesting title for: "${logContext}..."`);
50705
+ log14.debug(`Suggesting title for: "${logContext}..."`);
50734
50706
  try {
50735
50707
  const result = await quickQuery({
50736
50708
  prompt: buildTitlePrompt(context),
@@ -50738,22 +50710,22 @@ async function suggestSessionMetadata(context) {
50738
50710
  timeout: SUGGESTION_TIMEOUT
50739
50711
  });
50740
50712
  if (!result.success || !result.response) {
50741
- log13.debug(`Title suggestion failed: ${result.error || "no response"}`);
50713
+ log14.debug(`Title suggestion failed: ${result.error || "no response"}`);
50742
50714
  return null;
50743
50715
  }
50744
50716
  const metadata = parseMetadata(result.response);
50745
50717
  if (metadata) {
50746
- log13.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
50718
+ log14.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
50747
50719
  }
50748
50720
  return metadata;
50749
50721
  } catch (err) {
50750
- log13.debug(`Title suggestion error: ${err}`);
50722
+ log14.debug(`Title suggestion error: ${err}`);
50751
50723
  return null;
50752
50724
  }
50753
50725
  }
50754
50726
 
50755
50727
  // src/operations/suggestions/tag.ts
50756
- var log14 = createLogger("tags");
50728
+ var log15 = createLogger("tags");
50757
50729
  var SUGGESTION_TIMEOUT2 = 15000;
50758
50730
  var MAX_TAGS = 3;
50759
50731
  var VALID_TAGS = [
@@ -50785,7 +50757,7 @@ function parseTags(response) {
50785
50757
  return [...new Set(tags)].slice(0, MAX_TAGS);
50786
50758
  }
50787
50759
  async function suggestSessionTags(userMessage) {
50788
- log14.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
50760
+ log15.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
50789
50761
  try {
50790
50762
  const result = await quickQuery({
50791
50763
  prompt: buildTagPrompt(userMessage),
@@ -50793,14 +50765,14 @@ async function suggestSessionTags(userMessage) {
50793
50765
  timeout: SUGGESTION_TIMEOUT2
50794
50766
  });
50795
50767
  if (!result.success || !result.response) {
50796
- log14.debug(`Tag suggestion failed: ${result.error || "no response"}`);
50768
+ log15.debug(`Tag suggestion failed: ${result.error || "no response"}`);
50797
50769
  return [];
50798
50770
  }
50799
50771
  const tags = parseTags(result.response);
50800
- log14.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
50772
+ log15.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
50801
50773
  return tags;
50802
50774
  } catch (err) {
50803
- log14.debug(`Tag suggestion error: ${err}`);
50775
+ log15.debug(`Tag suggestion error: ${err}`);
50804
50776
  return [];
50805
50777
  }
50806
50778
  }
@@ -53893,11 +53865,11 @@ class BugReportExecutor extends BaseExecutor {
53893
53865
  }
53894
53866
  // src/operations/executors/worktree-prompt.ts
53895
53867
  init_emoji();
53896
- var log15 = createLogger("wt-prompt");
53868
+ var log16 = createLogger("wt-prompt");
53897
53869
  // src/operations/message-manager-events.ts
53898
- import { EventEmitter as EventEmitter4 } from "events";
53870
+ import { EventEmitter as EventEmitter3 } from "events";
53899
53871
 
53900
- class TypedEventEmitter extends EventEmitter4 {
53872
+ class TypedEventEmitter extends EventEmitter3 {
53901
53873
  emit(event, data) {
53902
53874
  return super.emit(event, data);
53903
53875
  }
@@ -53922,7 +53894,7 @@ function createMessageManagerEvents() {
53922
53894
  }
53923
53895
 
53924
53896
  // src/operations/message-manager.ts
53925
- var log16 = createLogger("msg-mgr");
53897
+ var log17 = createLogger("msg-mgr");
53926
53898
 
53927
53899
  class MessageManager {
53928
53900
  platform;
@@ -54010,7 +53982,7 @@ class MessageManager {
54010
53982
  });
54011
53983
  }
54012
53984
  async handleEvent(event) {
54013
- const logger = log16.forSession(this.sessionId);
53985
+ const logger = log17.forSession(this.sessionId);
54014
53986
  const transformCtx = {
54015
53987
  sessionId: this.sessionId,
54016
53988
  formatter: this.platform.getFormatter(),
@@ -54060,7 +54032,7 @@ class MessageManager {
54060
54032
  }
54061
54033
  }
54062
54034
  async executeOperation(op) {
54063
- const logger = log16.forSession(this.sessionId);
54035
+ const logger = log17.forSession(this.sessionId);
54064
54036
  const ctx = this.getExecutorContext();
54065
54037
  try {
54066
54038
  if (isContentOp(op)) {
@@ -54128,7 +54100,7 @@ class MessageManager {
54128
54100
  threadId: this.threadId,
54129
54101
  platform: this.platform,
54130
54102
  formatter: this.platform.getFormatter(),
54131
- logger: log16.forSession(this.sessionId),
54103
+ logger: log17.forSession(this.sessionId),
54132
54104
  postTracker: this.postTracker,
54133
54105
  contentBreaker: this.contentBreaker,
54134
54106
  threadLogger: this.session.threadLogger,
@@ -54339,13 +54311,13 @@ class MessageManager {
54339
54311
  return this.systemExecutor.postSuccess(message, this.getExecutorContext());
54340
54312
  }
54341
54313
  async prepareForUserMessage() {
54342
- const logger = log16.forSession(this.sessionId);
54314
+ const logger = log17.forSession(this.sessionId);
54343
54315
  logger.debug("Preparing for new user message");
54344
54316
  await this.closeCurrentPost();
54345
54317
  await this.bumpTaskList();
54346
54318
  }
54347
54319
  async handleUserMessage(message, files, username, displayName) {
54348
- const logger = log16.forSession(this.sessionId);
54320
+ const logger = log17.forSession(this.sessionId);
54349
54321
  if (!this.session.claude.isRunning()) {
54350
54322
  logger.debug("Claude not running, ignoring user message");
54351
54323
  return false;
@@ -54368,7 +54340,7 @@ class MessageManager {
54368
54340
  return this.session;
54369
54341
  }
54370
54342
  async handleReaction(postId, emoji, user, action) {
54371
- const logger = log16.forSession(this.sessionId);
54343
+ const logger = log17.forSession(this.sessionId);
54372
54344
  const ctx = this.getExecutorContext();
54373
54345
  logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
54374
54346
  if (await this.questionApprovalExecutor.handleReaction(postId, emoji, user, action, ctx)) {
@@ -54676,7 +54648,7 @@ function formatPullRequestLink(url, formatter) {
54676
54648
  }
54677
54649
 
54678
54650
  // src/operations/sticky-message/handler.ts
54679
- var log17 = createLogger("sticky");
54651
+ var log18 = createLogger("sticky");
54680
54652
  var botStartedAt = new Date;
54681
54653
  function getPendingPrompts(session) {
54682
54654
  const prompts2 = [];
@@ -54751,21 +54723,21 @@ function initialize(store) {
54751
54723
  stickyPostIds.set(platformId, postId);
54752
54724
  }
54753
54725
  if (persistedIds.size > 0) {
54754
- log17.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
54726
+ log18.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
54755
54727
  }
54756
54728
  }
54757
54729
  function setPlatformPaused(platformId, paused) {
54758
54730
  if (paused) {
54759
54731
  pausedPlatforms.set(platformId, true);
54760
- log17.debug(`Platform ${platformId} marked as paused`);
54732
+ log18.debug(`Platform ${platformId} marked as paused`);
54761
54733
  } else {
54762
54734
  pausedPlatforms.delete(platformId);
54763
- log17.debug(`Platform ${platformId} marked as active`);
54735
+ log18.debug(`Platform ${platformId} marked as active`);
54764
54736
  }
54765
54737
  }
54766
54738
  function setShuttingDown(shuttingDown) {
54767
54739
  isShuttingDown = shuttingDown;
54768
- log17.debug(`Bot shutdown state: ${shuttingDown}`);
54740
+ log18.debug(`Bot shutdown state: ${shuttingDown}`);
54769
54741
  }
54770
54742
  function getTaskContent(session) {
54771
54743
  const taskState = session.messageManager?.getTaskListState();
@@ -55044,64 +55016,89 @@ async function updateStickyMessage(platform, sessions, config) {
55044
55016
  updateLocks.delete(platformId);
55045
55017
  }
55046
55018
  }
55019
+ async function validateLastMessageIds(platform, sessions) {
55020
+ const sessionsWithLastMessage = sessions.filter((s) => s.lastMessageId);
55021
+ if (sessionsWithLastMessage.length === 0) {
55022
+ return;
55023
+ }
55024
+ const validationPromises = sessionsWithLastMessage.map(async (session) => {
55025
+ const lastMessageId = session.lastMessageId;
55026
+ if (!lastMessageId)
55027
+ return;
55028
+ try {
55029
+ const post2 = await platform.getPost(lastMessageId);
55030
+ if (!post2) {
55031
+ log18.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
55032
+ session.lastMessageId = undefined;
55033
+ session.lastMessageTs = undefined;
55034
+ }
55035
+ } catch (err) {
55036
+ log18.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
55037
+ session.lastMessageId = undefined;
55038
+ session.lastMessageTs = undefined;
55039
+ }
55040
+ });
55041
+ await Promise.all(validationPromises);
55042
+ }
55047
55043
  async function updateStickyMessageImpl(platform, sessions, config) {
55048
55044
  const platformSessions = [...sessions.values()].filter((s) => s.platformId === platform.platformId);
55049
- log17.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
55045
+ log18.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
55050
55046
  for (const s of platformSessions) {
55051
- log17.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
55047
+ log18.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
55052
55048
  }
55049
+ await validateLastMessageIds(platform, platformSessions);
55053
55050
  const formatter = platform.getFormatter();
55054
55051
  const content = await buildStickyMessage(sessions, platform.platformId, config, formatter, (threadId) => platform.getThreadLink(threadId));
55055
55052
  const existingPostId = stickyPostIds.get(platform.platformId);
55056
55053
  const shouldBump = needsBump.get(platform.platformId) ?? false;
55057
- log17.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
55054
+ log18.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
55058
55055
  try {
55059
55056
  if (existingPostId && !shouldBump) {
55060
- log17.debug(`Updating existing post in place...`);
55057
+ log18.debug(`Updating existing post in place...`);
55061
55058
  try {
55062
55059
  await platform.updatePost(existingPostId, content);
55063
55060
  try {
55064
55061
  await platform.pinPost(existingPostId);
55065
- log17.debug(`Re-pinned post`);
55062
+ log18.debug(`Re-pinned post`);
55066
55063
  } catch (pinErr) {
55067
- log17.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
55064
+ log18.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
55068
55065
  }
55069
- log17.debug(`Updated successfully`);
55066
+ log18.debug(`Updated successfully`);
55070
55067
  return;
55071
55068
  } catch (err) {
55072
- log17.debug(`Update failed, will create new: ${err}`);
55069
+ log18.debug(`Update failed, will create new: ${err}`);
55073
55070
  }
55074
55071
  }
55075
55072
  needsBump.set(platform.platformId, false);
55076
55073
  if (existingPostId) {
55077
- log17.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
55074
+ log18.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
55078
55075
  try {
55079
55076
  await platform.unpinPost(existingPostId);
55080
- log17.debug(`Unpinned successfully`);
55077
+ log18.debug(`Unpinned successfully`);
55081
55078
  } catch (err) {
55082
- log17.debug(`Unpin failed (probably already unpinned): ${err}`);
55079
+ log18.debug(`Unpin failed (probably already unpinned): ${err}`);
55083
55080
  }
55084
55081
  try {
55085
55082
  await platform.deletePost(existingPostId);
55086
- log17.debug(`Deleted successfully`);
55083
+ log18.debug(`Deleted successfully`);
55087
55084
  } catch (err) {
55088
- log17.debug(`Delete failed (probably already deleted): ${err}`);
55085
+ log18.debug(`Delete failed (probably already deleted): ${err}`);
55089
55086
  }
55090
55087
  stickyPostIds.delete(platform.platformId);
55091
55088
  }
55092
- log17.debug(`Creating new post...`);
55089
+ log18.debug(`Creating new post...`);
55093
55090
  const post2 = await platform.createPost(content);
55094
55091
  stickyPostIds.set(platform.platformId, post2.id);
55095
55092
  try {
55096
55093
  await platform.pinPost(post2.id);
55097
- log17.debug(`Pinned post successfully`);
55094
+ log18.debug(`Pinned post successfully`);
55098
55095
  } catch (err) {
55099
- log17.debug(`Failed to pin post: ${err}`);
55096
+ log18.debug(`Failed to pin post: ${err}`);
55100
55097
  }
55101
55098
  if (sessionStore) {
55102
55099
  sessionStore.saveStickyPostId(platform.platformId, post2.id);
55103
55100
  }
55104
- log17.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
55101
+ log18.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
55105
55102
  const excludePostIds = new Set;
55106
55103
  if (sessionStore) {
55107
55104
  for (const session of sessionStore.load().values()) {
@@ -55117,10 +55114,10 @@ async function updateStickyMessageImpl(platform, sessions, config) {
55117
55114
  }
55118
55115
  const botUser = await platform.getBotUser();
55119
55116
  cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
55120
- log17.debug(`Background cleanup failed: ${err}`);
55117
+ log18.debug(`Background cleanup failed: ${err}`);
55121
55118
  });
55122
55119
  } catch (err) {
55123
- log17.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
55120
+ log18.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
55124
55121
  }
55125
55122
  }
55126
55123
  async function updateAllStickyMessages(platforms, sessions, config) {
@@ -55145,7 +55142,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
55145
55142
  if (!forceRun) {
55146
55143
  const lastRun = lastCleanupTime.get(platformId) || 0;
55147
55144
  if (now - lastRun < CLEANUP_THROTTLE_MS) {
55148
- log17.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
55145
+ log18.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
55149
55146
  return;
55150
55147
  }
55151
55148
  }
@@ -55155,31 +55152,31 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
55155
55152
  const pinnedPostIds = await platform.getPinnedPosts();
55156
55153
  const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
55157
55154
  if (recentPinnedIds.length === 0) {
55158
- log17.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
55155
+ log18.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
55159
55156
  return;
55160
55157
  }
55161
- log17.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
55158
+ log18.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
55162
55159
  for (const postId of recentPinnedIds) {
55163
55160
  try {
55164
55161
  const post2 = await platform.getPost(postId);
55165
55162
  if (!post2)
55166
55163
  continue;
55167
55164
  if (post2.userId === botUserId) {
55168
- log17.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
55165
+ log18.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
55169
55166
  try {
55170
55167
  await platform.unpinPost(postId);
55171
55168
  await platform.deletePost(postId);
55172
- log17.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
55169
+ log18.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
55173
55170
  } catch (err) {
55174
- log17.debug(`Failed to cleanup ${postId}: ${err}`);
55171
+ log18.debug(`Failed to cleanup ${postId}: ${err}`);
55175
55172
  }
55176
55173
  }
55177
55174
  } catch (err) {
55178
- log17.debug(`Could not check post ${postId}: ${err}`);
55175
+ log18.debug(`Could not check post ${postId}: ${err}`);
55179
55176
  }
55180
55177
  }
55181
55178
  } catch (err) {
55182
- log17.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
55179
+ log18.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
55183
55180
  }
55184
55181
  }
55185
55182
  // src/operations/bug-report/handler.ts
@@ -59249,8 +59246,8 @@ function getUpdateInfo() {
59249
59246
 
59250
59247
  // src/operations/commands/handler.ts
59251
59248
  init_emoji();
59252
- var log18 = createLogger("commands");
59253
- var sessionLog2 = createSessionLog(log18);
59249
+ var log19 = createLogger("commands");
59250
+ var sessionLog2 = createSessionLog(log19);
59254
59251
  async function restartClaudeSession(session, cliOptions, ctx, actionName) {
59255
59252
  ctx.ops.stopTyping(session);
59256
59253
  transitionTo(session, "restarting");
@@ -59583,7 +59580,6 @@ async function updateSessionHeader(session, ctx) {
59583
59580
  `);
59584
59581
  const postId = session.sessionStartPostId;
59585
59582
  await updatePost2(session, postId, msg);
59586
- session.platform.pinPost(postId).catch(() => {});
59587
59583
  }
59588
59584
  async function showUpdateStatus(session, updateManager, ctx) {
59589
59585
  const formatter = session.platform.getFormatter();
@@ -59721,7 +59717,7 @@ async function handleBugReportApproval(session, isApproved, username) {
59721
59717
  import { exec as exec3 } from "child_process";
59722
59718
  import { promisify as promisify3 } from "util";
59723
59719
  var execAsync2 = promisify3(exec3);
59724
- var log19 = createLogger("branch");
59720
+ var log20 = createLogger("branch");
59725
59721
  var SUGGESTION_TIMEOUT3 = 15000;
59726
59722
  var MAX_SUGGESTIONS = 3;
59727
59723
  async function getCurrentBranch3(workingDir) {
@@ -59770,7 +59766,7 @@ function parseBranchSuggestions(response) {
59770
59766
  return lines.slice(0, MAX_SUGGESTIONS);
59771
59767
  }
59772
59768
  async function suggestBranchNames(workingDir, userMessage) {
59773
- log19.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
59769
+ log20.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
59774
59770
  try {
59775
59771
  const [currentBranch, recentCommits] = await Promise.all([
59776
59772
  getCurrentBranch3(workingDir),
@@ -59784,22 +59780,22 @@ async function suggestBranchNames(workingDir, userMessage) {
59784
59780
  workingDir
59785
59781
  });
59786
59782
  if (!result.success || !result.response) {
59787
- log19.debug(`Branch suggestion failed: ${result.error || "no response"}`);
59783
+ log20.debug(`Branch suggestion failed: ${result.error || "no response"}`);
59788
59784
  return [];
59789
59785
  }
59790
59786
  const suggestions = parseBranchSuggestions(result.response);
59791
- log19.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
59787
+ log20.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
59792
59788
  return suggestions;
59793
59789
  } catch (err) {
59794
- log19.debug(`Branch suggestion error: ${err}`);
59790
+ log20.debug(`Branch suggestion error: ${err}`);
59795
59791
  return [];
59796
59792
  }
59797
59793
  }
59798
59794
 
59799
59795
  // src/operations/worktree/handler.ts
59800
59796
  import { randomUUID as randomUUID3 } from "crypto";
59801
- var log20 = createLogger("worktree");
59802
- var sessionLog3 = createSessionLog(log20);
59797
+ var log21 = createLogger("worktree");
59798
+ var sessionLog3 = createSessionLog(log21);
59803
59799
  function parseWorktreeError(error) {
59804
59800
  const message = error instanceof Error ? error.message : String(error);
59805
59801
  const lowerMessage = message.toLowerCase();
@@ -60327,8 +60323,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
60327
60323
  }
60328
60324
  }
60329
60325
  // src/operations/events/handler.ts
60330
- var log21 = createLogger("events");
60331
- var sessionLog4 = createSessionLog(log21);
60326
+ var log22 = createLogger("events");
60327
+ var sessionLog4 = createSessionLog(log22);
60332
60328
  function detectAndExecuteClaudeCommands(text, session, ctx) {
60333
60329
  const parsed = parseClaudeCommand(text);
60334
60330
  if (parsed && isClaudeAllowedCommand(parsed.command)) {
@@ -60571,8 +60567,8 @@ function createSessionContext(config, state, ops) {
60571
60567
  }
60572
60568
  // src/operations/context-prompt/handler.ts
60573
60569
  init_emoji();
60574
- var log22 = createLogger("context");
60575
- var sessionLog5 = createSessionLog(log22);
60570
+ var log23 = createLogger("context");
60571
+ var sessionLog5 = createSessionLog(log23);
60576
60572
  var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
60577
60573
  var CONTEXT_OPTIONS = [3, 5, 10];
60578
60574
  var contextPromptTimeouts = new Map;
@@ -60758,7 +60754,7 @@ async function offerContextPrompt(session, queuedPrompt, queuedFiles, ctx, exclu
60758
60754
  return true;
60759
60755
  }
60760
60756
  // src/operations/streaming/handler.ts
60761
- var log23 = createLogger("streaming");
60757
+ var log24 = createLogger("streaming");
60762
60758
  async function buildMessageContent(text, platform, files, debug = false) {
60763
60759
  const imageFiles = files?.filter((f) => f.mimeType.startsWith("image/") && ["image/jpeg", "image/png", "image/gif", "image/webp"].includes(f.mimeType)) || [];
60764
60760
  if (imageFiles.length === 0) {
@@ -60768,7 +60764,7 @@ async function buildMessageContent(text, platform, files, debug = false) {
60768
60764
  for (const file of imageFiles) {
60769
60765
  try {
60770
60766
  if (!platform.downloadFile) {
60771
- log23.warn(`Platform does not support file downloads, skipping ${file.name}`);
60767
+ log24.warn(`Platform does not support file downloads, skipping ${file.name}`);
60772
60768
  continue;
60773
60769
  }
60774
60770
  const buffer = await platform.downloadFile(file.id);
@@ -60782,10 +60778,10 @@ async function buildMessageContent(text, platform, files, debug = false) {
60782
60778
  }
60783
60779
  });
60784
60780
  if (debug) {
60785
- log23.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
60781
+ log24.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
60786
60782
  }
60787
60783
  } catch (err) {
60788
- log23.error(`Failed to download image ${file.name}: ${err}`);
60784
+ log24.error(`Failed to download image ${file.name}: ${err}`);
60789
60785
  }
60790
60786
  }
60791
60787
  if (text) {
@@ -60811,8 +60807,8 @@ function stopTyping(session) {
60811
60807
  }
60812
60808
  }
60813
60809
  // src/session/lifecycle.ts
60814
- var log24 = createLogger("lifecycle");
60815
- var sessionLog6 = createSessionLog(log24);
60810
+ var log25 = createLogger("lifecycle");
60811
+ var sessionLog6 = createSessionLog(log25);
60816
60812
  function mutableSessions(ctx) {
60817
60813
  return ctx.state.sessions;
60818
60814
  }
@@ -60971,35 +60967,76 @@ function createMessageManager(session, ctx) {
60971
60967
  });
60972
60968
  return messageManager;
60973
60969
  }
60970
+ var METADATA_RETRY_DELAY_MS = 2000;
60971
+ var METADATA_MAX_RETRIES = 2;
60972
+ async function attemptMetadataFetch(session, prompt, ctx, attempt = 1, options = {}) {
60973
+ const sessionId = session.sessionId;
60974
+ const suggestMetadataFn = options.suggestMetadata ?? suggestSessionMetadata;
60975
+ const suggestTagsFn = options.suggestTags ?? suggestSessionTags;
60976
+ const [metadata, tags] = await Promise.all([
60977
+ suggestMetadataFn(prompt),
60978
+ suggestTagsFn(prompt)
60979
+ ]);
60980
+ const currentSession = ctx.state.sessions.get(sessionId);
60981
+ if (!currentSession) {
60982
+ sessionLog6(session).debug("Session gone before metadata suggestions completed");
60983
+ return { success: false, metadataSet: false, tagsSet: false };
60984
+ }
60985
+ let metadataSet = false;
60986
+ let tagsSet = false;
60987
+ let updated = false;
60988
+ if (metadata && !currentSession.sessionTitle) {
60989
+ currentSession.sessionTitle = metadata.title;
60990
+ currentSession.sessionDescription = metadata.description;
60991
+ sessionLog6(currentSession).debug(`Set title: "${metadata.title}" (attempt ${attempt})`);
60992
+ metadataSet = true;
60993
+ updated = true;
60994
+ } else if (currentSession.sessionTitle) {
60995
+ metadataSet = true;
60996
+ }
60997
+ if (tags.length > 0 && (!currentSession.sessionTags || currentSession.sessionTags.length === 0)) {
60998
+ currentSession.sessionTags = tags;
60999
+ sessionLog6(currentSession).debug(`Set tags: ${tags.join(", ")} (attempt ${attempt})`);
61000
+ tagsSet = true;
61001
+ updated = true;
61002
+ } else if (currentSession.sessionTags && currentSession.sessionTags.length > 0) {
61003
+ tagsSet = true;
61004
+ }
61005
+ if (updated) {
61006
+ ctx.ops.persistSession(currentSession);
61007
+ await ctx.ops.updateStickyMessage();
61008
+ await ctx.ops.updateSessionHeader(currentSession);
61009
+ }
61010
+ return { success: metadataSet && tagsSet, metadataSet, tagsSet };
61011
+ }
60974
61012
  function fireMetadataSuggestions(session, prompt, ctx) {
60975
61013
  (async () => {
60976
61014
  try {
60977
- const sessionId = session.sessionId;
60978
- const [metadata, tags] = await Promise.all([
60979
- suggestSessionMetadata(prompt),
60980
- suggestSessionTags(prompt)
60981
- ]);
60982
- const currentSession = ctx.state.sessions.get(sessionId);
60983
- if (!currentSession) {
60984
- sessionLog6(session).debug("Session gone before metadata suggestions completed");
60985
- return;
60986
- }
60987
- let updated = false;
60988
- if (metadata && !currentSession.sessionTitle) {
60989
- currentSession.sessionTitle = metadata.title;
60990
- currentSession.sessionDescription = metadata.description;
60991
- sessionLog6(currentSession).debug(`Set title: "${metadata.title}"`);
60992
- updated = true;
60993
- }
60994
- if (tags.length > 0 && (!currentSession.sessionTags || currentSession.sessionTags.length === 0)) {
60995
- currentSession.sessionTags = tags;
60996
- sessionLog6(currentSession).debug(`Set tags: ${tags.join(", ")}`);
60997
- updated = true;
60998
- }
60999
- if (updated) {
61000
- ctx.ops.persistSession(currentSession);
61001
- await ctx.ops.updateStickyMessage();
61002
- await ctx.ops.updateSessionHeader(currentSession);
61015
+ let result = await attemptMetadataFetch(session, prompt, ctx, 1);
61016
+ let attempt = 1;
61017
+ while (!result.success && attempt < METADATA_MAX_RETRIES + 1) {
61018
+ attempt++;
61019
+ const currentSession = ctx.state.sessions.get(session.sessionId);
61020
+ if (!currentSession) {
61021
+ sessionLog6(session).debug("Session gone, stopping metadata retries");
61022
+ return;
61023
+ }
61024
+ const missing = [];
61025
+ if (!result.metadataSet)
61026
+ missing.push("title/description");
61027
+ if (!result.tagsSet)
61028
+ missing.push("tags");
61029
+ sessionLog6(session).debug(`Retrying metadata fetch for ${missing.join(", ")} (attempt ${attempt}/${METADATA_MAX_RETRIES + 1})`);
61030
+ await new Promise((resolve6) => setTimeout(resolve6, METADATA_RETRY_DELAY_MS));
61031
+ result = await attemptMetadataFetch(session, prompt, ctx, attempt);
61032
+ }
61033
+ if (!result.success) {
61034
+ const missing = [];
61035
+ if (!result.metadataSet)
61036
+ missing.push("title/description");
61037
+ if (!result.tagsSet)
61038
+ missing.push("tags");
61039
+ sessionLog6(session).debug(`Metadata fetch incomplete after ${attempt} attempts: missing ${missing.join(", ")}`);
61003
61040
  }
61004
61041
  } catch (err) {
61005
61042
  sessionLog6(session).debug(`Metadata suggestion error: ${err}`);
@@ -61153,9 +61190,6 @@ ${CHAT_PLATFORM_PROMPT}`;
61153
61190
  fireMetadataSuggestions(session, options.prompt, ctx);
61154
61191
  keepAlive.sessionStarted();
61155
61192
  await ctx.ops.updateSessionHeader(session);
61156
- if (session.sessionStartPostId) {
61157
- await platform.pinPost(session.sessionStartPostId).catch(() => {});
61158
- }
61159
61193
  await ctx.ops.updateStickyMessage();
61160
61194
  ctx.ops.startTyping(session);
61161
61195
  claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
@@ -61199,28 +61233,28 @@ async function resumeSession(state, ctx) {
61199
61233
  !state.claudeSessionId && "claudeSessionId",
61200
61234
  !state.workingDir && "workingDir"
61201
61235
  ].filter(Boolean).join(", ");
61202
- log24.warn(`Skipping session with missing required fields: ${missing}`);
61236
+ log25.warn(`Skipping session with missing required fields: ${missing}`);
61203
61237
  return;
61204
61238
  }
61205
61239
  const shortId = state.threadId.substring(0, 8);
61206
61240
  const platforms = ctx.state.platforms;
61207
61241
  const platform = platforms.get(state.platformId);
61208
61242
  if (!platform) {
61209
- log24.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
61243
+ log25.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
61210
61244
  return;
61211
61245
  }
61212
61246
  const threadPost = await platform.getPost(state.threadId);
61213
61247
  if (!threadPost) {
61214
- log24.warn(`Thread ${shortId}... deleted, skipping resume`);
61248
+ log25.warn(`Thread ${shortId}... deleted, skipping resume`);
61215
61249
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
61216
61250
  return;
61217
61251
  }
61218
61252
  if (ctx.state.sessions.size >= ctx.config.maxSessions) {
61219
- log24.warn(`Max sessions reached, skipping resume for ${shortId}...`);
61253
+ log25.warn(`Max sessions reached, skipping resume for ${shortId}...`);
61220
61254
  return;
61221
61255
  }
61222
61256
  if (!existsSync10(state.workingDir)) {
61223
- log24.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
61257
+ log25.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
61224
61258
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
61225
61259
  const resumeFormatter = platform.getFormatter();
61226
61260
  const tempSession = {
@@ -61303,7 +61337,7 @@ ${CHAT_PLATFORM_PROMPT}`;
61303
61337
  worktreePath: detected.worktreePath,
61304
61338
  branch: detected.branch
61305
61339
  };
61306
- log24.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
61340
+ log25.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
61307
61341
  }
61308
61342
  }
61309
61343
  session.messageManager = createMessageManager(session, ctx);
@@ -61355,13 +61389,10 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
61355
61389
  await post(session, "resume", restartMsg);
61356
61390
  }
61357
61391
  await ctx.ops.updateSessionHeader(session);
61358
- if (session.sessionStartPostId) {
61359
- await session.platform.pinPost(session.sessionStartPostId).catch(() => {});
61360
- }
61361
61392
  await ctx.ops.updateStickyMessage();
61362
61393
  ctx.ops.persistSession(session);
61363
61394
  } catch (err) {
61364
- log24.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
61395
+ log25.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
61365
61396
  ctx.ops.emitSessionRemove(sessionId);
61366
61397
  mutableSessions(ctx).delete(sessionId);
61367
61398
  ctx.state.sessionStore.remove(sessionId);
@@ -61396,18 +61427,18 @@ async function resumePausedSession(threadId, message, files, ctx) {
61396
61427
  const persisted = ctx.state.sessionStore.load();
61397
61428
  const state = findPersistedByThreadId(persisted, threadId);
61398
61429
  if (!state) {
61399
- log24.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
61430
+ log25.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
61400
61431
  return;
61401
61432
  }
61402
61433
  const shortId = threadId.substring(0, 8);
61403
- log24.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
61434
+ log25.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
61404
61435
  await resumeSession(state, ctx);
61405
61436
  const session = ctx.ops.findSessionByThreadId(threadId);
61406
61437
  if (session && session.claude.isRunning() && session.messageManager) {
61407
61438
  session.messageCount++;
61408
61439
  await session.messageManager.handleUserMessage(message, files, state.startedBy);
61409
61440
  } else {
61410
- log24.warn(`Failed to resume session ${shortId}..., could not send message`);
61441
+ log25.warn(`Failed to resume session ${shortId}..., could not send message`);
61411
61442
  }
61412
61443
  }
61413
61444
  async function handleExit(sessionId, code, ctx) {
@@ -61415,7 +61446,7 @@ async function handleExit(sessionId, code, ctx) {
61415
61446
  const shortId = sessionId.substring(0, 8);
61416
61447
  sessionLog6(session).debug(`handleExit called code=${code} isShuttingDown=${ctx.state.isShuttingDown}`);
61417
61448
  if (!session) {
61418
- log24.debug(`Session ${shortId}... not found (already cleaned up)`);
61449
+ log25.debug(`Session ${shortId}... not found (already cleaned up)`);
61419
61450
  return;
61420
61451
  }
61421
61452
  if (isSessionRestarting(session)) {
@@ -61608,7 +61639,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
61608
61639
  }
61609
61640
 
61610
61641
  // src/operations/monitor/handler.ts
61611
- var log25 = createLogger("monitor");
61642
+ var log26 = createLogger("monitor");
61612
61643
  var DEFAULT_INTERVAL_MS = 60 * 1000;
61613
61644
 
61614
61645
  class SessionMonitor {
@@ -61630,14 +61661,14 @@ class SessionMonitor {
61630
61661
  }
61631
61662
  start() {
61632
61663
  if (this.isRunning) {
61633
- log25.debug("Session monitor already running");
61664
+ log26.debug("Session monitor already running");
61634
61665
  return;
61635
61666
  }
61636
61667
  this.isRunning = true;
61637
- log25.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
61668
+ log26.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
61638
61669
  this.timer = setInterval(() => {
61639
61670
  this.runCheck().catch((err) => {
61640
- log25.error(`Error during session monitoring: ${err}`);
61671
+ log26.error(`Error during session monitoring: ${err}`);
61641
61672
  });
61642
61673
  }, this.intervalMs);
61643
61674
  }
@@ -61647,7 +61678,7 @@ class SessionMonitor {
61647
61678
  this.timer = null;
61648
61679
  }
61649
61680
  this.isRunning = false;
61650
- log25.debug("Session monitor stopped");
61681
+ log26.debug("Session monitor stopped");
61651
61682
  }
61652
61683
  async runCheck() {
61653
61684
  await cleanupIdleSessions(this.sessionTimeoutMs, this.sessionWarningMs, this.getContext());
@@ -61770,9 +61801,9 @@ class SessionRegistry {
61770
61801
  }
61771
61802
 
61772
61803
  // src/session/manager.ts
61773
- var log26 = createLogger("manager");
61804
+ var log27 = createLogger("manager");
61774
61805
 
61775
- class SessionManager extends EventEmitter5 {
61806
+ class SessionManager extends EventEmitter4 {
61776
61807
  platforms = new Map;
61777
61808
  workingDir;
61778
61809
  skipPermissions;
@@ -61835,7 +61866,7 @@ class SessionManager extends EventEmitter5 {
61835
61866
  markNeedsBump(platformId);
61836
61867
  this.updateStickyMessage();
61837
61868
  });
61838
- log26.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
61869
+ log27.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
61839
61870
  }
61840
61871
  removePlatform(platformId) {
61841
61872
  this.platforms.delete(platformId);
@@ -61851,7 +61882,7 @@ class SessionManager extends EventEmitter5 {
61851
61882
  if (users) {
61852
61883
  users.add(sessionId);
61853
61884
  }
61854
- log26.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
61885
+ log27.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
61855
61886
  }
61856
61887
  unregisterWorktreeUser(worktreePath, sessionId) {
61857
61888
  const users = this.worktreeUsers.get(worktreePath);
@@ -61998,7 +62029,7 @@ class SessionManager extends EventEmitter5 {
61998
62029
  return false;
61999
62030
  }
62000
62031
  const shortId = persistedSession.threadId.substring(0, 8);
62001
- log26.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
62032
+ log27.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
62002
62033
  await resumeSession(persistedSession, this.getContext());
62003
62034
  return true;
62004
62035
  }
@@ -62028,7 +62059,7 @@ class SessionManager extends EventEmitter5 {
62028
62059
  }
62029
62060
  if (session.lastError?.postId === postId && isBugReportEmoji(emojiName)) {
62030
62061
  if (session.startedBy === username || session.platform.isUserAllowed(username) || session.sessionAllowedUsers.has(username)) {
62031
- log26.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
62062
+ log27.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
62032
62063
  await reportBug(session, undefined, username, this.getContext(), session.lastError);
62033
62064
  return;
62034
62065
  }
@@ -62188,11 +62219,11 @@ class SessionManager extends EventEmitter5 {
62188
62219
  }
62189
62220
  }
62190
62221
  if (sessionsToKill.length === 0) {
62191
- log26.info(`No active sessions to pause for platform ${platformId}`);
62222
+ log27.info(`No active sessions to pause for platform ${platformId}`);
62192
62223
  await this.updateStickyMessage();
62193
62224
  return;
62194
62225
  }
62195
- log26.info(`\u23F8\uFE0F Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
62226
+ log27.info(`\u23F8\uFE0F Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
62196
62227
  for (const session of sessionsToKill) {
62197
62228
  try {
62198
62229
  const fmt = session.platform.getFormatter();
@@ -62208,9 +62239,9 @@ class SessionManager extends EventEmitter5 {
62208
62239
  session.claude.kill();
62209
62240
  this.registry.unregister(session.sessionId);
62210
62241
  this.emitSessionRemove(session.sessionId);
62211
- log26.info(`\u23F8\uFE0F Paused session ${session.threadId.substring(0, 8)}`);
62242
+ log27.info(`\u23F8\uFE0F Paused session ${session.threadId.substring(0, 8)}`);
62212
62243
  } catch (err) {
62213
- log26.warn(`Failed to pause session ${session.threadId}: ${err}`);
62244
+ log27.warn(`Failed to pause session ${session.threadId}: ${err}`);
62214
62245
  }
62215
62246
  }
62216
62247
  for (const session of sessionsToKill) {
@@ -62231,17 +62262,17 @@ class SessionManager extends EventEmitter5 {
62231
62262
  sessionsToResume.push(state);
62232
62263
  }
62233
62264
  if (sessionsToResume.length === 0) {
62234
- log26.info(`No paused sessions to resume for platform ${platformId}`);
62265
+ log27.info(`No paused sessions to resume for platform ${platformId}`);
62235
62266
  await this.updateStickyMessage();
62236
62267
  return;
62237
62268
  }
62238
- log26.info(`\u25B6\uFE0F Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
62269
+ log27.info(`\u25B6\uFE0F Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
62239
62270
  for (const state of sessionsToResume) {
62240
62271
  try {
62241
62272
  await resumeSession(state, this.getContext());
62242
- log26.info(`\u25B6\uFE0F Resumed session ${state.threadId.substring(0, 8)}`);
62273
+ log27.info(`\u25B6\uFE0F Resumed session ${state.threadId.substring(0, 8)}`);
62243
62274
  } catch (err) {
62244
- log26.warn(`Failed to resume session ${state.threadId}: ${err}`);
62275
+ log27.warn(`Failed to resume session ${state.threadId}: ${err}`);
62245
62276
  }
62246
62277
  }
62247
62278
  await this.updateStickyMessage();
@@ -62253,14 +62284,14 @@ class SessionManager extends EventEmitter5 {
62253
62284
  const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
62254
62285
  const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
62255
62286
  if (staleIds.length > 0) {
62256
- log26.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
62287
+ log27.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
62257
62288
  }
62258
62289
  const removedCount = this.sessionStore.cleanHistory();
62259
62290
  if (removedCount > 0) {
62260
- log26.info(`\uD83D\uDDD1\uFE0F Permanently removed ${removedCount} old session(s) from history`);
62291
+ log27.info(`\uD83D\uDDD1\uFE0F Permanently removed ${removedCount} old session(s) from history`);
62261
62292
  }
62262
62293
  const persisted = this.sessionStore.load();
62263
- log26.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
62294
+ log27.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
62264
62295
  const excludePostIdsByPlatform = new Map;
62265
62296
  for (const session of persisted.values()) {
62266
62297
  const platformId = session.platformId;
@@ -62280,10 +62311,10 @@ class SessionManager extends EventEmitter5 {
62280
62311
  const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
62281
62312
  platform.getBotUser().then((botUser) => {
62282
62313
  cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
62283
- log26.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
62314
+ log27.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
62284
62315
  });
62285
62316
  }).catch((err) => {
62286
- log26.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
62317
+ log27.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
62287
62318
  });
62288
62319
  }
62289
62320
  if (persisted.size > 0) {
@@ -62297,10 +62328,10 @@ class SessionManager extends EventEmitter5 {
62297
62328
  }
62298
62329
  }
62299
62330
  if (pausedToSkip.length > 0) {
62300
- log26.info(`\u23F8\uFE0F ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
62331
+ log27.info(`\u23F8\uFE0F ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
62301
62332
  }
62302
62333
  if (activeToResume.length > 0) {
62303
- log26.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
62334
+ log27.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
62304
62335
  for (const state of activeToResume) {
62305
62336
  await resumeSession(state, this.getContext());
62306
62337
  }
@@ -62586,7 +62617,7 @@ class SessionManager extends EventEmitter5 {
62586
62617
  const message = messageBuilder(formatter);
62587
62618
  await post(session, "info", message);
62588
62619
  } catch (err) {
62589
- log26.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
62620
+ log27.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
62590
62621
  }
62591
62622
  }
62592
62623
  }
@@ -62605,7 +62636,7 @@ class SessionManager extends EventEmitter5 {
62605
62636
  session.messageManager?.setPendingUpdatePrompt({ postId: post2.id });
62606
62637
  this.registerPost(post2.id, session.threadId);
62607
62638
  } catch (err) {
62608
- log26.warn(`Failed to post ask message to ${threadId}: ${err}`);
62639
+ log27.warn(`Failed to post ask message to ${threadId}: ${err}`);
62609
62640
  }
62610
62641
  }
62611
62642
  }
@@ -66982,7 +67013,7 @@ var instances_default = instances;
66982
67013
 
66983
67014
  // node_modules/ink/build/components/App.js
66984
67015
  var import_react12 = __toESM(require_react(), 1);
66985
- import { EventEmitter as EventEmitter7 } from "events";
67016
+ import { EventEmitter as EventEmitter6 } from "events";
66986
67017
  import process19 from "process";
66987
67018
 
66988
67019
  // node_modules/ink/build/components/AppContext.js
@@ -66995,11 +67026,11 @@ var AppContext_default = AppContext;
66995
67026
 
66996
67027
  // node_modules/ink/build/components/StdinContext.js
66997
67028
  var import_react3 = __toESM(require_react(), 1);
66998
- import { EventEmitter as EventEmitter6 } from "events";
67029
+ import { EventEmitter as EventEmitter5 } from "events";
66999
67030
  import process16 from "process";
67000
67031
  var StdinContext = import_react3.createContext({
67001
67032
  stdin: process16.stdin,
67002
- internal_eventEmitter: new EventEmitter6,
67033
+ internal_eventEmitter: new EventEmitter5,
67003
67034
  setRawMode() {},
67004
67035
  isRawModeSupported: false,
67005
67036
  internal_exitOnCtrlC: true
@@ -67217,7 +67248,7 @@ class App extends import_react12.PureComponent {
67217
67248
  error: undefined
67218
67249
  };
67219
67250
  rawModeEnabledCount = 0;
67220
- internal_eventEmitter = new EventEmitter7;
67251
+ internal_eventEmitter = new EventEmitter6;
67221
67252
  isRawModeSupported() {
67222
67253
  return this.props.stdin.isTTY;
67223
67254
  }
@@ -71118,9 +71149,8 @@ function Platforms({ platforms }) {
71118
71149
  !platform2.enabled ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71119
71150
  dimColor: true,
71120
71151
  children: "\u25CB"
71121
- }, undefined, false, undefined, this) : platform2.reconnecting ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71122
- color: "yellow",
71123
- children: "\u25CC"
71152
+ }, undefined, false, undefined, this) : platform2.reconnecting ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Spinner2, {
71153
+ type: "dots"
71124
71154
  }, undefined, false, undefined, this) : platform2.connected ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71125
71155
  color: "green",
71126
71156
  children: "\u25CF"
@@ -71136,30 +71166,20 @@ function Platforms({ platforms }) {
71136
71166
  platform2.botName
71137
71167
  ]
71138
71168
  }, undefined, true, undefined, this),
71139
- platform2.reconnecting ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(jsx_dev_runtime3.Fragment, {
71140
- children: [
71141
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Spinner2, {
71142
- type: "dots"
71143
- }, undefined, false, undefined, this),
71144
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71145
- color: "yellow",
71146
- children: [
71147
- "reconnecting (",
71148
- platform2.reconnectAttempts,
71149
- ")"
71150
- ]
71151
- }, undefined, true, undefined, this)
71152
- ]
71153
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(jsx_dev_runtime3.Fragment, {
71169
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71170
+ dimColor: true,
71171
+ children: "on"
71172
+ }, undefined, false, undefined, this),
71173
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71174
+ dimColor: !platform2.enabled,
71175
+ children: platform2.displayName
71176
+ }, undefined, false, undefined, this),
71177
+ platform2.reconnecting && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71178
+ dimColor: true,
71154
71179
  children: [
71155
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71156
- dimColor: true,
71157
- children: "on"
71158
- }, undefined, false, undefined, this),
71159
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
71160
- dimColor: !platform2.enabled,
71161
- children: platform2.displayName
71162
- }, undefined, false, undefined, this)
71180
+ "(retry ",
71181
+ platform2.reconnectAttempts,
71182
+ ")"
71163
71183
  ]
71164
71184
  }, undefined, true, undefined, this)
71165
71185
  ]
@@ -71192,29 +71212,29 @@ function SessionLog({ logs, maxLines = 20 }) {
71192
71212
  return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
71193
71213
  flexDirection: "column",
71194
71214
  flexShrink: 0,
71195
- children: displayLogs.map((log27) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
71215
+ children: displayLogs.map((log28) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
71196
71216
  flexShrink: 0,
71197
71217
  children: [
71198
71218
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
71199
- color: getColorForLevel(log27.level),
71219
+ color: getColorForLevel(log28.level),
71200
71220
  dimColor: true,
71201
71221
  wrap: "truncate",
71202
71222
  children: [
71203
71223
  "[",
71204
- padComponent(log27.component),
71224
+ padComponent(log28.component),
71205
71225
  "]"
71206
71226
  ]
71207
71227
  }, undefined, true, undefined, this),
71208
71228
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
71209
- color: getColorForLevel(log27.level),
71229
+ color: getColorForLevel(log28.level),
71210
71230
  wrap: "truncate",
71211
71231
  children: [
71212
71232
  " ",
71213
- log27.message
71233
+ log28.message
71214
71234
  ]
71215
71235
  }, undefined, true, undefined, this)
71216
71236
  ]
71217
- }, log27.id, true, undefined, this))
71237
+ }, log28.id, true, undefined, this))
71218
71238
  }, undefined, false, undefined, this);
71219
71239
  }
71220
71240
  // src/ui/components/Footer.tsx
@@ -71712,7 +71732,7 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
71712
71732
  const scrollRef = import_react59.default.useRef(null);
71713
71733
  const { stdout } = use_stdout_default();
71714
71734
  const isDebug = process.env.DEBUG === "1";
71715
- const displayLogs = logs.filter((log27) => isDebug || log27.level !== "debug");
71735
+ const displayLogs = logs.filter((log28) => isDebug || log28.level !== "debug");
71716
71736
  const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
71717
71737
  import_react59.default.useEffect(() => {
71718
71738
  const handleResize = () => scrollRef.current?.remeasure();
@@ -71752,25 +71772,25 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
71752
71772
  overflow: "hidden",
71753
71773
  children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ScrollView, {
71754
71774
  ref: scrollRef,
71755
- children: visibleLogs.map((log27) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
71775
+ children: visibleLogs.map((log28) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
71756
71776
  children: [
71757
71777
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
71758
71778
  dimColor: true,
71759
71779
  children: [
71760
71780
  "[",
71761
- padComponent2(log27.component),
71781
+ padComponent2(log28.component),
71762
71782
  "]"
71763
71783
  ]
71764
71784
  }, undefined, true, undefined, this),
71765
71785
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
71766
- color: getLevelColor(log27.level),
71786
+ color: getLevelColor(log28.level),
71767
71787
  children: [
71768
71788
  " ",
71769
- log27.message
71789
+ log28.message
71770
71790
  ]
71771
71791
  }, undefined, true, undefined, this)
71772
71792
  ]
71773
- }, log27.id, true, undefined, this))
71793
+ }, log28.id, true, undefined, this))
71774
71794
  }, undefined, false, undefined, this)
71775
71795
  }, undefined, false, undefined, this);
71776
71796
  }
@@ -72287,10 +72307,10 @@ function useAppState(initialConfig) {
72287
72307
  });
72288
72308
  }, []);
72289
72309
  const getLogsForSession = import_react60.useCallback((sessionId) => {
72290
- return state.logs.filter((log27) => log27.sessionId === sessionId);
72310
+ return state.logs.filter((log28) => log28.sessionId === sessionId);
72291
72311
  }, [state.logs]);
72292
72312
  const getGlobalLogs = import_react60.useCallback(() => {
72293
- return state.logs.filter((log27) => !log27.sessionId);
72313
+ return state.logs.filter((log28) => !log28.sessionId);
72294
72314
  }, [state.logs]);
72295
72315
  const togglePlatformEnabled = import_react60.useCallback((platformId) => {
72296
72316
  let newEnabled = false;
@@ -73286,11 +73306,11 @@ Release notes not available. See ${formatter.formatLink("GitHub releases", "http
73286
73306
  }
73287
73307
 
73288
73308
  // src/auto-update/manager.ts
73289
- import { EventEmitter as EventEmitter10 } from "events";
73309
+ import { EventEmitter as EventEmitter9 } from "events";
73290
73310
 
73291
73311
  // src/auto-update/checker.ts
73292
- import { EventEmitter as EventEmitter8 } from "events";
73293
- var log27 = createLogger("checker");
73312
+ import { EventEmitter as EventEmitter7 } from "events";
73313
+ var log28 = createLogger("checker");
73294
73314
  var PACKAGE_NAME = "claude-threads";
73295
73315
  function compareVersions(a, b) {
73296
73316
  const partsA = a.replace(/^v/, "").split(".").map(Number);
@@ -73313,18 +73333,18 @@ async function fetchLatestVersion() {
73313
73333
  }
73314
73334
  });
73315
73335
  if (!response.ok) {
73316
- log27.warn(`Failed to fetch latest version: HTTP ${response.status}`);
73336
+ log28.warn(`Failed to fetch latest version: HTTP ${response.status}`);
73317
73337
  return null;
73318
73338
  }
73319
73339
  const data = await response.json();
73320
73340
  return data.version ?? null;
73321
73341
  } catch (err) {
73322
- log27.warn(`Failed to fetch latest version: ${err}`);
73342
+ log28.warn(`Failed to fetch latest version: ${err}`);
73323
73343
  return null;
73324
73344
  }
73325
73345
  }
73326
73346
 
73327
- class UpdateChecker extends EventEmitter8 {
73347
+ class UpdateChecker extends EventEmitter7 {
73328
73348
  config;
73329
73349
  checkInterval = null;
73330
73350
  lastCheck = null;
@@ -73336,38 +73356,38 @@ class UpdateChecker extends EventEmitter8 {
73336
73356
  }
73337
73357
  start() {
73338
73358
  if (!this.config.enabled) {
73339
- log27.debug("Auto-update disabled, not starting checker");
73359
+ log28.debug("Auto-update disabled, not starting checker");
73340
73360
  return;
73341
73361
  }
73342
73362
  setTimeout(() => {
73343
73363
  this.check().catch((err) => {
73344
- log27.warn(`Initial update check failed: ${err}`);
73364
+ log28.warn(`Initial update check failed: ${err}`);
73345
73365
  });
73346
73366
  }, 5000);
73347
73367
  const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
73348
73368
  this.checkInterval = setInterval(() => {
73349
73369
  this.check().catch((err) => {
73350
- log27.warn(`Periodic update check failed: ${err}`);
73370
+ log28.warn(`Periodic update check failed: ${err}`);
73351
73371
  });
73352
73372
  }, intervalMs);
73353
- log27.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
73373
+ log28.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
73354
73374
  }
73355
73375
  stop() {
73356
73376
  if (this.checkInterval) {
73357
73377
  clearInterval(this.checkInterval);
73358
73378
  this.checkInterval = null;
73359
73379
  }
73360
- log27.debug("Update checker stopped");
73380
+ log28.debug("Update checker stopped");
73361
73381
  }
73362
73382
  async check() {
73363
73383
  if (this.isChecking) {
73364
- log27.debug("Check already in progress, skipping");
73384
+ log28.debug("Check already in progress, skipping");
73365
73385
  return this.lastUpdateInfo;
73366
73386
  }
73367
73387
  this.isChecking = true;
73368
73388
  this.emit("check:start");
73369
73389
  try {
73370
- log27.debug("Checking for updates...");
73390
+ log28.debug("Checking for updates...");
73371
73391
  const latestVersion2 = await fetchLatestVersion();
73372
73392
  if (!latestVersion2) {
73373
73393
  this.emit("check:complete", false);
@@ -73384,18 +73404,18 @@ class UpdateChecker extends EventEmitter8 {
73384
73404
  detectedAt: new Date
73385
73405
  };
73386
73406
  if (!this.lastUpdateInfo || this.lastUpdateInfo.latestVersion !== latestVersion2) {
73387
- log27.info(`\uD83C\uDD95 Update available: v${currentVersion} \u2192 v${latestVersion2}`);
73407
+ log28.info(`\uD83C\uDD95 Update available: v${currentVersion} \u2192 v${latestVersion2}`);
73388
73408
  this.lastUpdateInfo = updateInfo;
73389
73409
  this.emit("update", updateInfo);
73390
73410
  }
73391
73411
  this.emit("check:complete", true);
73392
73412
  return updateInfo;
73393
73413
  }
73394
- log27.debug(`Up to date (v${currentVersion})`);
73414
+ log28.debug(`Up to date (v${currentVersion})`);
73395
73415
  this.emit("check:complete", false);
73396
73416
  return null;
73397
73417
  } catch (err) {
73398
- log27.warn(`Update check failed: ${err}`);
73418
+ log28.warn(`Update check failed: ${err}`);
73399
73419
  this.emit("check:error", err);
73400
73420
  return null;
73401
73421
  } finally {
@@ -73419,7 +73439,7 @@ class UpdateChecker extends EventEmitter8 {
73419
73439
  }
73420
73440
 
73421
73441
  // src/auto-update/scheduler.ts
73422
- import { EventEmitter as EventEmitter9 } from "events";
73442
+ import { EventEmitter as EventEmitter8 } from "events";
73423
73443
 
73424
73444
  // src/auto-update/types.ts
73425
73445
  var RESTART_EXIT_CODE = 42;
@@ -73465,9 +73485,9 @@ function isInScheduledWindow(window2) {
73465
73485
  }
73466
73486
 
73467
73487
  // src/auto-update/scheduler.ts
73468
- var log28 = createLogger("scheduler");
73488
+ var log29 = createLogger("scheduler");
73469
73489
 
73470
- class UpdateScheduler extends EventEmitter9 {
73490
+ class UpdateScheduler extends EventEmitter8 {
73471
73491
  config;
73472
73492
  getSessionActivity;
73473
73493
  getActiveThreadIds;
@@ -73489,7 +73509,7 @@ class UpdateScheduler extends EventEmitter9 {
73489
73509
  scheduleUpdate(updateInfo) {
73490
73510
  this.pendingUpdate = updateInfo;
73491
73511
  if (this.config.autoRestartMode === "immediate") {
73492
- log28.info("Immediate mode: triggering update now");
73512
+ log29.info("Immediate mode: triggering update now");
73493
73513
  this.emit("ready", updateInfo);
73494
73514
  return;
73495
73515
  }
@@ -73502,19 +73522,19 @@ class UpdateScheduler extends EventEmitter9 {
73502
73522
  this.scheduledRestartAt = null;
73503
73523
  this.askApprovals.clear();
73504
73524
  this.askStartTime = null;
73505
- log28.debug("Update schedule cancelled");
73525
+ log29.debug("Update schedule cancelled");
73506
73526
  }
73507
73527
  deferUpdate(minutes) {
73508
73528
  const deferUntil = new Date(Date.now() + minutes * 60 * 1000);
73509
73529
  this.scheduledRestartAt = null;
73510
73530
  this.idleStartTime = null;
73511
73531
  this.emit("deferred", deferUntil);
73512
- log28.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
73532
+ log29.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
73513
73533
  return deferUntil;
73514
73534
  }
73515
73535
  recordAskResponse(threadId, approved) {
73516
73536
  this.askApprovals.set(threadId, approved);
73517
- log28.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
73537
+ log29.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
73518
73538
  this.checkAskCondition();
73519
73539
  }
73520
73540
  getScheduledRestartAt() {
@@ -73535,7 +73555,7 @@ class UpdateScheduler extends EventEmitter9 {
73535
73555
  return;
73536
73556
  this.checkCondition();
73537
73557
  this.checkTimer = setInterval(() => this.checkCondition(), 1e4);
73538
- log28.debug(`Started checking for ${this.config.autoRestartMode} condition`);
73558
+ log29.debug(`Started checking for ${this.config.autoRestartMode} condition`);
73539
73559
  }
73540
73560
  stopChecking() {
73541
73561
  if (this.checkTimer) {
@@ -73566,17 +73586,17 @@ class UpdateScheduler extends EventEmitter9 {
73566
73586
  if (activity.activeSessionCount === 0) {
73567
73587
  if (!this.idleStartTime) {
73568
73588
  this.idleStartTime = new Date;
73569
- log28.debug("No active sessions, starting idle timer");
73589
+ log29.debug("No active sessions, starting idle timer");
73570
73590
  }
73571
73591
  const idleMs = Date.now() - this.idleStartTime.getTime();
73572
73592
  const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
73573
73593
  if (idleMs >= requiredMs) {
73574
- log28.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
73594
+ log29.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
73575
73595
  this.triggerCountdown();
73576
73596
  }
73577
73597
  } else {
73578
73598
  if (this.idleStartTime) {
73579
- log28.debug("Sessions became active, resetting idle timer");
73599
+ log29.debug("Sessions became active, resetting idle timer");
73580
73600
  this.idleStartTime = null;
73581
73601
  }
73582
73602
  }
@@ -73587,7 +73607,7 @@ class UpdateScheduler extends EventEmitter9 {
73587
73607
  const quietMs = Date.now() - activity.lastActivityAt.getTime();
73588
73608
  const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
73589
73609
  if (quietMs >= requiredMs && !activity.anySessionBusy) {
73590
- log28.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
73610
+ log29.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
73591
73611
  this.triggerCountdown();
73592
73612
  }
73593
73613
  } else if (activity.activeSessionCount === 0) {
@@ -73597,7 +73617,7 @@ class UpdateScheduler extends EventEmitter9 {
73597
73617
  const idleMs = Date.now() - this.idleStartTime.getTime();
73598
73618
  const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
73599
73619
  if (idleMs >= requiredMs) {
73600
- log28.info("No sessions and quiet timeout reached, triggering update");
73620
+ log29.info("No sessions and quiet timeout reached, triggering update");
73601
73621
  this.triggerCountdown();
73602
73622
  }
73603
73623
  }
@@ -73608,13 +73628,13 @@ class UpdateScheduler extends EventEmitter9 {
73608
73628
  }
73609
73629
  const activity = this.getSessionActivity();
73610
73630
  if (activity.activeSessionCount === 0) {
73611
- log28.info("Within scheduled window and no active sessions, triggering update");
73631
+ log29.info("Within scheduled window and no active sessions, triggering update");
73612
73632
  this.triggerCountdown();
73613
73633
  } else if (activity.lastActivityAt) {
73614
73634
  const quietMs = Date.now() - activity.lastActivityAt.getTime();
73615
73635
  const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
73616
73636
  if (quietMs >= requiredMs && !activity.anySessionBusy) {
73617
- log28.info("Within scheduled window and sessions quiet, triggering update");
73637
+ log29.info("Within scheduled window and sessions quiet, triggering update");
73618
73638
  this.triggerCountdown();
73619
73639
  }
73620
73640
  }
@@ -73622,14 +73642,14 @@ class UpdateScheduler extends EventEmitter9 {
73622
73642
  checkAskCondition() {
73623
73643
  const threadIds = this.getActiveThreadIds();
73624
73644
  if (threadIds.length === 0) {
73625
- log28.info("No active threads, proceeding with update");
73645
+ log29.info("No active threads, proceeding with update");
73626
73646
  this.triggerCountdown();
73627
73647
  return;
73628
73648
  }
73629
73649
  if (!this.askStartTime && this.pendingUpdate) {
73630
73650
  this.askStartTime = new Date;
73631
73651
  this.postAskMessage(threadIds, this.pendingUpdate.latestVersion).catch((err) => {
73632
- log28.warn(`Failed to post ask message: ${err}`);
73652
+ log29.warn(`Failed to post ask message: ${err}`);
73633
73653
  });
73634
73654
  return;
73635
73655
  }
@@ -73642,12 +73662,12 @@ class UpdateScheduler extends EventEmitter9 {
73642
73662
  denials++;
73643
73663
  }
73644
73664
  if (approvals > threadIds.length / 2) {
73645
- log28.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
73665
+ log29.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
73646
73666
  this.triggerCountdown();
73647
73667
  return;
73648
73668
  }
73649
73669
  if (denials > threadIds.length / 2) {
73650
- log28.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
73670
+ log29.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
73651
73671
  this.deferUpdate(60);
73652
73672
  return;
73653
73673
  }
@@ -73655,7 +73675,7 @@ class UpdateScheduler extends EventEmitter9 {
73655
73675
  const elapsedMs = Date.now() - this.askStartTime.getTime();
73656
73676
  const timeoutMs = this.config.askTimeoutMinutes * 60 * 1000;
73657
73677
  if (elapsedMs >= timeoutMs) {
73658
- log28.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
73678
+ log29.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
73659
73679
  this.triggerCountdown();
73660
73680
  }
73661
73681
  }
@@ -73675,7 +73695,7 @@ class UpdateScheduler extends EventEmitter9 {
73675
73695
  this.emit("ready", this.pendingUpdate);
73676
73696
  }
73677
73697
  }, 1000);
73678
- log28.info("Update countdown started (60 seconds)");
73698
+ log29.info("Update countdown started (60 seconds)");
73679
73699
  }
73680
73700
  stopCountdown() {
73681
73701
  if (this.countdownTimer) {
@@ -73690,7 +73710,7 @@ import { spawn as spawn6 } from "child_process";
73690
73710
  import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
73691
73711
  import { dirname as dirname8, resolve as resolve6 } from "path";
73692
73712
  import { homedir as homedir5 } from "os";
73693
- var log29 = createLogger("installer");
73713
+ var log30 = createLogger("installer");
73694
73714
  var STATE_PATH = resolve6(homedir5(), ".config", "claude-threads", UPDATE_STATE_FILENAME);
73695
73715
  var PACKAGE_NAME2 = "claude-threads";
73696
73716
  function loadUpdateState() {
@@ -73700,7 +73720,7 @@ function loadUpdateState() {
73700
73720
  return JSON.parse(content);
73701
73721
  }
73702
73722
  } catch (err) {
73703
- log29.warn(`Failed to load update state: ${err}`);
73723
+ log30.warn(`Failed to load update state: ${err}`);
73704
73724
  }
73705
73725
  return {};
73706
73726
  }
@@ -73711,9 +73731,9 @@ function saveUpdateState(state) {
73711
73731
  mkdirSync4(dir, { recursive: true });
73712
73732
  }
73713
73733
  writeFileSync5(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
73714
- log29.debug("Update state saved");
73734
+ log30.debug("Update state saved");
73715
73735
  } catch (err) {
73716
- log29.warn(`Failed to save update state: ${err}`);
73736
+ log30.warn(`Failed to save update state: ${err}`);
73717
73737
  }
73718
73738
  }
73719
73739
  function clearUpdateState() {
@@ -73722,7 +73742,7 @@ function clearUpdateState() {
73722
73742
  writeFileSync5(STATE_PATH, "{}", "utf-8");
73723
73743
  }
73724
73744
  } catch (err) {
73725
- log29.warn(`Failed to clear update state: ${err}`);
73745
+ log30.warn(`Failed to clear update state: ${err}`);
73726
73746
  }
73727
73747
  }
73728
73748
  function checkJustUpdated() {
@@ -73754,7 +73774,7 @@ function clearRuntimeSettings() {
73754
73774
  }
73755
73775
  }
73756
73776
  async function installVersion(version) {
73757
- log29.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
73777
+ log30.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
73758
73778
  saveUpdateState({
73759
73779
  previousVersion: VERSION,
73760
73780
  targetVersion: version,
@@ -73765,7 +73785,7 @@ async function installVersion(version) {
73765
73785
  const useBun = process.platform !== "win32";
73766
73786
  const cmd = useBun ? "bun" : "npm.cmd";
73767
73787
  const args = useBun ? ["install", "-g", `${PACKAGE_NAME2}@${version}`] : ["install", "-g", `${PACKAGE_NAME2}@${version}`];
73768
- log29.debug(`Using ${useBun ? "bun" : "npm"} for installation`);
73788
+ log30.debug(`Using ${useBun ? "bun" : "npm"} for installation`);
73769
73789
  const child = spawn6(cmd, args, {
73770
73790
  stdio: ["ignore", "pipe", "pipe"],
73771
73791
  env: {
@@ -73783,7 +73803,7 @@ async function installVersion(version) {
73783
73803
  });
73784
73804
  child.on("close", (code) => {
73785
73805
  if (code === 0) {
73786
- log29.info(`\u2705 Successfully installed ${PACKAGE_NAME2}@${version}`);
73806
+ log30.info(`\u2705 Successfully installed ${PACKAGE_NAME2}@${version}`);
73787
73807
  saveUpdateState({
73788
73808
  previousVersion: VERSION,
73789
73809
  targetVersion: version,
@@ -73793,20 +73813,20 @@ async function installVersion(version) {
73793
73813
  resolve7({ success: true });
73794
73814
  } else {
73795
73815
  const errorMsg = stderr || stdout || `Exit code: ${code}`;
73796
- log29.error(`\u274C Installation failed: ${errorMsg}`);
73816
+ log30.error(`\u274C Installation failed: ${errorMsg}`);
73797
73817
  clearUpdateState();
73798
73818
  resolve7({ success: false, error: errorMsg });
73799
73819
  }
73800
73820
  });
73801
73821
  child.on("error", (err) => {
73802
- log29.error(`\u274C Failed to spawn npm: ${err}`);
73822
+ log30.error(`\u274C Failed to spawn npm: ${err}`);
73803
73823
  clearUpdateState();
73804
73824
  resolve7({ success: false, error: err.message });
73805
73825
  });
73806
73826
  setTimeout(() => {
73807
73827
  if (child.exitCode === null) {
73808
73828
  child.kill();
73809
- log29.error("\u274C Installation timed out");
73829
+ log30.error("\u274C Installation timed out");
73810
73830
  clearUpdateState();
73811
73831
  resolve7({ success: false, error: "Installation timed out" });
73812
73832
  }
@@ -73847,9 +73867,9 @@ class UpdateInstaller {
73847
73867
  }
73848
73868
 
73849
73869
  // src/auto-update/manager.ts
73850
- var log30 = createLogger("updater");
73870
+ var log31 = createLogger("updater");
73851
73871
 
73852
- class AutoUpdateManager extends EventEmitter10 {
73872
+ class AutoUpdateManager extends EventEmitter9 {
73853
73873
  config;
73854
73874
  callbacks;
73855
73875
  checker;
@@ -73870,23 +73890,23 @@ class AutoUpdateManager extends EventEmitter10 {
73870
73890
  }
73871
73891
  start() {
73872
73892
  if (!this.config.enabled) {
73873
- log30.info("Auto-update is disabled");
73893
+ log31.info("Auto-update is disabled");
73874
73894
  return;
73875
73895
  }
73876
73896
  const updateResult = this.installer.checkJustUpdated();
73877
73897
  if (updateResult) {
73878
- log30.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
73898
+ log31.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
73879
73899
  this.callbacks.broadcastUpdate((fmt) => `\uD83C\uDF89 ${fmt.formatBold("Bot updated")} from v${updateResult.previousVersion} to v${updateResult.currentVersion}`).catch((err) => {
73880
- log30.warn(`Failed to broadcast update notification: ${err}`);
73900
+ log31.warn(`Failed to broadcast update notification: ${err}`);
73881
73901
  });
73882
73902
  }
73883
73903
  this.checker.start();
73884
- log30.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
73904
+ log31.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
73885
73905
  }
73886
73906
  stop() {
73887
73907
  this.checker.stop();
73888
73908
  this.scheduler.stop();
73889
- log30.debug("Auto-update manager stopped");
73909
+ log31.debug("Auto-update manager stopped");
73890
73910
  }
73891
73911
  getState() {
73892
73912
  return { ...this.state };
@@ -73900,10 +73920,10 @@ class AutoUpdateManager extends EventEmitter10 {
73900
73920
  async forceUpdate() {
73901
73921
  const updateInfo = this.state.updateInfo || await this.checker.check();
73902
73922
  if (!updateInfo) {
73903
- log30.info("No update available");
73923
+ log31.info("No update available");
73904
73924
  return;
73905
73925
  }
73906
- log30.info("Forcing immediate update");
73926
+ log31.info("Forcing immediate update");
73907
73927
  await this.performUpdate(updateInfo);
73908
73928
  }
73909
73929
  deferUpdate(minutes = 60) {
@@ -73958,7 +73978,7 @@ class AutoUpdateManager extends EventEmitter10 {
73958
73978
  await this.callbacks.broadcastUpdate((fmt) => `\u2705 ${fmt.formatBold("Update installed")} - restarting now. ${fmt.formatItalic("Sessions will resume automatically.")}`).catch(() => {});
73959
73979
  await new Promise((resolve7) => setTimeout(resolve7, 1000));
73960
73980
  await this.callbacks.prepareForRestart();
73961
- log30.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
73981
+ log31.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
73962
73982
  process.stdout.write("\x1B[2J\x1B[H");
73963
73983
  process.stdout.write("\x1B[?25h");
73964
73984
  process.exit(RESTART_EXIT_CODE);