Commit Graph

448 Commits

Author SHA1 Message Date
Peter Steinberger
fb5d8a9cd1 perf(slack): memoize allow-from and mention paths 2026-03-02 20:19:10 +00:00
Peter Steinberger
9d30159fcd refactor: dedupe channel and gateway surfaces 2026-03-02 19:57:33 +00:00
Peter Steinberger
d979eeda9f perf(runtime): reduce slack prep and qmd cache-key overhead 2026-03-02 19:48:02 +00:00
Peter Steinberger
0956b599e1 fix: harden slack debounce key routing and ordering (#31951) (thanks @scoootscooob) 2026-03-02 19:18:25 +00:00
scoootscooob
d4b20f5295 fix(slack): scope debounce key by message timestamp to prevent cross-thread collisions
Top-level channel messages from the same sender shared a bare channel
debounce key, causing concurrent messages in different threads to merge
into a single reply on the wrong thread. Now the debounce key includes
the message timestamp for top-level messages, matching how the downstream
session layer already scopes by canonicalThreadId.

Extracted buildSlackDebounceKey() for testability.

Closes #31935

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:18:25 +00:00
Peter Steinberger
83ec545bed test(perf): trim repeated setup in cron memory and config suites 2026-03-02 19:16:46 +00:00
stone-jin
2a98fd3d0b fix(slack): guard against undefined text in includes calls during mention handling 2026-03-02 19:00:08 +00:00
Maho
d21cf44452 fix(slack): remove message.channels/message.groups handlers that crash Bolt 4.6 (#32033)
* fix(slack): remove message.channels/message.groups handlers that crash Bolt 4.6

Bolt 4.6 rejects app.event() calls with event names starting with
"message." (e.g. "message.channels", "message.groups"), throwing
AppInitializationError on startup. These handlers were added in #31701
based on the incorrect assumption that Slack dispatches typed event
names to Bolt. In reality, Slack always delivers events with
type:"message" regardless of the Event Subscription name; the
channel_type field distinguishes the source.

The generic app.event("message") handler already receives all channel,
group, IM, and MPIM messages. The additional typed handlers were
unreachable even if Bolt allowed them, since no event payload ever
carries type:"message.channels".

This preserves the handleIncomingMessageEvent refactor from #31701
(extracting the handler into a named function) while removing only
the broken registrations.

Fixes the Slack provider crash loop affecting all accounts on
@slack/bolt >= 4.6.0.

Closes #31674 (original issue was not caused by missing handlers)

* fix: document Slack Bolt 4.6 startup handler fix (#32033) (thanks @mahopan)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-03-02 18:32:42 +00:00
Peter Steinberger
c973b053a5 refactor(net): unify proxy env checks and guarded fetch modes 2026-03-02 16:24:26 +00:00
Peter Steinberger
17c434f2f3 refactor: split browser context/actions and unify CDP timeout policy 2026-03-02 16:02:39 +00:00
Peter Steinberger
345abf0b20 fix: preserve dns pinning for strict web SSRF fetches 2026-03-02 15:54:46 +00:00
Peter Steinberger
d86c1a67e0 fix(slack): correct typed message event overloads (#31701) 2026-03-02 15:47:21 +00:00
Peter Steinberger
44c50d9a73 fix(types): tighten shared helper typing contracts 2026-03-02 15:21:19 +00:00
Peter Steinberger
ed21b63bb8 refactor(plugin-sdk): share auth, routing, and stream/account helpers 2026-03-02 15:21:19 +00:00
Tak Hoffman
1fe0f848df fix(slack): type message.channels/group handlers (#31758) 2026-03-02 08:09:49 -06:00
liuxiaopai-ai
06306501ab Slack: register typed channel/group message event handlers 2026-03-02 13:32:54 +00:00
Vincent Koc
29c3ce9454 [AI-assisted] test: fix typing and test fixture issues (#31444)
* test: fix typing and test fixture issues

* Fix type-test harness issues from session routing and mock typing

* Add routing regression test for session.mainKey precedence
2026-03-02 00:41:21 -08:00
Gustavo Madeira Santana
1443bb9a84 chore(tsgo/lint): fix CI errors 2026-03-02 03:03:11 -05:00
Peter Steinberger
45888276a3 test(integration): dedupe messaging, secrets, and plugin test suites 2026-03-02 07:13:11 +00:00
Peter Steinberger
40fda40aa7 fix(slack): scope download-file to channel and thread context 2026-03-02 02:23:22 +00:00
Peter Steinberger
8e48520d74 fix(channels): align command-body parsing sources 2026-03-01 23:11:48 +00:00
Agent
bc9f357ad7 test: fix fetch mock typing casts 2026-03-01 22:44:28 +00:00
Onur
18033d3962 Cron+Slack: fix cooldown omission and cache cap enforcement 2026-03-01 20:39:24 +01:00
pablohrcarvalho
11d34700c0 fix(slack): use thread-level sessions for channels to prevent context mixing (#10686)
* fix(slack): use thread-level sessions for channels to prevent context mixing

All messages in a Slack channel share a single session, causing context from
different threads to mix together. When users have multiple conversations in
different threads of the same channel, the agent sees combined context from
all threads, leading to confused responses.

Session key was: `slack:channel:${channelId}` (no thread identifier)

1. **Thread-level session keys**: Each message in channels/groups now gets
   its own session based on thread_ts:
   - Thread replies: use the parent thread's ts
   - New messages: use the message's own ts (becomes thread root)
   - DMs: unchanged (no thread-level sessions needed)

   New session key format: `slack:channel:${channelId}🧵${threadTs}`

2. **Increased thread cache TTL**: Changed from 60 seconds to 6 hours.
   Users often pause conversations, and the short TTL caused unnecessary
   API calls and thread resolution failures.

3. **Increased cache size**: Changed from 500 to 10,000 entries to support
   busy workspaces with many active threads.

1. Create two threads in the same Slack channel
2. In Thread A: tell the bot your name is "Alice" and ask about "billing"
3. In Thread B: tell the bot your name is "Bob" and ask about "API"
4. Reply in Thread A and ask "what's my name?" - should say "Alice"
5. Check sessions: each thread should have a unique session key with 🧵 suffix

Fixes context bleed issues related to #758

* fix(slack): also update resolveSlackSystemEventSessionKey for thread-level sessions

The context.ts file has a separate function for resolving session keys for
system events (reactions, file uploads, etc.). This also needs to support
thread-level sessions to ensure all Slack events route to the correct
thread-specific session.

Added threadTs and messageTs parameters to resolveSlackSystemEventSessionKey
and updated the implementation to use thread-level keys for channels/groups.

* fix(slack): preserve DM thread sessions for thread replies

The previous change broke thread-level sessions for DMs that have threads.
DMs with parent_user_id should still get thread-level sessions.

- For channels/groups: always use thread-level sessions
- For DMs: use thread-level sessions only when isThreadReply is true

* fix(slack): use thread-level sessionKey for previousTimestamp

Fixes the bug where previousTimestamp was read from the base channel
session key (route.sessionKey) instead of the resolved thread-level
sessionKey. This caused the elapsed-time calculation in the inbound
envelope to always pull from the channel session rather than the
thread session.

Also adds regression tests for the thread-level session key behavior.

Co-authored-by: Tony Dehnke <tdehnke@gmail.com>

* fix(slack): narrow #10686 to surgical thread-session patch

* test(slack): satisfy context/account typing in thread-session tests

* docs(changelog): record surgical slack thread-session fix

---------

Co-authored-by: Pablo Carvalho <pablo@telnyx.com>
Co-authored-by: Tony Dehnke <tdehnke@gmail.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 12:04:57 -06:00
msvechla
2c5b898eea feat(slack): add download-file action for on-demand file attachment access (#24723)
* feat(slack): add download-file action for on-demand file attachment access

Adds a new `download-file` message tool action that allows the agent to
download Slack file attachments by file ID on demand. This is a prerequisite
for accessing images posted in thread history, where file attachments are
not automatically resolved.

Changes:
- Add `files` field to `SlackMessageSummary` type so file IDs are
  visible in message read results
- Add `downloadSlackFile()` to fetch a file by ID via `files.info`
  and resolve it through the existing `resolveSlackMedia()` pipeline
- Register `download-file` in `CHANNEL_MESSAGE_ACTION_NAMES`,
  `MESSAGE_ACTION_TARGET_MODE`, and `listSlackMessageActions`
- Add `downloadFile` dispatch case in `handleSlackAction`
- Wire agent-facing `download-file` → internal `downloadFile` in
  `handleSlackMessageAction`

Closes #24681

* style: fix formatting in slack-actions and actions

* test(slack): cover download-file action path

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 11:45:05 -06:00
graysurf
eddaf19478 fix(slack): guard allow-from store resolution in monitor auth (#21967) 2026-03-01 11:42:58 -06:00
Oleksandr Zakotyanskyi
2a409bbba0 fix(slack): replace files.uploadV2 with 3-step upload flow to fix missing_scope error (#17558)
* fix(slack): replace files.uploadV2 with 3-step upload flow

files.uploadV2 from @slack/web-api internally calls the deprecated
files.upload endpoint, which fails with missing_scope even when
files:write is correctly granted in the bot token scopes.

Replace with Slack's recommended 3-step upload flow:
1. files.getUploadURLExternal - get presigned URL + file_id
2. fetch(upload_url) - upload file content
3. files.completeUploadExternal - finalize & share to channel/thread

This preserves all existing behavior including thread replies via
thread_ts and caption via initial_comment.

* fix(slack): harden external upload flow and tests

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 11:37:18 -06:00
Sid
39a45121d9 fix(discord,slack): add SSRF policy for media downloads in proxy environments (#25475)
* fix(discord,slack): add SSRF policy for media downloads in proxy environments

Discord and Slack media downloads (attachments, stickers, forwarded
images) call fetchRemoteMedia without any ssrfPolicy. When running
behind a local transparent proxy (Clash, mihomo, Shadowrocket) in
fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range,
which the SSRF guard blocks.

Add per-channel SSRF policy constants—matching the pattern already
applied to Telegram on main—that allowlist known CDN hostnames and
set allowRfc2544BenchmarkRange: true.

Refs #25355, #25322

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(slack): keep raw-fetch allowlist line anchors stable

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 11:30:10 -06:00
calder-sandy
93ac2b43fb feat(slack): per-thread session isolation for DM auto-threading (#26849)
* feat(slack): create thread sessions for auto-threaded DM messages

When replyToMode="all", every top-level message starts a new Slack thread.
Previously, only subsequent replies in that thread got an isolated session
(via 🧵<threadTs> suffix). The initial message fell back to the base
DM session, mixing context across unrelated conversations.

Now, when replyToMode="all" and a message is not already a thread reply,
the message's own ts is used as the threadId for session key resolution.
This gives the initial message AND all subsequent thread replies the same
isolated session.

This enables per-thread session isolation for Slack DMs — each new message
starts its own thread and session, keeping conversations separate.

* Slack: fix auto-thread session key mode check and add changelog

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 11:24:45 -06:00
Jin Kim
746688ddc9 Slack: redact and cap interaction system events (#28982) 2026-03-01 11:24:43 -06:00
tumf
e0571399ac fix(slack): reject HTML responses when downloading media (#4665)
* fix(slack): reject HTML responses when downloading media

Slack sometimes returns HTML login pages instead of binary media when
authentication fails or URLs expire. This change detects HTML responses
by checking content-type header and buffer content, then skips to the
next available file URL.

* fix: format import order and add braces to continue statement

* chore: format Slack media tests

* chore: apply formatter to Slack media tests

* fix(slack): merge auth-header forwarding and html media guard

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 11:20:25 -06:00
Glucksberg
6dbbc58a8d fix(slack): use SLACK_USER_TOKEN when connecting to Slack (#28103)
* fix(slack): use SLACK_USER_TOKEN when connecting to Slack (closes #26480)

* test(slack): fix account fixture typing for user token source

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 11:05:35 -06:00
dan bachelder
46da76e267 fix(slack): honor replyToModeByChatType when ThreadLabel exists (#26251)
* fix(slack): honor direct replyToMode when thread label exists

ThreadLabel is a session/conversation label, not a reliable indicator
of an actual Slack thread reply. Using it to force replyToMode="all"
overrides replyToModeByChatType.direct="off" in DMs.

Switch to MessageThreadId which indicates a real thread target is
available, preserving expected behavior: thread replies stay threaded,
normal DMs respect the configured mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Slack: add changelog for threading tool context fix

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 11:02:06 -06:00
Dennis Rankin
a28a4b1b61 feat: detect stale Slack sockets and auto-restart (#30153)
* feat: detect stale Slack sockets and auto-restart

Slack Socket Mode connections can silently stop delivering events while
still appearing connected (health checks pass, WebSocket stays open).
This "half-dead socket" problem causes messages to go unanswered.

This commit adds two layers of protection:

1. **Event liveness tracking**: Every inbound Slack event (messages,
   reactions, member joins/leaves, channel events, pins) now calls
   `setStatus({ lastEventAt, lastInboundAt })` to update the channel
   account snapshot with the timestamp of the last received event.

2. **Health monitor stale socket detection**: The channel health monitor
   now checks `lastEventAt` against a configurable threshold (default
   30 minutes). If a channel has been running longer than the threshold
   and hasn't received any events in that window, it is flagged as
   unhealthy and automatically restarted — the same way disconnected
   or crashed channels are already handled.

The restart reason is logged as "stale-socket" for observability, and
the existing cooldown/rate-limit logic (3 restarts/hour max) prevents
restart storms.

* Slack: gate liveness tracking to accepted events

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 10:58:21 -06:00
lailoo
43ddb41354 fix(slack): extract attachment text for bot messages with empty text (#27616) (#27642)
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini
2026-03-01 10:49:51 -06:00
Miguel Miranda Dias
949faff5ce fix(slack): reconnect socket mode after disconnect (#27232)
* fix(slack): reconnect socket mode after disconnect

* fix(slack): avoid orphaned disconnect waiters on start failure

* docs(changelog): record slack socket reconnect reliability fix

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 10:42:45 -06:00
Luis Conde
bd78a74298 feat(slack): track thread participation for auto-reply without @mention (#29165)
* feat(slack): track thread participation for auto-reply without @mention

* fix(slack): scope thread participation cache by accountId and capture actual reply thread ts

* fix(slack): capture reply thread ts from all delivery paths and only after success

* Slack: add changelog for thread participation cache behavior

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 10:42:12 -06:00
Peter Machona
dfbdab5a29 fix(slack): map legacy streaming=false to off (openclaw#26020) thanks @chilu18
Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: chilu18 <7957943+chilu18@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 10:21:25 -06:00
dan bachelder
9ae94390b9 fix(slack): resolve replyToMode per-message using chat type (#24717)
* fix(slack): resolve replyToMode per-message using chat type

The Slack monitor resolved replyToMode once at startup from the
top-level config, ignoring replyToModeByChatType overrides. This caused
DM replies to be threaded even when replyToModeByChatType.direct was
set to "off".

Now the inbound message handler calls resolveSlackReplyToMode(account,
chatType) per-message — the same function already used by the outbound
dock and tool threading context — so per-chat-type overrides take
effect on the inbound path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Slack: add changelog for per-message replyToMode resolution

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 10:21:01 -06:00
Mark L
265b22c401 fix(slack): skip monitor startup for disabled accounts [AI-assisted] (openclaw#30592) thanks @liuxiaopai-ai
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 10:19:50 -06:00
Colin Johnson
0f36ee5a2e Slack: harden slash and interactions ingress checks (openclaw#29091) thanks @Solvely-Colin
Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 09:40:57 -06:00
Glucksberg
3aad6c8bdb fix(slack): guard Socket Mode listeners access during startup (openclaw#28702) thanks @Glucksberg
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Glucksberg <80581902+Glucksberg@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 09:29:18 -06:00
HouRong
b3f60a68a0 fix(slack): thread agent identity through channel reply path (openclaw#27134) thanks @hou-rong
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: hou-rong <8758438+hou-rong@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 09:25:32 -06:00
Peter Steinberger
8bc80fad47 fix(slack): land #29032 /agentstatus alias from @maloqab
Land contributor PR #29032 by @maloqab with Slack native alias docs, integration tests, and changelog entry.

Co-authored-by: maloqab <mitebaloqab@gmail.com>
2026-02-27 19:09:38 +00:00
Peter Steinberger
564be6b402 refactor(channels): unify dm pairing policy flows 2026-02-26 22:36:20 +01:00
Peter Steinberger
bce643a0bd refactor(security): enforce account-scoped pairing APIs 2026-02-26 21:57:52 +01:00
Peter Steinberger
d6eefe2e75 style: format auth boundary updates 2026-02-26 18:50:47 +01:00
Peter Steinberger
64de4b6d6a fix: enforce explicit group auth boundaries across channels 2026-02-26 18:49:16 +01:00
Peter Steinberger
cd80c7e7ff refactor: unify dm policy store reads and reason codes 2026-02-26 17:47:57 +01:00
SidQin-cyber
eb9a968336 fix(slack): suppress NO_REPLY before Slack API call
Guard sendMessageSlack against NO_REPLY tokens reaching the Slack API,
which caused truncated push notifications before the reply filter could
intercept them.

Made-with: Cursor
(cherry picked from commit fab9b5203965749e0fc72b58c4f42fbb3a1a18b8)
2026-02-26 13:40:58 +00:00