diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cb2d4551..0cf9603a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,13 +10,11 @@ ### Changes - Docs: clarify per-agent auth stores, sandboxed skill binaries, and elevated semantics. -- Usage: add MiniMax coding plan usage tracking. - Daemon: support profile-aware service names for multi-gateway setups. (#671) — thanks @bjesuiter. - Docs: add FAQ entries for missing provider auth after adding agents and Gemini thinking signature errors. - Agents: add optional auth-profile copy prompt on `agents add` and improve auth error messaging. - Security: expand `clawdbot security audit` checks (model hygiene, config includes, plugin allowlists, exposure matrix) and extend `--fix` to tighten more sensitive state paths. - Security: add `SECURITY.md` reporting policy. -- Voice Call: add Plivo provider for the voice-call plugin. (#846) — thanks @vrknetha. - Plugins: add Zalo channel plugin with gateway HTTP hooks and onboarding install prompt. (#854) — thanks @longmaba. - Onboarding: add a security checkpoint prompt (docs link + sandboxing hint); require `--accept-risk` for `--non-interactive`. - Docs: expand gateway security hardening guidance and incident response checklist. @@ -30,78 +28,61 @@ - CLI/Docs: add per-command CLI doc pages and link them from `clawdbot --help`. - Browser: copy the installed Chrome extension path to clipboard after `clawdbot browser extension install/path`. - WhatsApp: add `channels.whatsapp.sendReadReceipts` to disable auto read receipts. (#882) — thanks @chrisrodz. + +### Fixes +- Slack: isolate thread history and avoid inheriting channel transcripts for new threads by default. (#758) +- Gateway: forward termination signals to respawned CLI child processes to avoid orphaned systemd runs. (#933) — thanks @roshanasingh4. +- Browser: add tests for snapshot labels/efficient query params and labeled image responses. +- macOS: ensure launchd log directory exists with a test-only override. (#909) — thanks @roshanasingh4. +- macOS: format ConnectionsStore config to satisfy SwiftFormat lint. (#852) — thanks @mneves75. +- Packaging: run `pnpm build` on `prepack` so npm publishes include fresh `dist/` output. +- Telegram: register dock native commands with underscores to avoid `BOT_COMMAND_INVALID` (#929, fixes #901) — thanks @grp06. +- Google: downgrade unsigned thinking blocks before send to avoid missing signature errors. +- Agents: make user time zone and 24-hour time explicit in the system prompt. (#859) — thanks @CashWilliams. +- Agents: strip downgraded tool call text without eating adjacent replies and filter thinking-tag leaks. (#905) — thanks @erikpr1994. +- Agents: cap tool call IDs for OpenAI/OpenRouter to avoid request rejections. (#875) — thanks @j1philli. +- Doctor: avoid re-adding WhatsApp config when only legacy ack reactions are set. (#927, fixes #900) — thanks @grp06. +- Agents: scrub tuple `items` schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06. +- Agents: stabilize sub-agent announce status from runtime outcomes and normalize Result/Notes. (#835) — thanks @roshanasingh4. +- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06. +- Auth: normalize Claude Code CLI profile mode to oauth and auto-migrate config. (#855) — thanks @sebslight. +- Daemon: clear persisted launchd disabled state before bootstrap (fixes `daemon install` after uninstall). (#849) — thanks @ndraiman. +- Logging: tolerate `EIO` from console writes to avoid gateway crashes. (#925, fixes #878) — thanks @grp06. +- Sandbox: restore `docker.binds` config validation for custom bind mounts. (#873) — thanks @akonyer. +- Sandbox: preserve configured PATH for `docker exec` so custom tools remain available. (#873) — thanks @akonyer. +- Slack: respect `channels.slack.requireMention` default when resolving channel mention gating. (#850) — thanks @evalexpr. +- Telegram: aggregate split inbound messages into one prompt (reduces “one reply per fragment”). +- Telegram: let control commands bypass per-chat sequentialization; always allow abort triggers. +- Auto-reply: treat trailing `NO_REPLY` tokens as silent replies. +- Config: prevent partial config writes from clobbering unrelated settings (base hash guard + merge patch for connection saves). + +## 2026.1.14 + +### Changes +- Usage: add MiniMax coding plan usage tracking. - Auth: label Claude Code CLI auth options. (#915) — thanks @SeanZoR. - Docs: standardize Claude Code CLI naming across docs and prompts. (follow-up to #915) - Telegram: add message delete action in the message tool. (#903) — thanks @sleontenko. - Config: add `channels..configWrites` gating for channel-initiated config writes; migrate Slack channel IDs. ### Fixes - -#### Agents -- Agents: make user time zone and 24-hour time explicit in the system prompt. (#859) — thanks @CashWilliams. -- Agents: strip downgraded tool call text without eating adjacent replies and filter thinking-tag leaks. (#905) — thanks @erikpr1994. -- Agents: cap tool call IDs for OpenAI/OpenRouter to avoid request rejections. (#875) — thanks @j1philli. -- Agents: scrub tuple `items` schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06. -- Agents: stabilize sub-agent announce status from runtime outcomes and normalize Result/Notes. (#835) — thanks @roshanasingh4. -- Agent: clear run context after CLI runs (`clearAgentRunContext`) to avoid runaway contexts. (#934) — thanks @ronak-guliani. - -#### Browser -- Browser: add tests for snapshot labels/efficient query params and labeled image responses. -- UI: use application-defined WebSocket close code (browser compatibility). (#918) — thanks @rahthakor. -- UI: move Docs link into the left navigation menu. - -#### macOS -- macOS: ensure launchd log directory exists with a test-only override. (#909) — thanks @roshanasingh4. -- macOS: format ConnectionsStore config to satisfy SwiftFormat lint. (#852) — thanks @mneves75. -- macOS: pass auth token/password to dashboard URL for authenticated access. (#918) — thanks @rahthakor. -- macOS: fix cron preview/testing payload to use `channel` key. (#867) — thanks @wes-davis. - -#### Messaging / Channels -- Messaging: unify markdown formatting + format-first chunking for Slack/Telegram/Signal. (#920) — thanks @TheSethRose. -- iMessage: prefer handle routing for direct-message replies; include imsg RPC error details. (#935) -- Slack: respect `channels.slack.requireMention` default when resolving channel mention gating. (#850) — thanks @evalexpr. -- Slack: drop Socket Mode events with mismatched `api_app_id`/`team_id`. (#889) — thanks @roshanasingh4. -- Discord: isolate autoThread thread context. (#856) — thanks @davidguttman. -- Telegram: register dock native commands with underscores to avoid `BOT_COMMAND_INVALID` (#929, fixes #901) — thanks @grp06. -- Telegram: honor `channels.telegram.timeoutSeconds` for grammY API requests. (#863) — thanks @Snaver. -- Telegram: split long captions into media + follow-up text messages. (#907) — thanks @jalehman. -- Telegram: migrate group config when supergroups change chat IDs. (#906) — thanks @sleontenko. -- Telegram: aggregate split inbound messages into one prompt (reduces “one reply per fragment”). -- Telegram: let control commands bypass per-chat sequentialization; always allow abort triggers. -- WhatsApp: fix context isolation using wrong ID (was bot's number, now conversation ID). (#911) — thanks @tristanmanchester. -- WhatsApp: honor SenderE164 for owner command auth when SenderId is LID; let owner control commands bypass group mention gating. -- WhatsApp: normalize user JIDs with device suffix for allowlist checks in groups. (#838) — thanks @peschee. - -#### Gateway / Daemon / Sessions -- Gateway/UI: ship session defaults in the hello snapshot so the Control UI canonicalizes main session keys (no bare `main` alias). -- Gateway: forward termination signals to respawned CLI child processes to avoid orphaned systemd runs. (#933) — thanks @roshanasingh4. - Sessions: return deep clones (`structuredClone`) so cached session entries can't be mutated. (#934) — thanks @ronak-guliani. - Heartbeat: keep `updatedAt` monotonic when restoring heartbeat sessions. (#934) — thanks @ronak-guliani. -- Daemon: clear persisted launchd disabled state before bootstrap (fixes `daemon install` after uninstall). (#849) — thanks @ndraiman. -- Gateway/Dev: ensure `pnpm gateway:dev` always uses the dev profile config + state (`~/.clawdbot-dev`). +- Agent: clear run context after CLI runs (`clearAgentRunContext`) to avoid runaway contexts. (#934) — thanks @ronak-guliani. + - Mac: pass auth token/password to dashboard URL for authenticated access. (#918) — thanks @rahthakor. + - UI: use application-defined WebSocket close code (browser compatibility). (#918) — thanks @rahthakor. - TUI: render picker overlays via the overlay stack so /models and /settings display. (#921) — thanks @grizzdank. - TUI: add a bright spinner + elapsed time in the status line for send/stream/run states. -- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06. -- Logging: tolerate `EIO` from console writes to avoid gateway crashes. (#925, fixes #878) — thanks @grp06. - -#### Tools -- Tools: enable `web_fetch` by default (unless explicitly disabled in config). - -#### Auth / Models -- Auth: normalize Claude Code CLI profile mode to oauth and auto-migrate config. (#855) — thanks @sebslight. -- Google: downgrade unsigned thinking blocks before send to avoid missing signature errors. - -#### Config / Doctor -- Doctor: avoid re-adding WhatsApp config when only legacy ack reactions are set. (#927, fixes #900) — thanks @grp06. -- Config: prevent partial config writes from clobbering unrelated settings (base hash guard + merge patch for connection saves). - -#### Packaging / Auto-reply -- Packaging: run `pnpm build` on `prepack` so npm publishes include fresh `dist/` output. -- Auto-reply: treat trailing `NO_REPLY` tokens as silent replies. - -#### Sandbox -- Sandbox: restore `docker.binds` config validation for custom bind mounts. (#873) — thanks @akonyer. -- Sandbox: preserve configured PATH for `docker exec` so custom tools remain available. (#873) — thanks @akonyer. +- Gateway/Dev: ensure `pnpm gateway:dev` always uses the dev profile config + state (`~/.clawdbot-dev`). +- macOS: fix cron preview/testing payload to use `channel` key. (#867) — thanks @wes-davis. +- Telegram: honor `channels.telegram.timeoutSeconds` for grammY API requests. (#863) — thanks @Snaver. +- Telegram: split long captions into media + follow-up text messages. (#907) - thanks @jalehman. +- Telegram: migrate group config when supergroups change chat IDs. (#906) — thanks @sleontenko. +- Messaging: unify markdown formatting + format-first chunking for Slack/Telegram/Signal. (#920) — thanks @TheSethRose. +- Slack: drop Socket Mode events with mismatched `api_app_id`/`team_id`. (#889) — thanks @roshanasingh4. +- Discord: isolate autoThread thread context. (#856) — thanks @davidguttman. +- WhatsApp: fix context isolation using wrong ID (was bot's number, now conversation ID). (#911) — thanks @tristanmanchester. +- WhatsApp: normalize user JIDs with device suffix for allowlist checks in groups. (#838) — thanks @peschee. ## 2026.1.13 diff --git a/docs/channels/slack.md b/docs/channels/slack.md index 8df75c060..f7f4af044 100644 --- a/docs/channels/slack.md +++ b/docs/channels/slack.md @@ -269,6 +269,11 @@ By default, Clawdbot replies in the main channel. Use `channels.slack.replyToMod The mode applies to both auto-replies and agent tool calls (`slack sendMessage`). +### Thread session isolation +Slack thread sessions are isolated by default. Configure with: +- `channels.slack.thread.historyScope`: `thread` (default) keeps per-thread history; `channel` shares history across the channel. +- `channels.slack.thread.inheritParent`: `false` (default) starts a clean thread session; `true` copies the parent channel transcript into the thread session. + ### Manual threading tags For fine-grained control, use these tags in agent responses: - `[[reply_to_current]]` — reply to the triggering message (start/continue thread). diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 254f53455..32670dc36 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -1050,6 +1050,10 @@ Slack runs in Socket Mode and requires both a bot token and app token: reactionNotifications: "own", // off | own | all | allowlist reactionAllowlist: ["U123"], replyToMode: "off", // off | first | all + thread: { + historyScope: "thread", // thread | channel + inheritParent: false + }, actions: { reactions: true, messages: true, @@ -1083,6 +1087,10 @@ Reaction notification modes: - `all`: all reactions on all messages. - `allowlist`: reactions from `channels.slack.reactionAllowlist` on all messages (empty list disables). +Thread session isolation: +- `channels.slack.thread.historyScope` controls whether thread history is per-thread (`thread`, default) or shared across the channel (`channel`). +- `channels.slack.thread.inheritParent` controls whether new thread sessions inherit the parent channel transcript (default: false). + Slack action groups (gate `slack` tool actions): | Action group | Default | Notes | | --- | --- | --- | diff --git a/src/config/schema.ts b/src/config/schema.ts index bccd67758..4b84c154e 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -202,6 +202,8 @@ const FIELD_LABELS: Record = { "channels.discord.token": "Discord Bot Token", "channels.slack.botToken": "Slack Bot Token", "channels.slack.appToken": "Slack App Token", + "channels.slack.thread.historyScope": "Slack Thread History Scope", + "channels.slack.thread.inheritParent": "Slack Thread Parent Inheritance", "channels.signal.account": "Signal Account", "channels.imessage.cliPath": "iMessage CLI Path", "plugins.enabled": "Enable Plugins", @@ -243,6 +245,10 @@ const FIELD_HELP: Record = { "tools.web.fetch.userAgent": "Override User-Agent header for web_fetch requests.", "channels.slack.allowBots": "Allow bot-authored messages to trigger Slack replies (default: false).", + "channels.slack.thread.historyScope": + 'Scope for Slack thread history context ("thread" isolates per thread; "channel" reuses channel history).', + "channels.slack.thread.inheritParent": + "If true, Slack thread sessions inherit the parent channel transcript (default: false).", "auth.profiles": "Named auth profiles (provider + mode + optional email).", "auth.order": "Ordered auth profile IDs per provider (used for automatic failover).", "auth.cooldowns.billingBackoffHours": diff --git a/src/config/types.slack.ts b/src/config/types.slack.ts index 20ed3f8ce..23f2e36a6 100644 --- a/src/config/types.slack.ts +++ b/src/config/types.slack.ts @@ -60,6 +60,13 @@ export type SlackSlashCommandConfig = { ephemeral?: boolean; }; +export type SlackThreadConfig = { + /** Scope for thread history context (thread|channel). Default: thread. */ + historyScope?: "thread" | "channel"; + /** If true, thread sessions inherit the parent channel transcript. Default: false. */ + inheritParent?: boolean; +}; + export type SlackAccountConfig = { /** Optional display name for this account (used in CLI/UI lists). */ name?: string; @@ -101,6 +108,8 @@ export type SlackAccountConfig = { reactionAllowlist?: Array; /** Control reply threading when reply tags are present (off|first|all). */ replyToMode?: ReplyToMode; + /** Thread session behavior. */ + thread?: SlackThreadConfig; actions?: SlackActionConfig; slashCommand?: SlackSlashCommandConfig; dm?: SlackDmConfig; diff --git a/src/config/zod-schema.providers-core.ts b/src/config/zod-schema.providers-core.ts index 00bd547de..3d18799b3 100644 --- a/src/config/zod-schema.providers-core.ts +++ b/src/config/zod-schema.providers-core.ts @@ -203,6 +203,11 @@ export const SlackChannelSchema = z.object({ systemPrompt: z.string().optional(), }); +export const SlackThreadSchema = z.object({ + historyScope: z.enum(["thread", "channel"]).optional(), + inheritParent: z.boolean().optional(), +}); + export const SlackAccountSchema = z.object({ name: z.string().optional(), capabilities: z.array(z.string()).optional(), @@ -224,6 +229,7 @@ export const SlackAccountSchema = z.object({ reactionNotifications: z.enum(["off", "own", "all", "allowlist"]).optional(), reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(), replyToMode: ReplyToModeSchema.optional(), + thread: SlackThreadSchema.optional(), actions: z .object({ reactions: z.boolean().optional(), diff --git a/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts b/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts index 400d84bb2..e377789e8 100644 --- a/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts +++ b/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts @@ -327,6 +327,81 @@ describe("monitorSlackProvider tool results", () => { expect(capturedCtx.CommandBody).toBe("second"); }); + it("scopes thread history to the thread by default", async () => { + config = { + messages: { ackReactionScope: "group-mentions" }, + channels: { + slack: { + historyLimit: 5, + dm: { enabled: true, policy: "open", allowFrom: ["*"] }, + channels: { C1: { allow: true, requireMention: false } }, + }, + }, + }; + + const capturedCtx: Array<{ Body?: string }> = []; + replyMock.mockImplementation(async (ctx) => { + capturedCtx.push(ctx ?? {}); + return undefined; + }); + + const controller = new AbortController(); + const run = monitorSlackProvider({ + botToken: "bot-token", + appToken: "app-token", + abortSignal: controller.signal, + }); + + await waitForEvent("message"); + const handler = getSlackHandlers()?.get("message"); + if (!handler) throw new Error("Slack message handler not registered"); + + await handler({ + event: { + type: "message", + user: "U1", + text: "thread-a-one", + ts: "200", + thread_ts: "100", + channel: "C1", + channel_type: "channel", + }, + }); + + await handler({ + event: { + type: "message", + user: "U1", + text: "thread-a-two", + ts: "201", + thread_ts: "100", + channel: "C1", + channel_type: "channel", + }, + }); + + await handler({ + event: { + type: "message", + user: "U2", + text: "thread-b-one", + ts: "301", + thread_ts: "300", + channel: "C1", + channel_type: "channel", + }, + }); + + await flush(); + controller.abort(); + await run; + + expect(replyMock).toHaveBeenCalledTimes(3); + expect(capturedCtx[1]?.Body).toContain("thread-a-one"); + expect(capturedCtx[2]?.Body).not.toContain("thread-a-one"); + expect(capturedCtx[2]?.Body).not.toContain("thread-a-two"); + }); + it("updates assistant thread status when replies start", async () => { replyMock.mockImplementation(async (_ctx, opts) => { await opts?.onReplyStart?.(); diff --git a/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts b/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts index 1ce1c18e4..ab808eb4a 100644 --- a/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts +++ b/src/slack/monitor.tool-result.threads-top-level-replies-replytomode-is-all.test.ts @@ -202,10 +202,60 @@ describe("monitorSlackProvider tool results", () => { ParentSessionKey?: string; }; expect(ctx.SessionKey).toBe("agent:main:main:thread:123"); - expect(ctx.ParentSessionKey).toBe("agent:main:main"); + expect(ctx.ParentSessionKey).toBeUndefined(); }); - it("forks thread sessions and injects starter context", async () => { + it("keeps thread parent inheritance opt-in", async () => { + replyMock.mockResolvedValue({ text: "thread reply" }); + + config = { + messages: { responsePrefix: "PFX" }, + channels: { + slack: { + dm: { enabled: true, policy: "open", allowFrom: ["*"] }, + channels: { C1: { allow: true, requireMention: false } }, + thread: { inheritParent: true }, + }, + }, + }; + + const controller = new AbortController(); + const run = monitorSlackProvider({ + botToken: "bot-token", + appToken: "app-token", + abortSignal: controller.signal, + }); + + await waitForEvent("message"); + const handler = getSlackHandlers()?.get("message"); + if (!handler) throw new Error("Slack message handler not registered"); + + await handler({ + event: { + type: "message", + user: "U1", + text: "hello", + ts: "123", + thread_ts: "111.222", + channel: "C1", + channel_type: "channel", + }, + }); + + await flush(); + controller.abort(); + await run; + + expect(replyMock).toHaveBeenCalledTimes(1); + const ctx = replyMock.mock.calls[0]?.[0] as { + SessionKey?: string; + ParentSessionKey?: string; + }; + expect(ctx.SessionKey).toBe("agent:main:slack:channel:C1:thread:111.222"); + expect(ctx.ParentSessionKey).toBe("agent:main:slack:channel:C1"); + }); + + it("injects starter context for thread replies", async () => { replyMock.mockResolvedValue({ text: "ok" }); const client = getSlackClient(); @@ -265,7 +315,7 @@ describe("monitorSlackProvider tool results", () => { ThreadLabel?: string; }; expect(ctx.SessionKey).toBe("agent:main:slack:channel:C1:thread:111.222"); - expect(ctx.ParentSessionKey).toBe("agent:main:slack:channel:C1"); + expect(ctx.ParentSessionKey).toBeUndefined(); expect(ctx.ThreadStarterBody).toContain("starter message"); expect(ctx.ThreadLabel).toContain("Slack thread #general"); }); @@ -329,7 +379,7 @@ describe("monitorSlackProvider tool results", () => { ParentSessionKey?: string; }; expect(ctx.SessionKey).toBe("agent:support:slack:channel:C1:thread:111.222"); - expect(ctx.ParentSessionKey).toBe("agent:support:slack:channel:C1"); + expect(ctx.ParentSessionKey).toBeUndefined(); }); it("keeps replies in channel root when message is not threaded (replyToMode off)", async () => { diff --git a/src/slack/monitor/context.ts b/src/slack/monitor/context.ts index 452ef3389..27bee0460 100644 --- a/src/slack/monitor/context.ts +++ b/src/slack/monitor/context.ts @@ -74,6 +74,8 @@ export type SlackMonitorContext = { reactionMode: SlackReactionNotificationMode; reactionAllowlist: Array; replyToMode: "off" | "first" | "all"; + threadHistoryScope: "thread" | "channel"; + threadInheritParent: boolean; slashCommand: Required; textLimit: number; ackReactionScope: string; @@ -133,6 +135,8 @@ export function createSlackMonitorContext(params: { reactionMode: SlackReactionNotificationMode; reactionAllowlist: Array; replyToMode: SlackMonitorContext["replyToMode"]; + threadHistoryScope: SlackMonitorContext["threadHistoryScope"]; + threadInheritParent: SlackMonitorContext["threadInheritParent"]; slashCommand: SlackMonitorContext["slashCommand"]; textLimit: number; ackReactionScope: string; @@ -363,6 +367,8 @@ export function createSlackMonitorContext(params: { reactionMode: params.reactionMode, reactionAllowlist: params.reactionAllowlist, replyToMode: params.replyToMode, + threadHistoryScope: params.threadHistoryScope, + threadInheritParent: params.threadInheritParent, slashCommand: params.slashCommand, textLimit: params.textLimit, ackReactionScope: params.ackReactionScope, diff --git a/src/slack/monitor/message-handler/prepare.ts b/src/slack/monitor/message-handler/prepare.ts index 3253851ea..d07e1c41a 100644 --- a/src/slack/monitor/message-handler/prepare.ts +++ b/src/slack/monitor/message-handler/prepare.ts @@ -263,7 +263,6 @@ export async function prepareSlackMessage(params: { : null; const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`; - const historyKey = message.channel; const historyEntry = isRoomish && ctx.historyLimit > 0 ? { @@ -291,9 +290,11 @@ export async function prepareSlackMessage(params: { const threadKeys = resolveThreadSessionKeys({ baseSessionKey, threadId: isThreadReply ? threadTs : undefined, - parentSessionKey: isThreadReply ? baseSessionKey : undefined, + parentSessionKey: isThreadReply && ctx.threadInheritParent ? baseSessionKey : undefined, }); const sessionKey = threadKeys.sessionKey; + const historyKey = + isThreadReply && ctx.threadHistoryScope === "thread" ? sessionKey : message.channel; enqueueSystemEvent(`${inboundLabel}: ${preview}`, { sessionKey, contextKey: `slack:message:${message.channel}:${message.ts ?? "unknown"}`, diff --git a/src/slack/monitor/provider.ts b/src/slack/monitor/provider.ts index 53a67f0f4..0e93d8269 100644 --- a/src/slack/monitor/provider.ts +++ b/src/slack/monitor/provider.ts @@ -74,6 +74,8 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { const reactionMode = slackCfg.reactionNotifications ?? "own"; const reactionAllowlist = slackCfg.reactionAllowlist ?? []; const replyToMode = slackCfg.replyToMode ?? "off"; + const threadHistoryScope = slackCfg.thread?.historyScope ?? "thread"; + const threadInheritParent = slackCfg.thread?.inheritParent ?? false; const slashCommand = resolveSlackSlashCommandConfig(opts.slashCommand ?? slackCfg.slashCommand); const textLimit = resolveTextChunkLimit(cfg, "slack", account.accountId); const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions"; @@ -129,6 +131,8 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { reactionMode, reactionAllowlist, replyToMode, + threadHistoryScope, + threadInheritParent, slashCommand, textLimit, ackReactionScope,