Skip to content

feat: attach Slack messages and threads as chat context - #134

Merged
adampeterhiggins merged 1 commit into
mainfrom
ah/slack-link-attachments
Oct 1, 2026
Merged

adampeterhiggins merged 1 commit into
mainfrom
ah/slack-link-attachments

Conversation

@adampeterhiggins

Copy link
Copy Markdown
Owner

There was no way to give an agent a Slack discussion without copying and pasting it. A Slack message can now be attached the same way a Linear issue or pull request is:

  • Paste or type a permalink into the web or desktop composer. It becomes a #channel · Author chip once the thread loads. A reply, or a message with replies, brings its whole thread, with the linked message marked. Channel links stay as links. The first time a link can't be read because Slack isn't connected, a notice explains why.
  • Paperclip → Slack message, or Attach Slack message in the command palette, opens a picker that uses Slack's search syntax (in:#eng from:@priya). Right-click a result and choose Attach only this message to leave out the rest of its thread.
  • # menu: a Slack tab beside Pull requests, Linear, GitHub issues and Repositories. It appears once Slack is connected and only searches while it's visible.
  • Mobile: chips render, the context sheet shows the snapshot with Open in Slack, and the attach menu has a Slack picker sheet. Mobile never turns pasted links into chips, so it has no paste case.

How it works

  • Server (apps/server/src/slack/)
    • Reads Slack with the user's own token (read-only user scopes): search.messages, conversations.replies/info and users.info.
    • Saves the thread as capped markdown when it is attached. That markdown always keeps the first message and the linked one, fills the rest newest first, and resolves <@U…> mentions and channel links. Files are listed by name, not downloaded.
    • The snapshot is a new slack-thread context record, and every provider gets it in the prompt like Linear and GitHub issues.
  • Auth (SlackAuth.ts) works like LinearAuth.
    • It uses PKCE with no client secret, which Slack has supported since March 2026.
    • The redirect lands on a fixed loopback port at http://localhost:47832/callback. Slack only treats localhost as a desktop redirect, not 127.0.0.1.
    • Remote browsers paste the redirect URL back.
    • Tokens without expires_in are kept as non-expiring; rotating tokens are refreshed.
  • No built-in Slack app. Slack limits apps distributed to other workspaces without Marketplace approval to 1 thread read a minute, 15 messages each, which breaks thread attachments. Apps used only in the workspace that made them keep normal limits. So Settings → Integrations → Slack has Copy manifest: read scopes, the redirect, and pkce_enabled. The user creates the app at api.slack.com/apps and pastes its client ID. The server keeps that client ID across disconnects, and T3CODE_SLACK_CLIENT_ID can supply one.

Docs: new docs/user/slack.md, plus updates to composer.md and fork-differences.md.

Testing

  • New tests: OAuth helpers, auth (no client ID configured, pasted-redirect login, disconnect keeps the client ID, refresh), the Web API wrapper (labels, thread_ts taken from permalinks, thread vs. single message, a reply link without thread_ts, not-found, revoked), markdown rendering, permalink parsing, the context record round-trip, and the prompt payload. All pass.
  • Typecheck passes for server, web, mobile, contracts, client-runtime and shared.
  • Not yet run against a real Slack workspace or in a client. Screenshots of the chip, picker and settings are still to come.
  • One server.test.ts case ("flushes more than one tool chunk before the synchronization marker") fails the same way without this change.

🤖 Generated with Claude Code

Pasting a Slack message link, picking one from the paperclip menu or the
command palette, or choosing one in the composer's `#` menu now attaches
the message's thread as a chip. It works the same way as Linear issues.
The server reads the thread with the user's own Slack token and saves it
as capped markdown when it is attached. That markdown keeps the first
message and the linked one, fills the rest newest first, and resolves
mentions to names. The snapshot is inlined into the prompt for every
provider. Right-clicking a result in the picker attaches the message on
its own.

Settings > Integrations > Slack connects the account with PKCE OAuth. The
redirect goes to a fixed localhost loopback port, and remote browsers can
paste the redirect URL back. There is no built-in Slack app. Slack lets a
distributed app without Marketplace approval read only 15 thread messages
a minute, so each workspace makes its own app from a manifest the setting
copies and enters its client ID. Mobile renders the chips and has a
picker sheet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant