claude-threads 1.31.2 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/index.js +75 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.32.0] - 2026-09-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Slack: shared event source — one Socket Mode connection per app** (#502, thanks @kaza). Slack round-robins Socket Mode envelopes across all of an app's open connections, so a second `SlackClient` on the same app token silently steals events from the first. Now exactly one client (the parent) owns the socket; other clients register as secondaries and receive their channels' events injected by the parent — Web API calls stay independent per instance. The parent mirrors connection state onto secondaries (idempotently re-armed across disconnects), and on reconnect, missed-message recovery runs for the parent and every registered secondary. Zero behavior change for existing single-channel configs; this is the mechanism that unblocks DM auto-discovery on Slack and other multi-channel consumers.
|
|
14
|
+
|
|
10
15
|
## [1.31.2] - 2026-08-30
|
|
11
16
|
|
|
12
17
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -71941,8 +71941,13 @@ class SlackClient extends BasePlatformClient {
|
|
|
71941
71941
|
rateLimitRetryAfter = 0;
|
|
71942
71942
|
outboundFiles;
|
|
71943
71943
|
formatter = new SlackFormatter;
|
|
71944
|
-
|
|
71944
|
+
channelClients = new Map;
|
|
71945
|
+
sharedEventSource;
|
|
71946
|
+
socketConnected = false;
|
|
71947
|
+
constructor(platformConfig, sharedEventSource) {
|
|
71945
71948
|
super();
|
|
71949
|
+
this.sharedEventSource = sharedEventSource;
|
|
71950
|
+
this.installStateMirror();
|
|
71946
71951
|
this.platformId = platformConfig.id;
|
|
71947
71952
|
this.displayName = platformConfig.displayName;
|
|
71948
71953
|
this.botToken = platformConfig.botToken;
|
|
@@ -71956,6 +71961,55 @@ class SlackClient extends BasePlatformClient {
|
|
|
71956
71961
|
this.approvals = platformConfig.approvals;
|
|
71957
71962
|
this.ackReaction = normalizeAckReaction(platformConfig.ackReaction, `platforms[${platformConfig.id}].ackReaction`);
|
|
71958
71963
|
}
|
|
71964
|
+
stateMirrors = ["connected", "disconnected", "reconnecting"].map((state) => ({
|
|
71965
|
+
state,
|
|
71966
|
+
handler: (...args) => {
|
|
71967
|
+
for (const secondary of this.channelClients.values()) {
|
|
71968
|
+
secondary.emit(state, ...args);
|
|
71969
|
+
}
|
|
71970
|
+
}
|
|
71971
|
+
}));
|
|
71972
|
+
installStateMirror() {
|
|
71973
|
+
for (const { state, handler: handler2 } of this.stateMirrors) {
|
|
71974
|
+
this.off(state, handler2);
|
|
71975
|
+
this.on(state, handler2);
|
|
71976
|
+
}
|
|
71977
|
+
}
|
|
71978
|
+
onConnectionEstablished() {
|
|
71979
|
+
const wasReconnecting = this.isReconnecting;
|
|
71980
|
+
super.onConnectionEstablished();
|
|
71981
|
+
if (!wasReconnecting)
|
|
71982
|
+
return;
|
|
71983
|
+
for (const secondary of this.channelClients.values()) {
|
|
71984
|
+
secondary.recoverMissedMessages().catch((err) => {
|
|
71985
|
+
log40.warn(`Failed to recover missed messages for ${secondary.platformId}: ${err}`);
|
|
71986
|
+
});
|
|
71987
|
+
}
|
|
71988
|
+
}
|
|
71989
|
+
registerChannelClient(channelId, client) {
|
|
71990
|
+
if (channelId === this.channelId) {
|
|
71991
|
+
throw new Error(`registerChannelClient: ${channelId} is the parent's own channel — a secondary there can never receive events`);
|
|
71992
|
+
}
|
|
71993
|
+
this.channelClients.set(channelId, client);
|
|
71994
|
+
}
|
|
71995
|
+
unregisterChannelClient(channelId, client) {
|
|
71996
|
+
if (client && this.channelClients.get(channelId) !== client)
|
|
71997
|
+
return;
|
|
71998
|
+
this.channelClients.delete(channelId);
|
|
71999
|
+
}
|
|
72000
|
+
_injectSlackEvent(event) {
|
|
72001
|
+
this.handleSlackEvent(event);
|
|
72002
|
+
}
|
|
72003
|
+
disconnect() {
|
|
72004
|
+
if (this.sharedEventSource) {
|
|
72005
|
+
this.sharedEventSource.unregisterChannelClient(this.channelId, this);
|
|
72006
|
+
}
|
|
72007
|
+
try {
|
|
72008
|
+
return super.disconnect();
|
|
72009
|
+
} finally {
|
|
72010
|
+
this.installStateMirror();
|
|
72011
|
+
}
|
|
72012
|
+
}
|
|
71959
72013
|
normalizePlatformUser(slackUser) {
|
|
71960
72014
|
const displayName = slackUser.profile?.display_name || slackUser.profile?.real_name || slackUser.real_name || slackUser.name;
|
|
71961
72015
|
return {
|
|
@@ -72059,6 +72113,16 @@ class SlackClient extends BasePlatformClient {
|
|
|
72059
72113
|
return data;
|
|
72060
72114
|
}
|
|
72061
72115
|
async connect() {
|
|
72116
|
+
if (this.sharedEventSource) {
|
|
72117
|
+
await this.fetchBotUser();
|
|
72118
|
+
if (this.isIntentionalDisconnect)
|
|
72119
|
+
return;
|
|
72120
|
+
this.sharedEventSource.registerChannelClient(this.channelId, this);
|
|
72121
|
+
if (this.sharedEventSource.socketConnected) {
|
|
72122
|
+
this.emit("connected");
|
|
72123
|
+
}
|
|
72124
|
+
return;
|
|
72125
|
+
}
|
|
72062
72126
|
await this.fetchBotUser();
|
|
72063
72127
|
wsLogger.debug(`Slack bot user ID: ${this.botUserId}`);
|
|
72064
72128
|
const response = await this.appApi("POST", "apps.connections.open");
|
|
@@ -72098,12 +72162,8 @@ class SlackClient extends BasePlatformClient {
|
|
|
72098
72162
|
this.handleSocketModeEvent(envelope);
|
|
72099
72163
|
if (envelope.type === "hello") {
|
|
72100
72164
|
clearTimeout(connectionTimeout);
|
|
72165
|
+
this.socketConnected = true;
|
|
72101
72166
|
this.onConnectionEstablished();
|
|
72102
|
-
if (this.isReconnecting && this.lastProcessedTs) {
|
|
72103
|
-
this.recoverMissedMessages().catch((err) => {
|
|
72104
|
-
log40.warn(`Failed to recover missed messages: ${err}`);
|
|
72105
|
-
});
|
|
72106
|
-
}
|
|
72107
72167
|
doResolve();
|
|
72108
72168
|
}
|
|
72109
72169
|
} catch (err) {
|
|
@@ -72111,6 +72171,7 @@ class SlackClient extends BasePlatformClient {
|
|
|
72111
72171
|
}
|
|
72112
72172
|
};
|
|
72113
72173
|
this.ws.onclose = (event) => {
|
|
72174
|
+
this.socketConnected = false;
|
|
72114
72175
|
clearTimeout(connectionTimeout);
|
|
72115
72176
|
wsLogger.info(`Socket Mode: WebSocket disconnected (code: ${event.code}, reason: ${event.reason || "none"}, clean: ${event.wasClean})`);
|
|
72116
72177
|
if (!settled) {
|
|
@@ -72162,6 +72223,14 @@ class SlackClient extends BasePlatformClient {
|
|
|
72162
72223
|
}
|
|
72163
72224
|
}
|
|
72164
72225
|
handleSlackEvent(event) {
|
|
72226
|
+
const eventChannel = event.channel || event.item?.channel;
|
|
72227
|
+
if (eventChannel && eventChannel !== this.channelId) {
|
|
72228
|
+
const secondary = this.channelClients.get(eventChannel);
|
|
72229
|
+
if (secondary) {
|
|
72230
|
+
secondary._injectSlackEvent(event);
|
|
72231
|
+
return;
|
|
72232
|
+
}
|
|
72233
|
+
}
|
|
72165
72234
|
if (event.type === "message" && (!event.subtype || event.subtype === "file_share")) {
|
|
72166
72235
|
if (event.user === this.botUserId || event.bot_id) {
|
|
72167
72236
|
return;
|
package/package.json
CHANGED