claude-threads 1.9.0 → 1.9.2
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 +15 -0
- package/dist/index.js +100 -56
- package/dist/mcp/permission-server.js +54 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ 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.9.2] - 2026-04-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Unreadable `[object ErrorEvent]` in WebSocket error logs.** Recent Node / undici deliver a browser-style `ErrorEvent` (not a plain `Error`) to `ws.onerror`, and `` `${event}` `` stringifies that wrapper to `[object ErrorEvent]` — the original failure cause was being dropped. New `formatWebSocketError(err)` helper in `src/platform/utils.ts` pulls the first usable signal (`.message` → `.error.message` → `.type (code: .code)` → `String(err)`), wired into all five WebSocket error sites across Slack + Mattermost main clients, both MCP permission-server clients, and the UI re-emit. The Slack client's rejection and re-emitted `Error` now carry the underlying message too, instead of the opaque `"Socket Mode WebSocket error"`. (#347)
|
|
12
|
+
- **Worktree creation under a parent branch gave a generic "Failed to create worktree" message.** When a flat branch `test` already exists and the user requests `test/add-unit-coverage`, git refuses with `fatal: 'refs/heads/test' exists; cannot create 'refs/heads/test/add-unit-coverage'`. `parseWorktreeError` now matches this specific shape and reports `Branch <parent> already exists and blocks <nested>` with the suggestion to pick a non-nested name or delete the parent branch first. (#347)
|
|
13
|
+
|
|
14
|
+
## [1.9.1] - 2026-04-24
|
|
15
|
+
|
|
16
|
+
### Internals
|
|
17
|
+
- **Unified `Executor<TState>` contract.** New interface in `src/operations/executors/types.ts` formalizes what `MessageManager` actually relies on: `getState` / `reset` required, `handleReaction` / `serialize` optional. `BaseExecutor<T>` implements it. A new `contract.test.ts` iterates every executor and asserts the shape — catches drift when someone adds an executor without the required members. (#346)
|
|
18
|
+
- **Uniform `handleReaction` signature across all seven reaction executors** — `(postId, emoji, user, action, ctx) => Promise<boolean>`. Previously `TaskList`, `Subagent`, and `WorktreePrompt` had slightly different shapes. `MessageManager.handleReaction` now dispatches via a `reactionDispatchList()` table instead of an if/else chain. (#346)
|
|
19
|
+
- **`MessageManager.serialize()` aggregates executor state** for `SessionManager.persistSession`. The writer no longer reaches into individual executors via named getters (`getTaskListState()`, `getPendingContextPrompt()`). Legacy getters kept as `@deprecated` shims — they still have non-persistence consumers. (#346)
|
|
20
|
+
- **Byte-identical `sessions.json` guarantee.** New snapshot tests in `manager.test.ts` pin the full payload's field set and run the new and legacy (`CLAUDE_THREADS_SERIALIZE_V2=0`) paths through a parity assertion. No persisted-schema change on disk. (#346)
|
|
21
|
+
- **Rollback hatch:** `CLAUDE_THREADS_SERIALIZE_V2=0` falls back to the pre-refactor per-getter writer for one release. Removed in the next minor. (#346)
|
|
22
|
+
|
|
8
23
|
## [1.9.0] - 2026-04-24
|
|
9
24
|
|
|
10
25
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -51565,6 +51565,23 @@ function truncateAtWord(str2, maxLength) {
|
|
|
51565
51565
|
function escapeRegExp(string) {
|
|
51566
51566
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
51567
51567
|
}
|
|
51568
|
+
function formatWebSocketError(err) {
|
|
51569
|
+
if (err instanceof Error)
|
|
51570
|
+
return err.message;
|
|
51571
|
+
if (err && typeof err === "object") {
|
|
51572
|
+
const e = err;
|
|
51573
|
+
if (typeof e.message === "string" && e.message)
|
|
51574
|
+
return e.message;
|
|
51575
|
+
if (e.error instanceof Error)
|
|
51576
|
+
return e.error.message;
|
|
51577
|
+
if (typeof e.error === "string" && e.error)
|
|
51578
|
+
return e.error;
|
|
51579
|
+
if (typeof e.type === "string" && e.type) {
|
|
51580
|
+
return typeof e.code === "string" || typeof e.code === "number" ? `${e.type} (code: ${e.code})` : e.type;
|
|
51581
|
+
}
|
|
51582
|
+
}
|
|
51583
|
+
return String(err);
|
|
51584
|
+
}
|
|
51568
51585
|
function getPlatformIcon(platformType) {
|
|
51569
51586
|
switch (platformType) {
|
|
51570
51587
|
case "slack":
|
|
@@ -52110,9 +52127,10 @@ class MattermostClient extends BasePlatformClient {
|
|
|
52110
52127
|
this.onConnectionClosed();
|
|
52111
52128
|
};
|
|
52112
52129
|
this.ws.onerror = (event) => {
|
|
52113
|
-
|
|
52114
|
-
|
|
52115
|
-
|
|
52130
|
+
const msg = formatWebSocketError(event);
|
|
52131
|
+
wsLogger.warn(`WebSocket error: ${msg}`);
|
|
52132
|
+
this.emit("error", new Error(`WebSocket error: ${msg}`));
|
|
52133
|
+
reject(new Error(`WebSocket error: ${msg}`));
|
|
52116
52134
|
};
|
|
52117
52135
|
});
|
|
52118
52136
|
}
|
|
@@ -52540,11 +52558,12 @@ class SlackClient extends BasePlatformClient {
|
|
|
52540
52558
|
};
|
|
52541
52559
|
this.ws.onerror = (event) => {
|
|
52542
52560
|
clearTimeout(connectionTimeout);
|
|
52543
|
-
|
|
52561
|
+
const msg = formatWebSocketError(event);
|
|
52562
|
+
wsLogger.warn(`Socket Mode: WebSocket error: ${msg}`);
|
|
52544
52563
|
if (!this.isIntentionalDisconnect && !this.isReconnecting) {
|
|
52545
|
-
this.emit("error", new Error(
|
|
52564
|
+
this.emit("error", new Error(`Socket Mode WebSocket error: ${msg}`));
|
|
52546
52565
|
}
|
|
52547
|
-
doReject(new Error(
|
|
52566
|
+
doReject(new Error(`Socket Mode WebSocket error: ${msg}`));
|
|
52548
52567
|
};
|
|
52549
52568
|
});
|
|
52550
52569
|
}
|
|
@@ -53160,7 +53179,7 @@ class MattermostPermissionApi {
|
|
|
53160
53179
|
}
|
|
53161
53180
|
};
|
|
53162
53181
|
ws.onerror = (event) => {
|
|
53163
|
-
mcpLogger.error(`WebSocket error: ${event}`);
|
|
53182
|
+
mcpLogger.error(`WebSocket error: ${formatWebSocketError(event)}`);
|
|
53164
53183
|
if (!resolved) {
|
|
53165
53184
|
resolved = true;
|
|
53166
53185
|
clearTimeout(timeout);
|
|
@@ -53356,7 +53375,7 @@ class SlackPermissionApi {
|
|
|
53356
53375
|
}
|
|
53357
53376
|
};
|
|
53358
53377
|
ws.onerror = (event) => {
|
|
53359
|
-
mcpLogger.error(`Socket Mode WebSocket error: ${event}`);
|
|
53378
|
+
mcpLogger.error(`Socket Mode WebSocket error: ${formatWebSocketError(event)}`);
|
|
53360
53379
|
if (!resolved) {
|
|
53361
53380
|
resolved = true;
|
|
53362
53381
|
clearTimeout(timeout);
|
|
@@ -58611,7 +58630,15 @@ class TaskListExecutor extends BaseExecutor {
|
|
|
58611
58630
|
await ctx.platform.updatePost(this.state.tasksPostId, displayContent);
|
|
58612
58631
|
} catch {}
|
|
58613
58632
|
}
|
|
58614
|
-
|
|
58633
|
+
serialize() {
|
|
58634
|
+
return {
|
|
58635
|
+
postId: this.state.tasksPostId,
|
|
58636
|
+
content: this.state.lastTasksContent,
|
|
58637
|
+
isMinimized: this.state.tasksMinimized,
|
|
58638
|
+
isCompleted: this.state.tasksCompleted
|
|
58639
|
+
};
|
|
58640
|
+
}
|
|
58641
|
+
async handleReaction(postId, emoji, _user, action, ctx) {
|
|
58615
58642
|
ctx.logger.debug(`TaskListExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji}, action=${action}, tasksPostId=${this.state.tasksPostId?.substring(0, 8) ?? "none"}`);
|
|
58616
58643
|
if (postId !== this.state.tasksPostId) {
|
|
58617
58644
|
ctx.logger.debug(`TaskListExecutor: postId does not match tasksPostId, ignoring`);
|
|
@@ -58880,7 +58907,7 @@ class SubagentExecutor extends BaseExecutor {
|
|
|
58880
58907
|
}
|
|
58881
58908
|
return false;
|
|
58882
58909
|
}
|
|
58883
|
-
async handleReaction(postId, emoji, action, ctx) {
|
|
58910
|
+
async handleReaction(postId, emoji, _user, action, ctx) {
|
|
58884
58911
|
ctx.logger.debug(`SubagentExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji}, action=${action}`);
|
|
58885
58912
|
if (!isMinimizeToggleEmoji(emoji)) {
|
|
58886
58913
|
ctx.logger.debug(`SubagentExecutor: emoji ${emoji} is not minimize toggle, ignoring`);
|
|
@@ -59420,6 +59447,9 @@ class PromptExecutor extends BaseExecutor {
|
|
|
59420
59447
|
getPendingContextPrompt() {
|
|
59421
59448
|
return this.state.pendingContextPrompt;
|
|
59422
59449
|
}
|
|
59450
|
+
serialize() {
|
|
59451
|
+
return this.state.pendingContextPrompt;
|
|
59452
|
+
}
|
|
59423
59453
|
hasPendingContextPrompt() {
|
|
59424
59454
|
return this.state.pendingContextPrompt !== null;
|
|
59425
59455
|
}
|
|
@@ -60782,12 +60812,12 @@ class MessageManager {
|
|
|
60782
60812
|
await this.taskListExecutor.bumpToBottom(this.getExecutorContext());
|
|
60783
60813
|
}
|
|
60784
60814
|
getTaskListState() {
|
|
60785
|
-
|
|
60815
|
+
return this.taskListExecutor.serialize();
|
|
60816
|
+
}
|
|
60817
|
+
serialize() {
|
|
60786
60818
|
return {
|
|
60787
|
-
|
|
60788
|
-
|
|
60789
|
-
isMinimized: state.tasksMinimized,
|
|
60790
|
-
isCompleted: state.tasksCompleted
|
|
60819
|
+
taskList: this.taskListExecutor.serialize(),
|
|
60820
|
+
contextPrompt: this.promptExecutor.serialize()
|
|
60791
60821
|
};
|
|
60792
60822
|
}
|
|
60793
60823
|
hydrateTaskListState(persisted) {
|
|
@@ -60880,33 +60910,27 @@ class MessageManager {
|
|
|
60880
60910
|
getSession() {
|
|
60881
60911
|
return this.session;
|
|
60882
60912
|
}
|
|
60913
|
+
reactionDispatchList() {
|
|
60914
|
+
return [
|
|
60915
|
+
{ name: "QuestionApprovalExecutor", executor: this.questionApprovalExecutor },
|
|
60916
|
+
{ name: "MessageApprovalExecutor", executor: this.messageApprovalExecutor },
|
|
60917
|
+
{ name: "PromptExecutor", executor: this.promptExecutor },
|
|
60918
|
+
{ name: "BugReportExecutor", executor: this.bugReportExecutor },
|
|
60919
|
+
{ name: "TaskListExecutor", executor: this.taskListExecutor },
|
|
60920
|
+
{ name: "SubagentExecutor", executor: this.subagentExecutor }
|
|
60921
|
+
];
|
|
60922
|
+
}
|
|
60883
60923
|
async handleReaction(postId, emoji, user, action) {
|
|
60884
60924
|
const logger = log18.forSession(this.sessionId);
|
|
60885
60925
|
const ctx = this.getExecutorContext();
|
|
60886
60926
|
logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
|
|
60887
|
-
|
|
60888
|
-
|
|
60889
|
-
|
|
60890
|
-
|
|
60891
|
-
|
|
60892
|
-
|
|
60893
|
-
|
|
60894
|
-
}
|
|
60895
|
-
if (await this.promptExecutor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
60896
|
-
logger.debug("Reaction handled by PromptExecutor");
|
|
60897
|
-
return true;
|
|
60898
|
-
}
|
|
60899
|
-
if (await this.bugReportExecutor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
60900
|
-
logger.debug("Reaction handled by BugReportExecutor");
|
|
60901
|
-
return true;
|
|
60902
|
-
}
|
|
60903
|
-
if (await this.taskListExecutor.handleReaction(postId, emoji, action, ctx)) {
|
|
60904
|
-
logger.debug("Reaction handled by TaskListExecutor");
|
|
60905
|
-
return true;
|
|
60906
|
-
}
|
|
60907
|
-
if (await this.subagentExecutor.handleReaction(postId, emoji, action, ctx)) {
|
|
60908
|
-
logger.debug("Reaction handled by SubagentExecutor");
|
|
60909
|
-
return true;
|
|
60927
|
+
for (const { name, executor } of this.reactionDispatchList()) {
|
|
60928
|
+
if (!executor.handleReaction)
|
|
60929
|
+
continue;
|
|
60930
|
+
if (await executor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
60931
|
+
logger.debug(`Reaction handled by ${name}`);
|
|
60932
|
+
return true;
|
|
60933
|
+
}
|
|
60910
60934
|
}
|
|
60911
60935
|
logger.debug("Reaction not handled by any executor");
|
|
60912
60936
|
return false;
|
|
@@ -66313,6 +66337,15 @@ function parseWorktreeError(error) {
|
|
|
66313
66337
|
suggestion: "Try a different branch name, or use `!worktree list` to see existing worktrees"
|
|
66314
66338
|
};
|
|
66315
66339
|
}
|
|
66340
|
+
const parentBlocksMatch = message.match(/'refs\/heads\/([^']+)' exists; cannot create '(?:refs\/heads\/)?([^']+)'/i);
|
|
66341
|
+
if (parentBlocksMatch) {
|
|
66342
|
+
const parent = parentBlocksMatch[1];
|
|
66343
|
+
const nested = parentBlocksMatch[2];
|
|
66344
|
+
return {
|
|
66345
|
+
summary: `Branch ${parent} already exists and blocks ${nested}`,
|
|
66346
|
+
suggestion: `Pick a name that does not start with ${parent}/, or delete the existing ${parent} branch first.`
|
|
66347
|
+
};
|
|
66348
|
+
}
|
|
66316
66349
|
if (lowerMessage.includes("already exists")) {
|
|
66317
66350
|
return {
|
|
66318
66351
|
summary: "A worktree or branch with this name already exists",
|
|
@@ -68910,19 +68943,29 @@ class SessionManager extends EventEmitter4 {
|
|
|
68910
68943
|
this.stopTyping(session);
|
|
68911
68944
|
}
|
|
68912
68945
|
persistSession(session) {
|
|
68913
|
-
|
|
68914
|
-
|
|
68915
|
-
|
|
68916
|
-
|
|
68917
|
-
|
|
68918
|
-
|
|
68919
|
-
|
|
68920
|
-
|
|
68921
|
-
|
|
68922
|
-
|
|
68923
|
-
|
|
68946
|
+
const useSerializeV2 = process.env.CLAUDE_THREADS_SERIALIZE_V2 !== "0";
|
|
68947
|
+
let taskListSnapshot;
|
|
68948
|
+
let contextPromptSnapshot;
|
|
68949
|
+
if (useSerializeV2 && session.messageManager) {
|
|
68950
|
+
const serialized = session.messageManager.serialize();
|
|
68951
|
+
taskListSnapshot = serialized.taskList;
|
|
68952
|
+
if (serialized.contextPrompt) {
|
|
68953
|
+
contextPromptSnapshot = serialized.contextPrompt;
|
|
68954
|
+
}
|
|
68955
|
+
} else {
|
|
68956
|
+
const legacyPrompt = session.messageManager?.getPendingContextPrompt();
|
|
68957
|
+
if (legacyPrompt) {
|
|
68958
|
+
contextPromptSnapshot = {
|
|
68959
|
+
postId: legacyPrompt.postId,
|
|
68960
|
+
queuedPrompt: legacyPrompt.queuedPrompt,
|
|
68961
|
+
queuedFiles: legacyPrompt.queuedFiles,
|
|
68962
|
+
threadMessageCount: legacyPrompt.threadMessageCount,
|
|
68963
|
+
createdAt: legacyPrompt.createdAt,
|
|
68964
|
+
availableOptions: legacyPrompt.availableOptions
|
|
68965
|
+
};
|
|
68966
|
+
}
|
|
68967
|
+
taskListSnapshot = session.messageManager?.getTaskListState();
|
|
68924
68968
|
}
|
|
68925
|
-
const taskState = session.messageManager?.getTaskListState();
|
|
68926
68969
|
const state = {
|
|
68927
68970
|
platformId: session.platformId,
|
|
68928
68971
|
threadId: session.threadId,
|
|
@@ -68937,10 +68980,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68937
68980
|
sessionAllowedUsers: [...session.sessionAllowedUsers],
|
|
68938
68981
|
forceInteractivePermissions: session.forceInteractivePermissions,
|
|
68939
68982
|
sessionStartPostId: session.sessionStartPostId,
|
|
68940
|
-
tasksPostId:
|
|
68941
|
-
lastTasksContent:
|
|
68942
|
-
tasksCompleted:
|
|
68943
|
-
tasksMinimized:
|
|
68983
|
+
tasksPostId: taskListSnapshot?.postId ?? null,
|
|
68984
|
+
lastTasksContent: taskListSnapshot?.content ?? null,
|
|
68985
|
+
tasksCompleted: taskListSnapshot?.isCompleted ?? false,
|
|
68986
|
+
tasksMinimized: taskListSnapshot?.isMinimized ?? false,
|
|
68944
68987
|
worktreeInfo: session.worktreeInfo,
|
|
68945
68988
|
isWorktreeOwner: session.isWorktreeOwner,
|
|
68946
68989
|
pendingWorktreePrompt: session.pendingWorktreePrompt,
|
|
@@ -68948,7 +68991,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68948
68991
|
queuedPrompt: session.queuedPrompt,
|
|
68949
68992
|
queuedFiles: session.queuedFiles,
|
|
68950
68993
|
firstPrompt: session.firstPrompt,
|
|
68951
|
-
pendingContextPrompt:
|
|
68994
|
+
pendingContextPrompt: contextPromptSnapshot,
|
|
68952
68995
|
needsContextPromptOnNextMessage: session.needsContextPromptOnNextMessage,
|
|
68953
68996
|
lifecyclePostId: session.lifecyclePostId,
|
|
68954
68997
|
isPaused: session.lifecycle.state === "paused" || session.lifecycle.state === "interrupted",
|
|
@@ -80089,7 +80132,8 @@ function wirePlatformEvents(platformId, client, session, ui) {
|
|
|
80089
80132
|
ui.setPlatformStatus(platformId, { reconnecting: true, reconnectAttempts: attempt });
|
|
80090
80133
|
});
|
|
80091
80134
|
client.on("error", (e) => {
|
|
80092
|
-
|
|
80135
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
80136
|
+
ui.addLog({ level: "error", component: platformId, message });
|
|
80093
80137
|
});
|
|
80094
80138
|
}
|
|
80095
80139
|
program.name("claude-threads").version(VERSION).description("Share Claude Code sessions in Mattermost").option("--url <url>", "Mattermost server URL").option("--token <token>", "Mattermost bot token").option("--channel <id>", "Mattermost channel ID").option("--bot-name <name>", "Bot mention name (default: claude-code)").option("--allowed-users <users>", "Comma-separated allowed usernames").option("--permission-mode <mode>", "Permission mode: default | auto | bypass (default: from config)").option("--skip-permissions", "[deprecated] Alias for --permission-mode bypass").option("--no-skip-permissions", "[deprecated] Alias for --permission-mode default").option("--chrome", "Enable Claude in Chrome integration").option("--no-chrome", "Disable Claude in Chrome integration").option("--worktree-mode <mode>", "Git worktree mode: off, prompt, require (default: prompt)").option("--keep-alive", "Enable system sleep prevention (default: enabled)").option("--no-keep-alive", "Disable system sleep prevention").option("--setup", "Run interactive setup wizard (reconfigure existing settings)").option("--debug", "Enable debug logging").option("--skip-version-check", "Skip Claude CLI version compatibility check").option("--auto-restart", "Enable auto-restart on updates (default when autoUpdate enabled)").option("--no-auto-restart", "Disable auto-restart on updates").option("--headless", "Run without interactive UI (logs to stdout)").parse();
|
|
@@ -50456,6 +50456,23 @@ class BaseExecutor {
|
|
|
50456
50456
|
}
|
|
50457
50457
|
}
|
|
50458
50458
|
// src/platform/utils.ts
|
|
50459
|
+
function formatWebSocketError(err) {
|
|
50460
|
+
if (err instanceof Error)
|
|
50461
|
+
return err.message;
|
|
50462
|
+
if (err && typeof err === "object") {
|
|
50463
|
+
const e = err;
|
|
50464
|
+
if (typeof e.message === "string" && e.message)
|
|
50465
|
+
return e.message;
|
|
50466
|
+
if (e.error instanceof Error)
|
|
50467
|
+
return e.error.message;
|
|
50468
|
+
if (typeof e.error === "string" && e.error)
|
|
50469
|
+
return e.error;
|
|
50470
|
+
if (typeof e.type === "string" && e.type) {
|
|
50471
|
+
return typeof e.code === "string" || typeof e.code === "number" ? `${e.type} (code: ${e.code})` : e.type;
|
|
50472
|
+
}
|
|
50473
|
+
}
|
|
50474
|
+
return String(err);
|
|
50475
|
+
}
|
|
50459
50476
|
function truncateMessageSafely(message, maxLength, truncationIndicator = "... (truncated)") {
|
|
50460
50477
|
if (message.length <= maxLength)
|
|
50461
50478
|
return message;
|
|
@@ -51175,7 +51192,15 @@ class TaskListExecutor extends BaseExecutor {
|
|
|
51175
51192
|
await ctx.platform.updatePost(this.state.tasksPostId, displayContent);
|
|
51176
51193
|
} catch {}
|
|
51177
51194
|
}
|
|
51178
|
-
|
|
51195
|
+
serialize() {
|
|
51196
|
+
return {
|
|
51197
|
+
postId: this.state.tasksPostId,
|
|
51198
|
+
content: this.state.lastTasksContent,
|
|
51199
|
+
isMinimized: this.state.tasksMinimized,
|
|
51200
|
+
isCompleted: this.state.tasksCompleted
|
|
51201
|
+
};
|
|
51202
|
+
}
|
|
51203
|
+
async handleReaction(postId, emoji4, _user, action, ctx) {
|
|
51179
51204
|
ctx.logger.debug(`TaskListExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji4}, action=${action}, tasksPostId=${this.state.tasksPostId?.substring(0, 8) ?? "none"}`);
|
|
51180
51205
|
if (postId !== this.state.tasksPostId) {
|
|
51181
51206
|
ctx.logger.debug(`TaskListExecutor: postId does not match tasksPostId, ignoring`);
|
|
@@ -51444,7 +51469,7 @@ class SubagentExecutor extends BaseExecutor {
|
|
|
51444
51469
|
}
|
|
51445
51470
|
return false;
|
|
51446
51471
|
}
|
|
51447
|
-
async handleReaction(postId, emoji4, action, ctx) {
|
|
51472
|
+
async handleReaction(postId, emoji4, _user, action, ctx) {
|
|
51448
51473
|
ctx.logger.debug(`SubagentExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji4}, action=${action}`);
|
|
51449
51474
|
if (!isMinimizeToggleEmoji(emoji4)) {
|
|
51450
51475
|
ctx.logger.debug(`SubagentExecutor: emoji ${emoji4} is not minimize toggle, ignoring`);
|
|
@@ -51984,6 +52009,9 @@ class PromptExecutor extends BaseExecutor {
|
|
|
51984
52009
|
getPendingContextPrompt() {
|
|
51985
52010
|
return this.state.pendingContextPrompt;
|
|
51986
52011
|
}
|
|
52012
|
+
serialize() {
|
|
52013
|
+
return this.state.pendingContextPrompt;
|
|
52014
|
+
}
|
|
51987
52015
|
hasPendingContextPrompt() {
|
|
51988
52016
|
return this.state.pendingContextPrompt !== null;
|
|
51989
52017
|
}
|
|
@@ -52734,12 +52762,12 @@ class MessageManager {
|
|
|
52734
52762
|
await this.taskListExecutor.bumpToBottom(this.getExecutorContext());
|
|
52735
52763
|
}
|
|
52736
52764
|
getTaskListState() {
|
|
52737
|
-
|
|
52765
|
+
return this.taskListExecutor.serialize();
|
|
52766
|
+
}
|
|
52767
|
+
serialize() {
|
|
52738
52768
|
return {
|
|
52739
|
-
|
|
52740
|
-
|
|
52741
|
-
isMinimized: state.tasksMinimized,
|
|
52742
|
-
isCompleted: state.tasksCompleted
|
|
52769
|
+
taskList: this.taskListExecutor.serialize(),
|
|
52770
|
+
contextPrompt: this.promptExecutor.serialize()
|
|
52743
52771
|
};
|
|
52744
52772
|
}
|
|
52745
52773
|
hydrateTaskListState(persisted) {
|
|
@@ -52832,33 +52860,27 @@ class MessageManager {
|
|
|
52832
52860
|
getSession() {
|
|
52833
52861
|
return this.session;
|
|
52834
52862
|
}
|
|
52863
|
+
reactionDispatchList() {
|
|
52864
|
+
return [
|
|
52865
|
+
{ name: "QuestionApprovalExecutor", executor: this.questionApprovalExecutor },
|
|
52866
|
+
{ name: "MessageApprovalExecutor", executor: this.messageApprovalExecutor },
|
|
52867
|
+
{ name: "PromptExecutor", executor: this.promptExecutor },
|
|
52868
|
+
{ name: "BugReportExecutor", executor: this.bugReportExecutor },
|
|
52869
|
+
{ name: "TaskListExecutor", executor: this.taskListExecutor },
|
|
52870
|
+
{ name: "SubagentExecutor", executor: this.subagentExecutor }
|
|
52871
|
+
];
|
|
52872
|
+
}
|
|
52835
52873
|
async handleReaction(postId, emoji4, user, action) {
|
|
52836
52874
|
const logger = log3.forSession(this.sessionId);
|
|
52837
52875
|
const ctx = this.getExecutorContext();
|
|
52838
52876
|
logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji4}, user=${user}, action=${action}`);
|
|
52839
|
-
|
|
52840
|
-
|
|
52841
|
-
|
|
52842
|
-
|
|
52843
|
-
|
|
52844
|
-
|
|
52845
|
-
|
|
52846
|
-
}
|
|
52847
|
-
if (await this.promptExecutor.handleReaction(postId, emoji4, user, action, ctx)) {
|
|
52848
|
-
logger.debug("Reaction handled by PromptExecutor");
|
|
52849
|
-
return true;
|
|
52850
|
-
}
|
|
52851
|
-
if (await this.bugReportExecutor.handleReaction(postId, emoji4, user, action, ctx)) {
|
|
52852
|
-
logger.debug("Reaction handled by BugReportExecutor");
|
|
52853
|
-
return true;
|
|
52854
|
-
}
|
|
52855
|
-
if (await this.taskListExecutor.handleReaction(postId, emoji4, action, ctx)) {
|
|
52856
|
-
logger.debug("Reaction handled by TaskListExecutor");
|
|
52857
|
-
return true;
|
|
52858
|
-
}
|
|
52859
|
-
if (await this.subagentExecutor.handleReaction(postId, emoji4, action, ctx)) {
|
|
52860
|
-
logger.debug("Reaction handled by SubagentExecutor");
|
|
52861
|
-
return true;
|
|
52877
|
+
for (const { name, executor } of this.reactionDispatchList()) {
|
|
52878
|
+
if (!executor.handleReaction)
|
|
52879
|
+
continue;
|
|
52880
|
+
if (await executor.handleReaction(postId, emoji4, user, action, ctx)) {
|
|
52881
|
+
logger.debug(`Reaction handled by ${name}`);
|
|
52882
|
+
return true;
|
|
52883
|
+
}
|
|
52862
52884
|
}
|
|
52863
52885
|
logger.debug("Reaction not handled by any executor");
|
|
52864
52886
|
return false;
|
|
@@ -57482,7 +57504,7 @@ class MattermostPermissionApi {
|
|
|
57482
57504
|
}
|
|
57483
57505
|
};
|
|
57484
57506
|
ws.onerror = (event) => {
|
|
57485
|
-
mcpLogger.error(`WebSocket error: ${event}`);
|
|
57507
|
+
mcpLogger.error(`WebSocket error: ${formatWebSocketError(event)}`);
|
|
57486
57508
|
if (!resolved) {
|
|
57487
57509
|
resolved = true;
|
|
57488
57510
|
clearTimeout(timeout);
|
|
@@ -57749,7 +57771,7 @@ class SlackPermissionApi {
|
|
|
57749
57771
|
}
|
|
57750
57772
|
};
|
|
57751
57773
|
ws.onerror = (event) => {
|
|
57752
|
-
mcpLogger.error(`Socket Mode WebSocket error: ${event}`);
|
|
57774
|
+
mcpLogger.error(`Socket Mode WebSocket error: ${formatWebSocketError(event)}`);
|
|
57753
57775
|
if (!resolved) {
|
|
57754
57776
|
resolved = true;
|
|
57755
57777
|
clearTimeout(timeout);
|