mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-06 14:02:36 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Slack messages can create tasks and continue existing tasks. > - A reply in a subscribed thread can start agent work without a new mention. > - Some users want to control each reply with an explicit bot mention. > - This pull request adds a Require at-mention setting to Slack app settings. > - The setting checks new messages and queued messages before they start work. ## Linked Issues or Issue Description **What existing behavior does this improve?** Slack app settings and the rules that admit inbound messages. **Subsystem affected** Server, UI, database, and shared API contracts. **Current behavior** After the bot joins a thread, an ordinary reply can wake the agent. Direct messages do not need a bot mention. **Proposed behavior** Add Require at-mention below Allow direct messages. When enabled, each channel message, thread reply, and direct message must mention the bot. Direct messages must also be allowed. **Reason and benefit** Users can prevent ordinary conversation from starting agent work. **Breaking changes** New Slack connections require a bot mention by default. Existing connections keep their saved setting. Work that has already started is not cancelled. **Additional context** A search for existing at-mention PR titles found no duplicate. ## What Changed - Add the endpoint field, schema validation, API response, and UI toggle. New Slack connections enable the setting. - Add an idempotent database migration and generated metadata. Renumber it to 0321 after current master. - Check the current setting at inbound admission, task mutation, and the first heartbeat queue claim. - Keep Slack tool access and recovery for already-running work when the setting changes. - Cancel queued batches that contain an unmentioned input, including coalesced messages. - Dispatch promoted follow-ups after the scheduler releases its agent start lock. Preserve these checks in the new queue and run-control modules. - Filter unmentioned messages without retaining their content. - Keep provider-message deduplication and existing access checks. - Add thread, DM, duplicate-delivery, queued-message, and new-connection default regression tests. - Document the setting and its scope. ## Verification - Previous head `16791bb7`: typecheck, build, all server shards, Runner checks, and E2E passed. Two chat shards failed in three fixtures that used unmentioned Slack messages. - Update setup follow-ups to mention the Slack bot. Disable the mention rule explicitly in the callback ordering test, which tests ordinary subscribed replies. - Previous local checks passed: six Slack integration cases, all 46 durable chat scheduler tests, all 24 queue/run-control tests, migration safety, and UI token gates. - Greptile reviewed `16791bb7` with 5/5. All review threads are resolved. - Head `53163c20f`: all five focused integration cases passed. These cover all three prior CI failures and both default-on channel/DM cases. Fresh CI and Greptile are running. - Full local recursive typecheck, build, and repository tests were not repeated for this fixture-only change. CI supplies these checks. - Manual check: create a Slack connection. Require at-mention must be on. An ordinary thread reply or DM must not wake the agent. A bot mention must wake it once. Disable the setting and confirm ordinary replies work again. ## Risks - The migration adds a non-null boolean with a false default. Apply it before the new server runs. - The setting also applies to DMs. The UI help text states this. - Enabling it blocks queued unmentioned work but does not cancel a run that has already started. - Full local verification is limited by the environment constraints above; latest-head CI must pass before merge. ## Model Used OpenAI GPT-6 (Codex agent), with reasoning, tool use, and code execution. The runtime does not expose a more specific model version or context-window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
214 lines
12 KiB
Markdown
214 lines
12 KiB
Markdown
# Slack tools for connected agents
|
|
|
|
A linked person can mention the bot and ask it to read the discussion, summarize
|
|
decisions, and create assigned follow-up tasks. Task creation, assignment,
|
|
approvals, and completion remain normal Paperclip operations. Slack contributes
|
|
provider tools and a bundled skill; it does not introduce another task lifecycle.
|
|
|
|
## Authority and channel access
|
|
|
|
The controller resolves company, endpoint, assigned agent, task, run, and accepted
|
|
linked requester from the admitted Slack event or the current Paperclip task/run
|
|
identity. Slack-origin work remains bound to its originating endpoint. Ordinary
|
|
tasks and routines receive only active Slack connections assigned to that agent,
|
|
and use the accepted responsible user's linked Slack account. They never borrow
|
|
the connection owner's identity or an earlier Slack sender. A routine uses its
|
|
persisted responsible user when it runs, with fresh membership/link checks.
|
|
|
|
An optional endpoint ID selects among the supplied assignments; it cannot grant
|
|
access to another company's or agent's bot. Native tools, CLI calls and queued
|
|
approvals enforce the same binding. Tokens and requester identities stay server-side.
|
|
|
|
Reads require current bot membership and requester access. Full workspace members
|
|
can read public channels where the bot belongs; guests, private channels and Slack
|
|
Connect conversations require verified requester membership. Membership checks
|
|
paginate and fail closed. Other people's bot DMs are never exposed.
|
|
|
|
**Allowed Channels controls responses and writes, not reads.** Invite the bot to
|
|
another shared channel to make it readable. Newly invited channels start enabled;
|
|
a person can disable responses while preserving shared read access.
|
|
Retrieved messages, files, names, topics, canvases and list records are source
|
|
material. Unlinked participants cannot start work, approve actions or grant access.
|
|
|
|
Private-source markers are recorded before returning private content. They restrict
|
|
both explicit tool writes and automatic publications, including uploads. Research
|
|
across private channels in a Slack-origin task must originate in the requester's DM.
|
|
Ordinary-task research is subject to the same private-source publication boundary. Private material can
|
|
be published only in its source channel or that requester's DM. Shared document
|
|
edits after private research fail closed because Slack does not provide a complete
|
|
sharing audience through file metadata; use a message or upload instead.
|
|
|
|
Each call rechecks live authority. Queued writes recheck again before transport.
|
|
Authorization and capability revisions participate in session compatibility and
|
|
signed approvals. Removing a tool profile binding does not get undone by resolving
|
|
a retained session. Disconnecting an identity or disabling a connection revokes
|
|
access for retained runs too.
|
|
|
|
## Tools and scope upgrades
|
|
|
|
The reviewed, strict method/scope/argument matrix is
|
|
[`packages/shared/src/slack-tools.ts`](../../packages/shared/src/slack-tools.ts).
|
|
It is the source for tool descriptors, validation, policy catalog and Storybook.
|
|
There is no arbitrary Slack method executor, user impersonation or workspace-admin
|
|
tool. User search credentials never authorize writes.
|
|
|
|
- Discovery: channels, channel details, members, user identities and emoji.
|
|
- Reading: paginated history, threads, individual messages, source links and linked
|
|
files. Text files up to 256 KiB are available inline. Other files return metadata
|
|
with an explicit limitation; do not claim their contents were read.
|
|
- Search: bounded channel history by text, author and time. File mode matches
|
|
linked filenames/titles, not file contents. Coverage includes page cursors,
|
|
inspected time bounds and omitted matches. Use the returned continuation time
|
|
if matching results were truncated. Thread replies require separate reads.
|
|
- Collaboration: bot messages/replies, edits to its own messages, task-attachment
|
|
uploads (up to 20 MiB), reactions, pins, bookmarks, topics and purposes.
|
|
- Documents: canvas creation, text reading, section lookup and edits; bot-owned
|
|
lists, text records, renaming and approved channel sharing. Slack plan and
|
|
document permissions apply. Lists need a separate approved share before others
|
|
can use them.
|
|
- Governed actions: deleting the bot's messages, deleting bookmarks, creating
|
|
channels, inviting people and granting list access require approval. New channels
|
|
remain disabled for ongoing responses. The bot cannot join an existing channel,
|
|
invite itself or enable a destination.
|
|
|
|
New manifests request collaboration scopes. Existing bots keep their current
|
|
permissions and Settings identifies missing scopes. Add them under **OAuth &
|
|
Permissions → Bot Token Scopes** in Slack and reinstall the app. Scope possession
|
|
alone is not a guarantee: Slack feature availability, destination access and
|
|
Paperclip per-action policy still apply.
|
|
|
|
## Delivery and retry behavior
|
|
|
|
Native and HTTP tool calls pass through the same existing policy/approval gateway.
|
|
Mutations receive durable action receipts. Idempotency keys are scoped to company,
|
|
connection, task and requester, and cannot be reused with different arguments.
|
|
|
|
Ordinary writes rejected by Slack with a definite rate limit can retry with the
|
|
same arguments and key after Retry-After, during the same authorized run. Policy
|
|
and current access are checked again. An uncertain transport outcome is never
|
|
blindly resent. `slack_delivery` reconciles posts by their client message ID and
|
|
uploads by their recorded file ID; unresolved operations remain uncertain.
|
|
An identical automatic final response is suppressed after an explicit send, or
|
|
held while that send's delivery remains unresolved. Distinct summaries, progress,
|
|
questions and blockers continue through existing routing.
|
|
|
|
## Continuing a Slack task from Paperclip
|
|
|
|
Human replies entered on a Slack-linked task are attributed to their Paperclip
|
|
author in the original Slack thread. The author must have an active linked Slack
|
|
account for this connection. Delivery rechecks workspace and channel access;
|
|
revoked or changed identities cannot deliver queued messages or agent replies.
|
|
Selected agent responses return to the same thread without echoing internal notes.
|
|
|
|
The explicit channel composer also requests agent work through a durable outbox.
|
|
It respects task pause holds, unresolved blockers, and closed isolated workspaces.
|
|
Restore cancelled tasks or reopen closed workspaces in the ordinary Paperclip task
|
|
flow first. The outbox checks these guards again before dispatching queued work.
|
|
|
|
## Optional personal search authorization
|
|
|
|
Connection managers may configure the Slack application's Client ID and Client
|
|
Secret in Access. Register the displayed HTTPS callback URL and user scopes
|
|
`search:read.public`, `search:read.private` and `search:read.files` in Slack.
|
|
A linked person can then connect or disconnect their own search grant. State,
|
|
secret bindings, refresh and grants reuse the existing connection infrastructure.
|
|
Grants bind company, endpoint, Slack workspace, linked Slack identity and app
|
|
configuration revision. Concurrent disconnect invalidates in-flight callbacks.
|
|
No DM-search scopes are requested.
|
|
|
|
**Native RTS search is not enabled on current runtimes.** Slack's
|
|
[Real-time Search API](https://docs.slack.dev/apis/web-api/real-time-search-api/)
|
|
requires transient handling of retrieved data. Current native and CLI/sandbox
|
|
runtimes retain tool transcripts. The provider implementation and search-only
|
|
OAuth flow are present, but RTS results must not enter the ordinary gateway until
|
|
an entire runtime/provider path is qualified: no raw results in logs, model
|
|
transcripts, replay snapshots, indexes or artifacts. Recovery must re-fetch.
|
|
Access displays this limitation even when a personal grant is connected.
|
|
|
|
The transient provider implementation uses the verified event's short-lived,
|
|
memory-only action token for bot searches, and optional personal OAuth for private
|
|
search. It checks every returned workspace/channel/user/date and verifies file
|
|
shares with the bot's credentials. Model-authored modifiers are not an access
|
|
boundary. Slack limits RTS to internal or directory-published apps and applies
|
|
separate search and plan limits. Bounded history remains available without OAuth.
|
|
|
|
## Native and CLI execution
|
|
|
|
The connector runtime contributes tools to verified Slack conversations and the
|
|
assigned agent's Paperclip tasks and routines, when their responsible user has a
|
|
current link to that bot's workspace. The bundled
|
|
[`skills/slack/SKILL.md`](../../skills/slack/SKILL.md) and generated adjacent
|
|
`TOOLS.json` provide the equivalent HTTP interface for CLI/sandbox adapters:
|
|
|
|
```text
|
|
POST /api/companies/:companyId/slack/tasks/:issueId/tools
|
|
Authorization: Bearer <agent run key>
|
|
X-Paperclip-Run-Id: <run ID>
|
|
|
|
{"endpointId":"<assigned endpoint ID>","tool":"slack_history","arguments":{"channel":"C123","limit":50}}
|
|
```
|
|
|
|
Company/task path parameters are checked against the authenticated run and
|
|
admitted request. Bot and OAuth secrets stay server-side. Other providers and the
|
|
separate Slack MCP connection retain their existing behavior.
|
|
|
|
## Verification
|
|
|
|
Automated coverage includes admitted-request binding and recovery, wrong company,
|
|
agent and task, revocation, approval after a run completes, missing scopes,
|
|
membership pagination, private publication boundaries, rate-limit retries,
|
|
uncertain delivery, OAuth refresh and disconnect races. Native RTS provider tests
|
|
use fixtures; they do not qualify production transcript handling.
|
|
|
|
Storybook: **Connections → Slack → Task tools** includes capabilities, permission
|
|
upgrades, OAuth configuration, connect and connected states. Live staging evidence
|
|
and outstanding limitations are tracked in the dated implementation checklist.
|
|
|
|
## Channel invitations and the first mention
|
|
|
|
Newly discovered Slack channels that people invite the bot to start enabled in
|
|
Allowed Channels. This applies whether provider inventory, the bot's membership event, or
|
|
the first verified message arrives first. Existing enabled/disabled choices are
|
|
preserved across inventory refresh, repeat invitations, and reconnects. An
|
|
explicitly disabled channel must be re-enabled in Paperclip; a mention does not
|
|
undo that choice. Provider removal and archive state still prevent delivery, and
|
|
requesters still need the connection's usual identity and execution permissions.
|
|
Channels created by the bot stay disabled until enabled by a person.
|
|
|
|
Slack sends the message that prompted an accepted invitation as an
|
|
[`app_mention` event](https://docs.slack.dev/reference/events/app_mention/).
|
|
Process that original event through normal admission and deduplication; do not
|
|
scan history and turn arbitrary old mentions into new work. Historical messages
|
|
already filtered under the old disabled default are not automatically replayed.
|
|
|
|
## Paperclip messages and scheduled delivery
|
|
|
|
Authenticated human messages submitted on a Slack-linked task are also queued to
|
|
its original thread, labeled with the author's display name and “via Paperclip.”
|
|
The normal task wakeup performs the work. A durable mirror receipt establishes the
|
|
return path for its selected final response; intermediate agent bookkeeping stays
|
|
internal. Incoming Slack messages are not mirrored back, and retries use stable
|
|
publication identities. The explicit Board-send composer also starts work using an
|
|
idempotent wakeup outbox. Provider outages leave durable delivery state for retry.
|
|
|
|
The agent can use `slack_open_dm` to open its DM with the current task's linked
|
|
responsible user, followed by `slack_post_message`. This needs `im:write`; existing
|
|
apps without that scope require reinstalling with the updated manifest. Other
|
|
people's bot DMs remain inaccessible. Scheduling uses ordinary Paperclip routines,
|
|
not a Slack-specific timer. Routine results are sent explicitly through the tool;
|
|
ordinary task finals are not automatically broadcast to Slack.
|
|
|
|
## Require an at-mention for inbound messages
|
|
|
|
Slack app settings include **Require at-mention** directly below **Allow direct
|
|
messages**. It defaults to on for new Slack connections. Existing connections
|
|
keep their saved setting and thread and DM behavior.
|
|
When enabled, each inbound message must explicitly @mention the connected bot,
|
|
including replies in existing channel threads and direct messages. DMs must also
|
|
be enabled with **Allow direct messages**. Unmentioned messages do not create
|
|
or continue a task. Queued messages are checked again before starting agent work.
|
|
If a queued run combines several messages, an unmentioned input cancels that
|
|
queued run when this setting is enabled. Send a new mention to start it again.
|
|
This setting does not cancel work that has already started or revoke its Slack
|
|
tool access.
|