claude-threads 1.32.1 → 1.33.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.
@@ -554,6 +554,14 @@ claudeAccounts:
554
554
  | `apiKey` | One of | Anthropic API key. Billed against that key; session history stays under the bot's default `HOME`. |
555
555
  | `displayName` | No | Human-readable label in UI (defaults to `id`) |
556
556
 
557
+ ⚠️ An account with `home` owns the **whole** Claude profile, not just its
558
+ credentials: user settings, hooks and global MCP configuration are read from
559
+ that home too. Anything the daemon inherited that could point elsewhere — an
560
+ API key, a bearer token, `CLAUDE_CONFIG_DIR` — is cleared for that child, or
561
+ the account you selected would be silently overridden by the one the daemon
562
+ runs as. Daemon-level hooks and settings therefore do **not** carry into a
563
+ pooled account; put them in the account's own home.
564
+
557
565
  Exactly one of `home` or `apiKey` should be set per account. Persisted sessions record which account they ran under and resume on the same one.
558
566
 
559
567
  ## Environment Variables
@@ -0,0 +1,79 @@
1
+ # Posts made through the app's own user token are the person's messages
2
+
3
+ Upstream discussion: anneschuth/claude-threads#526.
4
+
5
+ ## What it does
6
+
7
+ A message posted with `chat.postMessage` and a **user token of this app**
8
+ (`xoxp-…`, obtained when a person authorises the app) shows up in Slack as
9
+ that person, and Slack delivers it with `user` = the person **and**
10
+ `bot_id` + `app_id` of the app. Today the Slack client drops every message
11
+ that carries a `bot_id`, so such a post never reaches Claude, silently.
12
+
13
+ With this change the client treats a message as bot-authored only when
14
+
15
+ - its `user` is the bot's own user (the daemon's replies), or
16
+ - it carries a `bot_id` and is **not** a post from this app, from this
17
+ workspace, with a `user`.
18
+
19
+ So a post relayed through this app's own user token is handled exactly like
20
+ the person typing it: same session routing, same allow-list, same everything.
21
+ Other apps' bot posts, classic bot posts without a `user`, the daemon's own
22
+ replies, and a bot copy of this same app installed in another workspace that
23
+ shares a Slack Connect channel (same `app_id`, other `team`) stay ignored,
24
+ as before.
25
+
26
+ The rule is applied in the three places the filter lives: live events,
27
+ missed-message recovery after a reconnect, and thread history with
28
+ `excludeBotMessages`.
29
+
30
+ ## Why
31
+
32
+ Any integration that speaks *for* a person — a voice front desk, a phone
33
+ bridge, an approval UI — posts on the person's behalf with the app's user
34
+ token, because that is the only way to keep attribution honest and the bot's
35
+ consent gate (channel membership, allow-lists) intact. Slack marks every
36
+ API-posted message with the app's `bot_id`; there is no way to post a
37
+ "pure" human message through the API. Without this exception the daemon
38
+ can never be driven by such an integration.
39
+
40
+ Origin: the VVS live voice desk (`voice/` on the `local/voice-desk` branch
41
+ of the kaza fork) relayed a spoken question into a task channel as the
42
+ speaker, and the daemon ACKed the event and dropped it at the `bot_id`
43
+ check with no log line. Seen 2026-09-02, message
44
+ `{user: U…, bot_id: B0BUDMNPD26, app_id: A0BSH7L7N9E, bot_profile: {…}}`.
45
+
46
+ ## How
47
+
48
+ - The client learns its workspace from `auth.test` (`team_id`, already
49
+ called for the bot user) and its own app id from the Socket Mode `hello`
50
+ (`connection_info.app_id`) and, belt and braces, from every `events_api`
51
+ envelope (`payload.api_app_id`). Secondary clients on a shared socket get
52
+ it handed over with each injected event. No new config, no new API call.
53
+ - One predicate, `isBotAuthored(message)`, replaces the three inline
54
+ `user === botUserId || bot_id` checks.
55
+ - `app_id` is added to the `SlackEvent` and `SlackMessage` types; it was
56
+ already on the wire.
57
+ - Until app and team ids are known (only possible before the first `hello`), the
58
+ old rule applies: anything with a `bot_id` is ignored. Conservative, and
59
+ the window is the connection handshake.
60
+
61
+ Why `app_id` and not `bot_id`: the `bot_id` on a user-token post is a
62
+ per-authorisation bot profile (`B0BUDMNPD26` above), different from the
63
+ bot token's own `bot_id` from `auth.test` (`B0BSH828Z6U`). `app_id` is the
64
+ one stable identity.
65
+
66
+ ## Decisions
67
+
68
+ | Decision | Why |
69
+ |---|---|
70
+ | No config flag | with `app_id` and `team` checked, only this installation's own user tokens qualify; nothing to opt out of |
71
+ | `team` must match too (Codex review) | Slack Connect lets the same app be installed on both sides of a shared channel; the other copy's bot shares our `app_id` but not our `team`, and two daemons answering each other is the one loop this must never open |
72
+ | Learn the app id from the socket, not config | zero setup; every install already receives it |
73
+ | Old behaviour until the app id is known | never widen the filter on a guess |
74
+
75
+ ## Lessons learned
76
+
77
+ - A dropped event at the "ignore ourselves" check leaves no trace in the
78
+ log. Reproducing needed `conversations.history` on the exact `ts` to see
79
+ the stored message's fields.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "1.32.1",
3
+ "version": "1.33.1",
4
4
  "description": "Run Claude Code from Slack or Mattermost. Sessions stream live into threads where your whole team can watch and steer.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -122,7 +122,7 @@
122
122
  "flatted": ">=3.4.0",
123
123
  "picomatch": ">=2.3.2",
124
124
  "path-to-regexp": ">=8.4.0",
125
- "fast-uri": ">=4.1.2",
125
+ "fast-uri": ">=4.1.4",
126
126
  "brace-expansion": ">=5.0.9",
127
127
  "ip-address": ">=10.3.1",
128
128
  "ws": "$ws",
@@ -135,7 +135,7 @@
135
135
  "flatted": ">=3.4.0",
136
136
  "picomatch": ">=2.3.2",
137
137
  "path-to-regexp": ">=8.4.0",
138
- "fast-uri": ">=4.1.2",
138
+ "fast-uri": ">=4.1.4",
139
139
  "brace-expansion": ">=5.0.9",
140
140
  "ip-address": ">=10.3.1",
141
141
  "ws": "$ws",