claude-threads 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.1] - 2026-01-17
9
+
10
+ ### Fixed
11
+ - **WebSocket reconnection restored** - Fixed critical bugs in reconnection logic that were introduced in v1.0.3 (#231)
12
+ - Heartbeat now properly triggers reconnection when detecting dead connections (was just closing without reconnecting)
13
+ - Auto-retry on reconnection failure restored (was lost when code was refactored to base class)
14
+ - TUI will no longer show "connected" when connection is actually dead
15
+ - All platforms (Mattermost and Slack) now benefit from robust reconnection logic
16
+
8
17
  ## [1.3.0] - 2026-01-17
9
18
 
10
19
  ### Added
package/dist/index.js CHANGED
@@ -50921,6 +50921,7 @@ class BasePlatformClient extends EventEmitter {
50921
50921
  reconnectAttempts = 0;
50922
50922
  maxReconnectAttempts = 10;
50923
50923
  reconnectDelay = 1000;
50924
+ reconnectTimeout = null;
50924
50925
  isUserAllowed(username) {
50925
50926
  if (this.allowedUsers.length === 0) {
50926
50927
  return true;
@@ -50945,6 +50946,10 @@ class BasePlatformClient extends EventEmitter {
50945
50946
  wsLogger.info("Disconnecting (intentional)");
50946
50947
  this.isIntentionalDisconnect = true;
50947
50948
  this.stopHeartbeat();
50949
+ if (this.reconnectTimeout) {
50950
+ clearTimeout(this.reconnectTimeout);
50951
+ this.reconnectTimeout = null;
50952
+ }
50948
50953
  this.forceCloseConnection();
50949
50954
  }
50950
50955
  prepareForReconnect() {
@@ -50960,7 +50965,7 @@ class BasePlatformClient extends EventEmitter {
50960
50965
  if (silentFor > this.HEARTBEAT_TIMEOUT_MS) {
50961
50966
  log.warn(`Connection dead (no activity for ${Math.round(silentFor / 1000)}s), reconnecting...`);
50962
50967
  this.stopHeartbeat();
50963
- this.forceCloseConnection();
50968
+ this.scheduleReconnect();
50964
50969
  return;
50965
50970
  }
50966
50971
  wsLogger.debug(`Heartbeat check (last activity ${Math.round(silentFor / 1000)}s ago)`);
@@ -50973,6 +50978,10 @@ class BasePlatformClient extends EventEmitter {
50973
50978
  }
50974
50979
  }
50975
50980
  scheduleReconnect() {
50981
+ if (this.reconnectTimeout) {
50982
+ clearTimeout(this.reconnectTimeout);
50983
+ this.reconnectTimeout = null;
50984
+ }
50976
50985
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
50977
50986
  log.error("Max reconnection attempts reached");
50978
50987
  return;
@@ -50981,11 +50990,17 @@ class BasePlatformClient extends EventEmitter {
50981
50990
  this.isReconnecting = true;
50982
50991
  this.reconnectAttempts++;
50983
50992
  const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
50984
- log.info(`Reconnecting... (attempt ${this.reconnectAttempts})`);
50993
+ wsLogger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
50985
50994
  this.emit("reconnecting", this.reconnectAttempts);
50986
- setTimeout(() => {
50995
+ this.reconnectTimeout = setTimeout(() => {
50996
+ this.reconnectTimeout = null;
50997
+ if (this.isIntentionalDisconnect) {
50998
+ wsLogger.debug("Skipping reconnect: intentional disconnect was called");
50999
+ return;
51000
+ }
50987
51001
  this.connect().catch((err) => {
50988
- log.error(`Reconnection failed: ${err}`);
51002
+ wsLogger.error(`Reconnection failed: ${err}`);
51003
+ this.scheduleReconnect();
50989
51004
  });
50990
51005
  }, delay);
50991
51006
  }
@@ -51823,7 +51838,6 @@ class SlackClient extends BasePlatformClient {
51823
51838
  channelId;
51824
51839
  skipPermissions;
51825
51840
  apiUrl;
51826
- reconnectTimeout = null;
51827
51841
  userCache = new Map;
51828
51842
  usernameToIdCache = new Map;
51829
51843
  botUserId = null;
@@ -52147,33 +52161,6 @@ class SlackClient extends BasePlatformClient {
52147
52161
  this.ws = null;
52148
52162
  }
52149
52163
  }
52150
- scheduleReconnect() {
52151
- if (this.reconnectTimeout) {
52152
- clearTimeout(this.reconnectTimeout);
52153
- this.reconnectTimeout = null;
52154
- }
52155
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
52156
- log3.error("Max reconnection attempts reached");
52157
- return;
52158
- }
52159
- this.forceCloseConnection();
52160
- this.isReconnecting = true;
52161
- this.reconnectAttempts++;
52162
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
52163
- wsLogger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
52164
- this.emit("reconnecting", this.reconnectAttempts);
52165
- this.reconnectTimeout = setTimeout(() => {
52166
- this.reconnectTimeout = null;
52167
- if (this.isIntentionalDisconnect) {
52168
- wsLogger.debug("Skipping reconnect: intentional disconnect was called");
52169
- return;
52170
- }
52171
- this.connect().catch((err) => {
52172
- wsLogger.error(`Reconnection failed: ${err}`);
52173
- this.scheduleReconnect();
52174
- });
52175
- }, delay);
52176
- }
52177
52164
  async recoverMissedMessages() {
52178
52165
  if (!this.lastProcessedTs) {
52179
52166
  return;
@@ -52204,16 +52191,6 @@ class SlackClient extends BasePlatformClient {
52204
52191
  log3.warn(`Failed to recover missed messages: ${err}`);
52205
52192
  }
52206
52193
  }
52207
- disconnect() {
52208
- wsLogger.info("Disconnecting Socket Mode WebSocket (intentional)");
52209
- this.isIntentionalDisconnect = true;
52210
- this.stopHeartbeat();
52211
- if (this.reconnectTimeout) {
52212
- clearTimeout(this.reconnectTimeout);
52213
- this.reconnectTimeout = null;
52214
- }
52215
- this.forceCloseConnection();
52216
- }
52217
52194
  async fetchBotUser() {
52218
52195
  const response = await this.api("POST", "auth.test");
52219
52196
  this.botUserId = response.user_id;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Share Claude Code sessions live in a Mattermost channel with interactive features",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",