Skip to content

feat(channels): run Slack, Discord and Telegram on the AgentController - #54

Closed
hamchowderr wants to merge 2 commits into
mainfrom
claude/project-thread-4iq90v
Closed

hamchowderr wants to merge 2 commits into
mainfrom
claude/project-thread-4iq90v

Conversation

@hamchowderr

Copy link
Copy Markdown
Owner

Requested by Chowderr · project thread

Before: Slack, Discord and Telegram called agent.generate() directly. Any action that needed approval (every Zapier write) stalled with no way to answer it, and the reply came back empty. Runs were capped at 5 steps, so a normal Zapier action couldn't finish. There were no schedules or subagents. The Discord bot went deaf 24 hours after each deploy. Typing /link broke the DM it was typed in, because the old thread still belonged to the pre-link user and Mastra refuses that mismatch.

After: all three run on the Foreman AgentController, the same engine as the web chat. Actions that need approval post Approve / Deny buttons in the chat, and Zapier writes always ask. Schedules, subagents and plan mode work. Memory is shared with the web chat. The Discord Gateway reconnects on its own. /link moves your earlier channel conversations onto your web account.

How

  • src/channels/controller-channels.ts builds the channels config for createForemanAgentController, including only the platforms that have credentials:
    • resolveResourceId makes the sender's Foreman user own each channel thread.
    • resolveSession creates one Session per channel thread, scoped channel:<threadId> so it never reuses or re-points the user's web session. It grants the same auto-allowed tools as web and refuses anyone who isn't the thread's owner, posting a note in the chat when it does.
    • Every message, slash command and approval click stamps userId and workspaceId on the RequestContext before dispatch.
    • Chat SDK state now uses Mastra's Postgres state adapter (core's default).
  • src/channels/relink.ts: after a successful /link, moves the threads of the sender's previous owner via updateThreadResourceId. For a channel-only placeholder it moves every thread on that platform. For a real account it moves only the chat the code was typed in.
  • redeemChannelLinkCode now rejects codes made for a different app and claims codes atomically.
  • Webhooks are Mastra-generated at /api/agent-controllers/foreman-agent-controller/channels/<platform>/webhook. The old /slack|/discord|/telegram/webhook paths forward there, so registered URLs keep working.
  • webhook-server.ts and inbox-worker-server.ts call skipControllerChannels(), so only the main server hosts the channels. Two hosts would answer every message twice.
  • New Slack OAuth installs are loaded into the live adapter, so they work without a restart.
  • Removed the old bots, the FOREMAN_NATIVE_CHANNELS Agent.channels scaffolding (3i9k) and dev:telegram. Telegram now polls by itself under npm run dev.
  • Docs (repo + site) updated. Slack also needs Interactivity pointed at the webhook URL for the buttons to work.

Known limits:

  • Pending approval cards don't survive a restart. Controller sessions are in memory, the same as web chat.
  • Shared Slack/Discord threads are owner-only, which is a fail-closed default for open decision 7jn1.
  • Teams, Google Chat, WhatsApp, GitHub, Linear and iMessage are unchanged and still on :4112.
  • Not run against a live Slack/Discord/Telegram workspace. The behavior is covered by unit tests plus a real AgentController route test.

Checks: tsc clean, npm run lint clean (warnings only, none new), npm test 543 passed / 10 skipped.


Generated by Claude Code

The three channel bots called agent.generate() directly, so they had no way to
show or answer tool approvals (every Zapier write stalled), no schedules or
subagents, a 5-step cap, in-memory Chat SDK state, a Discord Gateway that
stopped after 24 hours, and a /link that broke the DM it was typed in.

They now run on the Foreman AgentController's channels, the same engine as the
web chat:
- one Session per channel thread, owned by the sender's Foreman user (shared
  memory with web), in its own scope so it never touches the web session
- sender's user id + workspace stamped on every run and approval click; only
  the thread's owner can use its session
- Approve/Deny buttons for gated tools; same auto-allowed tools as web
- Postgres-backed Chat SDK state; Discord Gateway auto-reconnects
- /link moves earlier channel threads onto the linked account; link codes are
  now tied to their app and claimed atomically
- old /slack|/discord|/telegram/webhook paths forward to the new routes; the
  :4112 server and inbox worker no longer host these three
- new Slack OAuth installs load into the live adapter

Removes the old bots, the FOREMAN_NATIVE_CHANNELS Agent.channels scaffolding
and dev:telegram (polling now starts with the agent server).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3R2sPwBzA2bpj3HT2BDv2
@vercel

vercel Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
foreman Ready Ready Preview Oct 19, 2026 8:21am UTC

Request Review

@hamchowderr hamchowderr self-assigned this Oct 19, 2026
@hamchowderr
hamchowderr marked this pull request as ready for review October 19, 2026 08:12
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T08:19:20.655751Z 2add2ca Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2add2ca7d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

toolDisplay "text" made core treat Discord as unable to render approval
buttons, so it auto-resumed gated tools and Zapier writes from Discord ran
without approval. Use core's default "cards" display, and test that no
platform is configured with a display that drops the buttons.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3R2sPwBzA2bpj3HT2BDv2
@hamchowderr

Copy link
Copy Markdown
Owner Author

This PR has been split into three stacked PRs, one concern each, to be merged in this order:

Once all three are merged, the code is identical to this PR's last green commit (869e0c2). I'm closing this PR in favour of them.


Generated by Claude Code

This branch was successfully deployed

1 active deployment
Preview — 869e0c25 Deployed Oct 19, 2026 by vercel[bot]
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.

2 participants