claude-threads 1.40.2 → 1.41.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.
@@ -15692,6 +15692,7 @@ var require_constants2 = __commonJS(function(exports, module) {
15692
15692
 
15693
15693
  // node_modules/ws/lib/buffer-util.js
15694
15694
  var require_buffer_util = __commonJS(function(exports, module) {
15695
+ var { websocketMask, websocketUnmask } = __require("http");
15695
15696
  var { EMPTY_BUFFER } = require_constants2();
15696
15697
  var FastBuffer = Buffer[Symbol.species];
15697
15698
  function concat(list, totalLength) {
@@ -15749,7 +15750,20 @@ var require_buffer_util = __commonJS(function(exports, module) {
15749
15750
  toBuffer,
15750
15751
  unmask: _unmask
15751
15752
  };
15752
- if (!process.env.WS_NO_BUFFER_UTIL) {
15753
+ if (websocketMask) {
15754
+ module.exports.mask = function(source, mask, output, offset, length) {
15755
+ if (length < 24)
15756
+ _mask(source, mask, output, offset, length);
15757
+ else
15758
+ websocketMask(source, mask, output, offset, length);
15759
+ };
15760
+ module.exports.unmask = function(buffer, mask) {
15761
+ if (buffer.length < 24)
15762
+ _unmask(buffer, mask);
15763
+ else
15764
+ websocketUnmask(buffer, mask);
15765
+ };
15766
+ } else if (!process.env.WS_NO_BUFFER_UTIL) {
15753
15767
  try {
15754
15768
  const bufferUtil = (()=>{throw new Error("Cannot require module "+"bufferutil");})();
15755
15769
  module.exports.mask = function(source, mask2, output, offset, length) {
@@ -16737,13 +16751,16 @@ var require_sender = __commonJS(function(exports, module) {
16737
16751
  target[2] = target[3] = 0;
16738
16752
  target.writeUIntBE(dataLength, 4, 6);
16739
16753
  }
16740
- if (!options.mask)
16741
- return [target, data];
16754
+ if (!options.mask) {
16755
+ return dataLength ? [target, data] : [target];
16756
+ }
16742
16757
  target[1] |= 128;
16743
16758
  target[offset - 4] = mask[0];
16744
16759
  target[offset - 3] = mask[1];
16745
16760
  target[offset - 2] = mask[2];
16746
16761
  target[offset - 1] = mask[3];
16762
+ if (!dataLength)
16763
+ return [target];
16747
16764
  if (skipMasking)
16748
16765
  return [target, data];
16749
16766
  if (merge) {
@@ -17803,9 +17820,14 @@ var require_websocket = __commonJS(function(exports, module) {
17803
17820
  opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
17804
17821
  }
17805
17822
  if (isIpcUrl) {
17806
- const parts = opts.path.split(":");
17807
- opts.socketPath = parts[0];
17808
- opts.path = parts[1];
17823
+ const i = opts.path.indexOf(":");
17824
+ if (i === -1) {
17825
+ opts.socketPath = opts.path;
17826
+ opts.path = "";
17827
+ } else {
17828
+ opts.socketPath = opts.path.slice(0, i);
17829
+ opts.path = opts.path.slice(i + 1);
17830
+ }
17809
17831
  }
17810
17832
  let req;
17811
17833
  if (opts.followRedirects) {
@@ -34277,12 +34299,13 @@ function truncateAtCodePoint(text, max) {
34277
34299
  return { text, truncated: false };
34278
34300
  return { text: points.slice(0, max).join(""), truncated: true };
34279
34301
  }
34302
+ var COMMAND_BLOCK_MIN_LENGTH = 120;
34280
34303
  var bashToolFormatter = {
34281
34304
  toolNames: ["Bash"],
34282
34305
  format(toolName, input, options) {
34283
34306
  if (toolName !== "Bash")
34284
34307
  return null;
34285
- const { formatter, maxCommandLength = 50, worktreeInfo } = options;
34308
+ const { formatter, detailed = false, maxCommandLength = 50, worktreeInfo } = options;
34286
34309
  let cmd = input.command || "";
34287
34310
  if (worktreeInfo?.path) {
34288
34311
  cmd = cmd.replace(new RegExp(escapeRegExp(worktreeInfo.path), "g"), `[${worktreeInfo.branch}]`);
@@ -34292,8 +34315,14 @@ var bashToolFormatter = {
34292
34315
  const permission = truncateAtCodePoint(cmd, PERMISSION_COMMAND_MAX);
34293
34316
  const permissionCmd = escapeCodeBlockContent(permission.text) + (permission.truncated ? `
34294
34317
  [... truncated]` : "");
34318
+ let display = `\uD83D\uDCBB ${formatter.formatBold("Bash")} ${formatter.formatCode(displayCmd + (truncated ? "..." : ""))}`;
34319
+ if (detailed && truncated && (cmd.length > COMMAND_BLOCK_MIN_LENGTH || cmd.includes(`
34320
+ `))) {
34321
+ display += `
34322
+ ${formatter.formatCodeBlock(permissionCmd, "bash")}`;
34323
+ }
34295
34324
  return {
34296
- display: `\uD83D\uDCBB ${formatter.formatBold("Bash")} ${formatter.formatCode(displayCmd + (truncated ? "..." : ""))}`,
34325
+ display,
34297
34326
  permissionText: `\uD83D\uDCBB ${formatter.formatBold("Bash")}
34298
34327
  ${formatter.formatCodeBlock(permissionCmd, "bash")}`,
34299
34328
  isDestructive: true
@@ -35087,8 +35116,12 @@ function formatBytes(bytes) {
35087
35116
 
35088
35117
  // src/operations/streaming/handler.ts
35089
35118
  var log9 = createLogger("streaming");
35119
+
35120
+ // src/operations/user-attribution/formatter.ts
35121
+ var speakersBySession = new WeakMap;
35090
35122
  // src/operations/message-manager.ts
35091
35123
  var log10 = createLogger("msg-mgr");
35124
+ var REACTION_ANCHOR_TYPES = new Set(["content", "task_list", "subagent"]);
35092
35125
  // src/persistence/session-store.ts
35093
35126
  init_logger();
35094
35127
  import { join as join3 } from "path";
@@ -38847,6 +38880,10 @@ var LOGS_BASE_DIR = join4(stateHome(), ".claude-threads", "logs");
38847
38880
  // src/operations/bug-report/handler.ts
38848
38881
  var piiRedactor = new Redactor({ aggressive: true });
38849
38882
  var MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
38883
+ // src/session/lifecycle-delivery.ts
38884
+ init_logger();
38885
+ var sessionLog2 = createSessionLog(createLogger("lifecycle"));
38886
+
38850
38887
  // src/mcp/decision-bridge.ts
38851
38888
  import { createServer, createConnection } from "node:net";
38852
38889
  var MAX_BRIDGE_REQUEST_BYTES = 1024 * 1024;
@@ -39740,13 +39777,13 @@ var log19 = createLogger("tags");
39740
39777
  // src/session/metadata-suggestions.ts
39741
39778
  init_logger();
39742
39779
  var log20 = createLogger("session");
39743
- var sessionLog2 = createSessionLog(log20);
39780
+ var sessionLog3 = createSessionLog(log20);
39744
39781
 
39745
39782
  // src/operations/context-prompt/handler.ts
39746
39783
  init_emoji();
39747
39784
  init_logger();
39748
39785
  var log21 = createLogger("context");
39749
- var sessionLog3 = createSessionLog(log21);
39786
+ var sessionLog4 = createSessionLog(log21);
39750
39787
  var contextPromptTimeouts = new Map;
39751
39788
  var contextPromptFiles = new Map;
39752
39789
  // src/memory/store.ts
@@ -39792,21 +39829,21 @@ var log27 = createLogger("watches");
39792
39829
  // src/operations/commands/guards.ts
39793
39830
  init_logger();
39794
39831
  var log28 = createLogger("commands");
39795
- var sessionLog4 = createSessionLog(log28);
39832
+ var sessionLog5 = createSessionLog(log28);
39796
39833
 
39797
39834
  // src/operations/commands/automation.ts
39798
39835
  init_logger();
39799
39836
  var log29 = createLogger("commands");
39800
- var sessionLog5 = createSessionLog(log29);
39837
+ var sessionLog6 = createSessionLog(log29);
39801
39838
 
39802
39839
  // src/operations/agent-actions/handler.ts
39803
39840
  init_logger();
39804
39841
  var log30 = createLogger("agent-actions");
39805
- var sessionLog6 = createSessionLog(log30);
39842
+ var sessionLog7 = createSessionLog(log30);
39806
39843
 
39807
39844
  // src/session/lifecycle.ts
39808
39845
  var log31 = createLogger("lifecycle");
39809
- var sessionLog7 = createSessionLog(log31);
39846
+ var sessionLog8 = createSessionLog(log31);
39810
39847
  var _inFlightSessionStarts = new Map;
39811
39848
  var CHAT_PLATFORM_PROMPT = generateChatPlatformPrompt();
39812
39849
 
@@ -39830,11 +39867,11 @@ var DEFAULT_FILE3 = join9(DEFAULT_CONFIG_DIR2, "github-emails.yaml");
39830
39867
 
39831
39868
  // src/operations/commands/handler.ts
39832
39869
  var log33 = createLogger("commands");
39833
- var sessionLog8 = createSessionLog(log33);
39870
+ var sessionLog9 = createSessionLog(log33);
39834
39871
  // src/operations/commands/memory.ts
39835
39872
  init_logger();
39836
39873
  var log34 = createLogger("commands");
39837
- var sessionLog9 = createSessionLog(log34);
39874
+ var sessionLog10 = createSessionLog(log34);
39838
39875
  // src/operations/suggestions/branch.ts
39839
39876
  init_quick_query();
39840
39877
  init_logger();
@@ -39847,11 +39884,11 @@ var log35 = createLogger("branch");
39847
39884
  init_cli();
39848
39885
  init_logger();
39849
39886
  var log36 = createLogger("worktree");
39850
- var sessionLog10 = createSessionLog(log36);
39887
+ var sessionLog11 = createSessionLog(log36);
39851
39888
  // src/operations/events/handler.ts
39852
39889
  init_logger();
39853
39890
  var log37 = createLogger("events");
39854
- var sessionLog11 = createSessionLog(log37);
39891
+ var sessionLog12 = createSessionLog(log37);
39855
39892
  // src/operations/monitor/handler.ts
39856
39893
  init_logger();
39857
39894
  var log38 = createLogger("monitor");
@@ -173,6 +173,7 @@ What happens: every `audio/*` attachment (or a file with an audio extension when
173
173
  | `sessionHeader` | No | Per-thread header visibility: `full` (default) / `minimal` (status bar only) / `hidden` (no header post) |
174
174
  | `stickyMessage` | No | Channel sticky visibility: `full` (default) / `minimal` (status bar only) / `hidden` (no sticky, no bumping) |
175
175
  | `lifecycle` | No | Session status posts — idle warning, timeout, pause, resume, and the shutdown notice a deploy leaves in each open thread: `full` (default, all of them) / `minimal` (drops the idle warning, which predicts a timeout the next message would undo anyway) / `hidden` (none). Editing a status post the thread already has is not suppressed at any level: it adds no post and no notification, and leaving a stale "session idle" up across a restart would be worse. ⚠️ An **abnormal exit** (`[Exited: <code>]`, non-zero only) posts at every level including `hidden`: it is a failure report, and silencing it would make a session that died look exactly like one that finished. Note that `hidden` also removes the post a 🔄 reaction resumes from — sending a message in the thread still resumes it, and the channel sticky says so. |
176
+ | `lifecycleDelivery` | No | How a **new** lifecycle notice reaches the thread: `post` (default, a new post as before) / `dedup` (no new post when the bot's last post in the thread already shows the same notice: that post is updated to the new text in place instead, which notifies nobody; a "Session resumed" notice counts as the shutdown notice the next restart would post, so back-to-back restarts leave one post) / `reaction` (an emoji on the bot's last post instead of a new post, which notifies nobody: ⏳ idle, ⏰ timed out, 💤 paused, 🔌 shutting down, 🌅 resumed; when that last post is a notice the bot had to post itself, it is updated in place instead, so its text and emoji never disagree). `lifecycle` still decides whether a notice is wanted at all, so `hidden` delivers nothing. Editing an existing status post is unchanged, except that a post carrying the notice as a reaction is never rewritten (its emoji is swapped instead). The deduped or reacted-to post is what a 🔄 reaction resumes the session from. With nothing to react to yet (right after a restart, before the bot has posted), the notice is posted. Any newer post in the thread, by anyone, ends a notice's turn as the last post, so a later notice is never refreshed above it. Under `reaction` the emoji only goes on Claude's reply or a task-list post; on a question, a plan or a prompt the reactions are the answers, so the notice is posted instead, as it is when an emoji swap fails. Posts made while the bot is down are not seen. `reaction` also drops the notice's text, such as "react 🔄 to resume" and the minutes left. ⚠️ An **abnormal exit** always posts, whatever this is set to. |
176
177
  | `directChannelMode` | No | Direct channel mode: the whole channel is one session, and the bot replies with top-level channel posts instead of thread replies. `true` for defaults, or an options object (`respondTo`). See [Direct Channel Mode](#direct-channel-mode). |
177
178
  | `approvals` | No | Who may answer tool-permission prompts and other reaction gates: `owner` (session participants) or `all_users` (everyone on `allowedUsers`). Unset keeps the historical default per mode — `all_users` for thread sessions, `owner` for direct channel mode. See [Approvals](#approvals). |
178
179
  | `ackReaction` | No | Read receipt: react to every accepted message (session start, follow-up, resume) the instant it is accepted, before Claude produces output. `true` uses 👀 (`eyes`), a string names a custom emoji. Persistent, unlike the typing indicator — useful in busy channels and for messages queued behind an in-flight session start. The receipt means *accepted*, not *delivered*: a later failure (capacity limit, Claude not coming up) is still reported by its own post. `!commands` are not acked — they have their own immediate feedback, and neither are messages accepted through the message-approval flow (an authorized user approving a non-participant's message) — there the approval reaction is already the visible signal. Note: in direct channel mode this is one reaction API call per accepted message. Default off. |
@@ -199,6 +200,7 @@ What happens: every `audio/*` attachment (or a file with an audio extension when
199
200
  | `sessionHeader` | No | Per-thread header visibility: `full` (default) / `minimal` (status bar only) / `hidden` (no header post) |
200
201
  | `stickyMessage` | No | Channel sticky visibility: `full` (default) / `minimal` (status bar only) / `hidden` (no sticky, no bumping) |
201
202
  | `lifecycle` | No | Session status posts — idle warning, timeout, pause, resume, and the shutdown notice a deploy leaves in each open thread: `full` (default, all of them) / `minimal` (drops the idle warning, which predicts a timeout the next message would undo anyway) / `hidden` (none). Editing a status post the thread already has is not suppressed at any level: it adds no post and no notification, and leaving a stale "session idle" up across a restart would be worse. ⚠️ An **abnormal exit** (`[Exited: <code>]`, non-zero only) posts at every level including `hidden`: it is a failure report, and silencing it would make a session that died look exactly like one that finished. Note that `hidden` also removes the post a 🔄 reaction resumes from — sending a message in the thread still resumes it, and the channel sticky says so. |
203
+ | `lifecycleDelivery` | No | How a **new** lifecycle notice reaches the thread: `post` (default, a new post as before) / `dedup` (no new post when the bot's last post in the thread already shows the same notice: that post is updated to the new text in place instead, which notifies nobody; a "Session resumed" notice counts as the shutdown notice the next restart would post, so back-to-back restarts leave one post) / `reaction` (an emoji on the bot's last post instead of a new post, which notifies nobody: ⏳ idle, ⏰ timed out, 💤 paused, 🔌 shutting down, 🌅 resumed; when that last post is a notice the bot had to post itself, it is updated in place instead, so its text and emoji never disagree). `lifecycle` still decides whether a notice is wanted at all, so `hidden` delivers nothing. Editing an existing status post is unchanged, except that a post carrying the notice as a reaction is never rewritten (its emoji is swapped instead). The deduped or reacted-to post is what a 🔄 reaction resumes the session from. With nothing to react to yet (right after a restart, before the bot has posted), the notice is posted. Any newer post in the thread, by anyone, ends a notice's turn as the last post, so a later notice is never refreshed above it. Under `reaction` the emoji only goes on Claude's reply or a task-list post; on a question, a plan or a prompt the reactions are the answers, so the notice is posted instead, as it is when an emoji swap fails. Posts made while the bot is down are not seen. `reaction` also drops the notice's text, such as "react 🔄 to resume" and the minutes left. ⚠️ An **abnormal exit** always posts, whatever this is set to. |
202
204
  | `directChannelMode` | No | Direct channel mode: the whole channel is one session, and the bot replies with top-level channel posts instead of thread replies. `true` for defaults, or an options object (`respondTo`). See [Direct Channel Mode](#direct-channel-mode). |
203
205
  | `approvals` | No | Who may answer tool-permission prompts and other reaction gates: `owner` (session participants) or `all_users` (everyone on `allowedUsers`). Unset keeps the historical default per mode — `all_users` for thread sessions, `owner` for direct channel mode. See [Approvals](#approvals). |
204
206
  | `ackReaction` | No | Read receipt: react to every accepted message (session start, follow-up, resume) the instant it is accepted, before Claude produces output. `true` uses 👀 (`eyes`), a string names a custom emoji. Persistent, unlike the typing indicator — useful in busy channels and for messages queued behind an in-flight session start. The receipt means *accepted*, not *delivered*: a later failure (capacity limit, Claude not coming up) is still reported by its own post. `!commands` are not acked — they have their own immediate feedback, and neither are messages accepted through the message-approval flow (an authorized user approving a non-participant's message) — there the approval reaction is already the visible signal. Note: in direct channel mode this is one reaction API call per accepted message. Default off. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "1.40.2",
3
+ "version": "1.41.0",
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",
@@ -85,7 +85,7 @@
85
85
  "react": "^19.3.1",
86
86
  "semver": "7.8.5",
87
87
  "update-notifier": "^7.3.1",
88
- "ws": "8.22.0",
88
+ "ws": "8.22.1",
89
89
  "zod": "^4.6.7"
90
90
  },
91
91
  "devDependencies": {
@@ -101,7 +101,7 @@
101
101
  "@types/ws": "^8.18.2",
102
102
  "eslint": "^10.13.0",
103
103
  "husky": "^9.1.7",
104
- "knip": "6.43.0",
104
+ "knip": "6.44.1",
105
105
  "lint-staged": "^17.0.4",
106
106
  "prettier": "^3.9.10",
107
107
  "react-devtools-core": "^8.0.0",