Skip to content

feat(scripts): add host-side Discord bridge#458

Closed
phanisaimunipalli wants to merge 6 commits into
NVIDIA:mainfrom
phanisaimunipalli:feat/discord-bridge
Closed

feat(scripts): add host-side Discord bridge#458
phanisaimunipalli wants to merge 6 commits into
NVIDIA:mainfrom
phanisaimunipalli:feat/discord-bridge

Conversation

@phanisaimunipalli

@phanisaimunipalli phanisaimunipalli commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a host-side Discord bridge for NemoClaw, following the same architecture as the existing Telegram bridge.

A host-side bridge is required because the sandbox proxy does not support CONNECT tunneling for WebSockets, which the Discord gateway (gateway.discord.gg) requires. This is the same constraint described in #409.

Changes

  • scripts/discord-bridge.js — Discord bot that forwards channel messages to the OpenClaw agent via SSH and posts responses back. Uses discord.js v14.
  • scripts/start-services.sh — Updated to start/stop the Discord bridge alongside the Telegram bridge when DISCORD_BOT_TOKEN is set.
  • docs/deployment/set-up-discord-bridge.md — Step-by-step setup guide covering bot creation, intents, guild invite, access control, and session reset.
  • docs/index.md — Nav entry added under Deployment.
  • package.json — Added discord.js ^14.16.3 dependency.

Features

  • Per-channel session continuity via channel-scoped session IDs (dc-ch-<channelId>)
  • Message chunking at Discord's 2000-character limit
  • NEMOCLAW_MODEL and SANDBOX_NAME env var support (consistent with Telegram bridge)
  • ALLOWED_GUILD_IDS for server-level access control
  • !reset command to clear a channel's conversation session
  • Typing indicator while the agent runs

Testing

export DISCORD_BOT_TOKEN=<your-token>
export NVIDIA_API_KEY=<your-key>
node scripts/discord-bridge.js

Closes #422.

Summary by CodeRabbit

  • New Features

    • Discord bridge: chat relaying with per-channel conversation context, per-channel !reset command, typing indicators, long-message chunking, and optional guild allow-list; supports selecting sandbox and model.
  • Documentation

    • New step-by-step Discord bridge setup guide covering bot creation, intents/permissions, required environment variables, startup/verification, usage, and shutdown.
  • Chores

    • Service management updated to start/stop and report Discord bridge status; added Discord runtime dependency.

@coderabbitai

coderabbitai Bot commented Mar 20, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds a host-side Discord bridge: new Node.js bridge script using discord.js, package dependency, service lifecycle integration to start/stop the bridge, and published deployment documentation describing env vars, per-channel sessions, a !reset command, and operational behavior.

Changes

Cohort / File(s) Summary
Documentation
docs/deployment/set-up-discord-bridge.md, docs/index.md
New deployment guide for the Discord bridge and added Deployment toctree entry.
Dependency Manifest
package.json
Added runtime dependency discord.js@^14.16.3.
Discord Bridge Script
scripts/discord-bridge.js
New executable implementing Discord→NemoClaw bridge: env var validation, discord.js client and intents, per-channel session IDs and !reset, optional ALLOWED_GUILD_IDS, per-channel request queueing, typing indicator handling, obtains sandbox SSH config via openshell, writes a temp SSH config, spawns ssh to run nemoclaw-start openclaw agent --session-id dc-<id> with exported env, captures stdout/stderr, parses response, chunks replies (2000 chars), and reports errors. Review SSH temp-file lifecycle, env propagation, queueing/typing edge cases, and guild allow-list enforcement.
Service Lifecycle
scripts/start-services.sh
Integrated discord-bridge into start/status/stop flows; conditional launch when DISCORD_BOT_TOKEN is set; status reporter/banners updated; startup gating warns only when both Telegram and Discord tokens are missing.

Sequence Diagram

sequenceDiagram
    participant User as Discord User
    participant DiscordAPI as Discord API
    participant Bridge as Discord Bridge
    participant OpenShell as OpenShell CLI
    participant SSH as SSH
    participant Sandbox as NemoClaw Sandbox
    participant Agent as OpenClaw Agent

    User->>DiscordAPI: Send message to channel
    DiscordAPI->>Bridge: messageCreate event
    Bridge->>Bridge: validate guild / handle !reset / ensure sessionId
    Bridge->>DiscordAPI: start typing indicator
    Bridge->>OpenShell: request sandbox ssh-config
    OpenShell-->>Bridge: ssh config
    Bridge->>Bridge: write temp SSH config
    Bridge->>SSH: ssh -F temp.conf run "nemoclaw-start openclaw agent" with env + --session-id
    SSH->>Sandbox: execute command
    Sandbox->>Agent: deliver message (model, API key, session)
    Agent-->>Sandbox: return response stdout
    Sandbox-->>SSH: return stdout/stderr
    SSH-->>Bridge: collect output
    Bridge->>Bridge: parse & chunk response
    Bridge->>DiscordAPI: send response chunks
    DiscordAPI-->>User: deliver messages
    Bridge->>DiscordAPI: clear typing indicator
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hop from channel to sandbox with a cheerful ping,
I carry tokens, sessions, and a tiny ssh string.
Per-channel tales I keep, reset with a tap,
I type, I queue, I fetch replies — then bring them back.
Hoppity answers delivered, then I curl for a nap.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(scripts): add host-side Discord bridge' accurately and concisely summarizes the main change—adding a Discord bridge script to the codebase.
Linked Issues check ✅ Passed All coding objectives from issue #422 are met: host-side Discord bridge implemented, per-channel session IDs added, lifecycle integrated with start-services.sh, deployment documentation provided, and feature parity with Telegram bridge achieved.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #422: Discord bridge implementation, service integration, documentation, and dependency addition. No unrelated changes are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
docs/deployment/set-up-discord-bridge.md (3)

26-50: Add intro sentences under these H2 headings.

Prerequisites, Create a Discord Application and Bot, and Invite the Bot to Your Server all jump straight into lists. The docs style guide wants each H2/H3 section to start with a short introductory sentence.

As per coding guidelines, "Sections use H2 and H3, each starting with an introductory sentence."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` around lines 26 - 50, Add a
one-sentence introductory line under each H2 heading "Prerequisites", "Create a
Discord Application and Bot", and "Invite the Bot to Your Server" describing
what the section covers (e.g., overview of required items, what creating the
app/bot entails, and what inviting the bot accomplishes) so each section begins
with a short context sentence per the docs style guide; keep the sentences
concise and placed immediately before the existing lists/steps.

121-125: Use Next Steps here.

New docs pages are supposed to end with a Next Steps section, and this H2 should also open with a brief intro sentence before the links.

As per coding guidelines, "A 'Next Steps' section at the bottom links to related pages," and "Sections use H2 and H3, each starting with an introductory sentence."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` around lines 121 - 125, Rename the
H2 titled "Related Topics" to "Next Steps" and add a one-sentence introductory
lead immediately below that H2 (e.g., "Next steps and related documentation to
help you continue setup:") before the existing bullet links; keep the three
existing links unchanged and retain the H2 level formatting so the section
matches the documentation guidelines and the rest of the page structure.

39-39: Replace the em dash in this note.

LLM pattern detected. The dash here is acting as sentence punctuation; use a period instead.

As per coding guidelines, "Excessive em dashes. One per paragraph is fine; multiple per paragraph or em dashes used instead of commas/periods should be flagged."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` at line 39, The sentence "Store it
securely — Discord does not show it again." uses an em dash as sentence
punctuation; replace the em dash with a period and adjust spacing so it reads
"Store it securely. Discord does not show it again." to comply with the
guideline against excessive em dashes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Around line 51-65: The docs only show exporting DISCORD_BOT_TOKEN but omits
NVIDIA_API_KEY which causes `nemoclaw start` to fail; update the "Set the
Environment Variable" section to instruct users to also export NVIDIA_API_KEY
(e.g., export NVIDIA_API_KEY=<your-key>) before running `nemoclaw start`, and
add a brief note mentioning that the Discord bridge depends on NVIDIA_API_KEY so
startup will error if it is missing (references: NVIDIA_API_KEY, the `nemoclaw
start` command and the discord bridge startup).

In `@scripts/discord-bridge.js`:
- Line 156: The current console.log in scripts/discord-bridge.js prints full
chat transcripts (using message.guild.name, message.channel.name,
message.author.username and content); change this to avoid logging raw user
messages by either (a) logging only non-sensitive metadata (e.g., guild id,
channel id, author id/username and message length) or (b) gating full-text logs
behind an explicit debug flag (e.g., process.env.DEBUG_DISCORD === 'true') so
that content is never written to logs by default; update both occurrences that
print message content (the console.log at the message output line and the
similar log at the other noted line) and ensure any debug-path still redacts or
escapes sensitive fields unless the debug flag is set.
- Around line 56-57: Concurrent calls to runAgentInSandbox() reuse the same
dc-ch-<channelId> session and the same /tmp/nemoclaw-dc-ssh-<sessionId>.conf
which can cause one call to delete or overwrite the SSH config while another is
using it; fix by serializing requests per channel or making the config unique
per invocation. Implement a channel-level mutex/queue keyed by channelId around
runAgentInSandbox() usage (or the code that writes/deletes confPath) so only one
request for a given channel runs at a time, or change the confPath generation to
include a unique suffix (e.g., timestamp+PID+random) so each invocation writes a
distinct temp file and cleans up its own file without affecting others; update
all spots that write/remove the SSH config (the code that sets confPath and
calls writeFileSync/unlink) to use the mutex or unique-per-call path
accordingly.
- Line 35: The script's SANDBOX constant uses a different default ("nemoclaw")
than the service launcher; update the SANDBOX fallback in
scripts/discord-bridge.js so it matches the launcher by using "default" instead
of "nemoclaw" (i.e., change the SANDBOX assignment that references
process.env.SANDBOX_NAME to fall back to "default") to ensure both tools target
the same sandbox when SANDBOX_NAME is unset.
- Around line 147-165: The reset path deletes activeSessions but a new sessionId
is always recomputed as `ch-${channelId}` regardless of the map, so reset
doesn't clear downstream history; change the logic so you first check
activeSessions.get(channelId) and reuse that sessionId if present, otherwise
create and set sessionId = `ch-${channelId}` only when absent (use
activeSessions.get(channelId) and activeSessions.set(channelId, sessionId)
around the existing `sessionId` usage), ensuring the !reset branch truly clears
the stored session by removing the map entry and letting the next message create
a fresh entry.

---

Nitpick comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Around line 26-50: Add a one-sentence introductory line under each H2 heading
"Prerequisites", "Create a Discord Application and Bot", and "Invite the Bot to
Your Server" describing what the section covers (e.g., overview of required
items, what creating the app/bot entails, and what inviting the bot
accomplishes) so each section begins with a short context sentence per the docs
style guide; keep the sentences concise and placed immediately before the
existing lists/steps.
- Around line 121-125: Rename the H2 titled "Related Topics" to "Next Steps" and
add a one-sentence introductory lead immediately below that H2 (e.g., "Next
steps and related documentation to help you continue setup:") before the
existing bullet links; keep the three existing links unchanged and retain the H2
level formatting so the section matches the documentation guidelines and the
rest of the page structure.
- Line 39: The sentence "Store it securely — Discord does not show it again."
uses an em dash as sentence punctuation; replace the em dash with a period and
adjust spacing so it reads "Store it securely. Discord does not show it again."
to comply with the guideline against excessive em dashes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d0b12b56-0206-42d3-9905-66be8b7e7114

📥 Commits

Reviewing files that changed from the base of the PR and between dbfd78c and 0a8b989.

📒 Files selected for processing (5)
  • docs/deployment/set-up-discord-bridge.md
  • docs/index.md
  • package.json
  • scripts/discord-bridge.js
  • scripts/start-services.sh

Comment thread docs/deployment/set-up-discord-bridge.md Outdated
Comment thread scripts/discord-bridge.js Outdated
Comment thread scripts/discord-bridge.js Outdated
Comment thread scripts/discord-bridge.js Outdated
Comment thread scripts/discord-bridge.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
scripts/discord-bridge.js (2)

188-200: ⚠️ Potential issue | 🟠 Major

Serialize same-channel requests before reusing sessionId.

Lines 188-197 still let two messages in the same channel call runAgentInSandbox() concurrently against the same sessionId. The unique temp-file path fixed the config-file collision, but replies can still arrive out of order and the shared downstream history can interleave. Add a channel-scoped queue or mutex around the agent run.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/discord-bridge.js` around lines 188 - 200, Concurrent calls to
runAgentInSandbox using the same sessionId can interleave history and cause
out-of-order replies; add channel-scoped serialization so only one
runAgentInSandbox runs per channel at a time (e.g., a Map keyed by channelId
holding a Promise/lock or a simple async queue). Wrap the section that obtains
sessionId and calls runAgentInSandbox/sendChunked (references:
activeSessions.get(channelId), sessionId, runAgentInSandbox, sendChunked,
typingInterval) in that per-channel mutex/queue so subsequent messages for the
same channel await the prior agent run finishing (including clearInterval and
sendChunked) before starting; ensure the typingInterval is still cleared on
completion or error and that errors release the lock so the queue can proceed.

168-188: ⚠️ Potential issue | 🟠 Major

Use a stable per-channel session ID by default.

Line 186 timestamps every first-seen channel, which makes the downstream session process-lifetime-scoped instead of channel-scoped. After a bridge restart, the same Discord channel gets a new dc-... session and loses history even though nobody ran !reset. Keep ch-${channelId} as the default, and mint the timestamped variant only in the reset branch.

💡 Minimal fix
   if (content === "!reset") {
-    activeSessions.delete(channelId);
+    activeSessions.set(channelId, `ch-${channelId}-${Date.now()}`);
     await message.reply("Session reset.");
     return;
   }
@@
-  if (!activeSessions.has(channelId)) {
-    activeSessions.set(channelId, `ch-${channelId}-${Date.now()}`);
-  }
-  const sessionId = activeSessions.get(channelId);
+  const sessionId = activeSessions.get(channelId) ?? `ch-${channelId}`;
+  activeSessions.set(channelId, sessionId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/discord-bridge.js` around lines 168 - 188, The session IDs are
currently created with a timestamp causing sessions to change after restarts;
change the default creation in the session-reuse block (where
activeSessions.set(channelId, `ch-${channelId}-${Date.now()}`) and sessionId is
read) to a stable per-channel id like `ch-${channelId}`, and only create a
timestamped id when the user explicitly resets (modify the "!reset" branch that
currently calls activeSessions.delete(channelId) to instead set a new
timestamped id, e.g., `ch-${channelId}-${Date.now()}`, or delete then
immediately set the timestamped id so downstream history is only lost on
explicit reset).
🧹 Nitpick comments (1)
docs/deployment/set-up-discord-bridge.md (1)

69-69: Replace these colon-ended lead-ins with period-ended sentences.

Lines 69, 84, 102, 112, and 123 are full sentences that end with colons before command blocks. The docs style guide wants sentences to end with periods and reserves colons for lists. As per coding guidelines, "Every sentence must end with a period." and "Colons should only introduce a list."

Also applies to: 84-84, 102-102, 112-112, 123-123

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` at line 69, Replace the
colon-terminated lead-in sentences with period-terminated sentences for each
instance (e.g., change "Start the Discord bridge and other auxiliary services:"
to "Start the Discord bridge and other auxiliary services.") — update the same
style for the other identified strings on lines that currently read like full
sentences ending with a colon (the lead-ins at the same locations as "Start the
Discord bridge and other auxiliary services:") so every sentence ends with a
period and colons are only used for lists.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Around line 104-106: The unlabeled fenced code block containing "!reset"
should either be converted to an inline code span (`!reset`) or have an explicit
language added to the fence (e.g., ```text) to satisfy markdownlint; update the
fenced block around the "!reset" token to use a language identifier like "text"
or collapse it to inline code.
- Around line 57-65: Add documentation for the SANDBOX_NAME and NEMOCLAW_MODEL
environment variables in the "Set the Environment Variables" section: describe
that SANDBOX_NAME selects the target sandbox (and state the default if any) and
that NEMOCLAW_MODEL selects the model to use (and state the default model), show
example export commands for both (e.g., export SANDBOX_NAME=<name> and export
NEMOCLAW_MODEL=<model>), and note that the bridge will read these variables at
startup so operators can target a non-default sandbox or model.

In `@scripts/discord-bridge.js`:
- Around line 145-151: The Client is created without an allowedMentions policy
so bot output can trigger unintended pings; update the new Client instantiation
(the Client constructor where const client = new Client(...)) to include an
allowedMentions option that disables parsing (e.g., allowedMentions: { parse:
[], repliedUser: false }) so user/role/@everyone/@here mentions are not
automatically parsed by default.

---

Duplicate comments:
In `@scripts/discord-bridge.js`:
- Around line 188-200: Concurrent calls to runAgentInSandbox using the same
sessionId can interleave history and cause out-of-order replies; add
channel-scoped serialization so only one runAgentInSandbox runs per channel at a
time (e.g., a Map keyed by channelId holding a Promise/lock or a simple async
queue). Wrap the section that obtains sessionId and calls
runAgentInSandbox/sendChunked (references: activeSessions.get(channelId),
sessionId, runAgentInSandbox, sendChunked, typingInterval) in that per-channel
mutex/queue so subsequent messages for the same channel await the prior agent
run finishing (including clearInterval and sendChunked) before starting; ensure
the typingInterval is still cleared on completion or error and that errors
release the lock so the queue can proceed.
- Around line 168-188: The session IDs are currently created with a timestamp
causing sessions to change after restarts; change the default creation in the
session-reuse block (where activeSessions.set(channelId,
`ch-${channelId}-${Date.now()}`) and sessionId is read) to a stable per-channel
id like `ch-${channelId}`, and only create a timestamped id when the user
explicitly resets (modify the "!reset" branch that currently calls
activeSessions.delete(channelId) to instead set a new timestamped id, e.g.,
`ch-${channelId}-${Date.now()}`, or delete then immediately set the timestamped
id so downstream history is only lost on explicit reset).

---

Nitpick comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Line 69: Replace the colon-terminated lead-in sentences with period-terminated
sentences for each instance (e.g., change "Start the Discord bridge and other
auxiliary services:" to "Start the Discord bridge and other auxiliary
services.") — update the same style for the other identified strings on lines
that currently read like full sentences ending with a colon (the lead-ins at the
same locations as "Start the Discord bridge and other auxiliary services:") so
every sentence ends with a period and colons are only used for lists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29c888da-c67e-4da1-a9bd-0912cfeb46d0

📥 Commits

Reviewing files that changed from the base of the PR and between 0a8b989 and 23dc8c9.

📒 Files selected for processing (2)
  • docs/deployment/set-up-discord-bridge.md
  • scripts/discord-bridge.js

Comment thread docs/deployment/set-up-discord-bridge.md
Comment thread docs/deployment/set-up-discord-bridge.md Outdated
Comment thread scripts/discord-bridge.js
@wscurran

Copy link
Copy Markdown
Contributor

Thanks for adding a host-side Discord bridge, this will enable more flexible communication options for our users.

@wscurran wscurran added integration: discord Discord integration or channel behavior integration: telegram Telegram integration or channel behavior labels Mar 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Line 43: The line containing "Store it securely. Discord does not show it
again." has two sentences on one line; split this into two separate lines so
each sentence is on its own line (i.e., one line "Store it securely." and the
next line "Discord does not show it again.") to comply with the
one-sentence-per-line formatting rule.
- Line 133: Replace the sentence fragment "To stop the Discord bridge and all
other auxiliary services." with a complete sentence that ends with a period; for
example, change it to "To stop the Discord bridge and all other auxiliary
services, run the following commands." or "Use this section to stop the Discord
bridge and all other auxiliary services." Ensure the updated line is a full
sentence and fits the surrounding section's tone and instructions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3785e035-f95c-4d1b-971c-51621455a16f

📥 Commits

Reviewing files that changed from the base of the PR and between 23dc8c9 and 0025b80.

📒 Files selected for processing (2)
  • docs/deployment/set-up-discord-bridge.md
  • scripts/discord-bridge.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/discord-bridge.js

Comment thread docs/deployment/set-up-discord-bridge.md Outdated
Comment thread docs/deployment/set-up-discord-bridge.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
docs/deployment/set-up-discord-bridge.md (2)

75-76: Replace inline colon punctuation in default-value clauses.

(default: ...) uses a colon as general punctuation, which this style guide disallows outside list introductions.

As per coding guidelines, "Colons should only introduce a list. Flag colons used as general punctuation between clauses."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` around lines 75 - 76, The
default-value clauses for SANDBOX_NAME and NEMOCLAW_MODEL use a disallowed
inline colon "(default: `...`)"; update the text for SANDBOX_NAME and
NEMOCLAW_MODEL to remove the colon (e.g., "(default `default`)" and "(default
`nvidia/nemotron-3-super-120b-a12b`)" or rewrite as "default is `...`") so the
parentheses no longer contain a colon used as general punctuation.

91-91: Use active voice for the startup condition.

“The Discord bridge starts only when the DISCORD_BOT_TOKEN environment variable is set.” uses passive voice; rephrase with an explicit subject (for example, “when you set ...”).

As per coding guidelines, "Active voice required. Flag passive constructions."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` at line 91, Rephrase the passive
startup sentence into active voice; replace "The Discord bridge starts only when
the `DISCORD_BOT_TOKEN` environment variable is set." with an explicit subject
instruction such as "Set the `DISCORD_BOT_TOKEN` environment variable to start
the Discord bridge." to comply with the Active voice required guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Around line 75-76: The default-value clauses for SANDBOX_NAME and
NEMOCLAW_MODEL use a disallowed inline colon "(default: `...`)"; update the text
for SANDBOX_NAME and NEMOCLAW_MODEL to remove the colon (e.g., "(default
`default`)" and "(default `nvidia/nemotron-3-super-120b-a12b`)" or rewrite as
"default is `...`") so the parentheses no longer contain a colon used as general
punctuation.
- Line 91: Rephrase the passive startup sentence into active voice; replace "The
Discord bridge starts only when the `DISCORD_BOT_TOKEN` environment variable is
set." with an explicit subject instruction such as "Set the `DISCORD_BOT_TOKEN`
environment variable to start the Discord bridge." to comply with the Active
voice required guideline.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e63eaca8-306f-4d50-b191-aeafdd16ada3

📥 Commits

Reviewing files that changed from the base of the PR and between 0025b80 and 9a5c709.

📒 Files selected for processing (1)
  • docs/deployment/set-up-discord-bridge.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
docs/deployment/set-up-discord-bridge.md (1)

6-8: Fix product-name casing in frontmatter keywords and tags.

Line 6 and Line 8 use lowercase product names (nemoclaw, openclaw, openshell). Please update these to NemoClaw, OpenClaw, and OpenShell for terminology consistency across docs metadata and prose.

As per coding guidelines, "NemoClaw, OpenClaw, and OpenShell must use correct casing."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` around lines 6 - 8, Update the
frontmatter entries "keywords" and "tags" to use the correct product-name
casing: replace "nemoclaw" with "NemoClaw", "openclaw" with "OpenClaw", and
"openshell" with "OpenShell" in the values for the keywords and tags arrays so
that the metadata keys keywords and tags contain the correctly cased product
names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Around line 6-8: Update the frontmatter entries "keywords" and "tags" to use
the correct product-name casing: replace "nemoclaw" with "NemoClaw", "openclaw"
with "OpenClaw", and "openshell" with "OpenShell" in the values for the keywords
and tags arrays so that the metadata keys keywords and tags contain the
correctly cased product names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 28febc8f-59c8-44d3-bc68-71699ded0834

📥 Commits

Reviewing files that changed from the base of the PR and between 9a5c709 and 280a62b.

📒 Files selected for processing (1)
  • docs/deployment/set-up-discord-bridge.md

@phanisaimunipalli

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Mar 28, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Reviews resumed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Around line 75-76: Split the two sentences about SANDBOX_NAME and
NEMOCLAW_MODEL so each sentence is on its own line: ensure the sentence
describing `SANDBOX_NAME` ("`SANDBOX_NAME` selects which sandbox the bridge
connects to. The default is `default`.") is two separate lines, and do the same
for `NEMOCLAW_MODEL` ("`NEMOCLAW_MODEL` sets the model the agent uses for
inference. The default is `nvidia/nemotron-3-super-120b-a12b`.") so every
sentence appears on its own line for readable diffs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 787db70d-fc34-4424-be48-be5c9a48d1b0

📥 Commits

Reviewing files that changed from the base of the PR and between 9a5c709 and f0dbb20.

📒 Files selected for processing (1)
  • docs/deployment/set-up-discord-bridge.md

Comment on lines +75 to +76
`SANDBOX_NAME` selects which sandbox the bridge connects to. The default is `default`.
`NEMOCLAW_MODEL` sets the model the agent uses for inference. The default is `nvidia/nemotron-3-super-120b-a12b`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Split into one sentence per line.

Lines 75 and 76 each contain two sentences on the same line, violating the formatting rule requiring one sentence per line for readable diffs. As per coding guidelines, "One sentence per line in source (makes diffs readable). Flag paragraphs where multiple sentences appear on the same line."

📝 Proposed fix
-`SANDBOX_NAME` selects which sandbox the bridge connects to. The default is `default`.
-`NEMOCLAW_MODEL` sets the model the agent uses for inference. The default is `nvidia/nemotron-3-super-120b-a12b`.
+`SANDBOX_NAME` selects which sandbox the bridge connects to.
+The default is `default`.
+`NEMOCLAW_MODEL` sets the model the agent uses for inference.
+The default is `nvidia/nemotron-3-super-120b-a12b`.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`SANDBOX_NAME` selects which sandbox the bridge connects to. The default is `default`.
`NEMOCLAW_MODEL` sets the model the agent uses for inference. The default is `nvidia/nemotron-3-super-120b-a12b`.
`SANDBOX_NAME` selects which sandbox the bridge connects to.
The default is `default`.
`NEMOCLAW_MODEL` sets the model the agent uses for inference.
The default is `nvidia/nemotron-3-super-120b-a12b`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` around lines 75 - 76, Split the two
sentences about SANDBOX_NAME and NEMOCLAW_MODEL so each sentence is on its own
line: ensure the sentence describing `SANDBOX_NAME` ("`SANDBOX_NAME` selects
which sandbox the bridge connects to. The default is `default`.") is two
separate lines, and do the same for `NEMOCLAW_MODEL` ("`NEMOCLAW_MODEL` sets the
model the agent uses for inference. The default is
`nvidia/nemotron-3-super-120b-a12b`.") so every sentence appears on its own line
for readable diffs.

mafueee pushed a commit to mafueee/NemoClaw that referenced this pull request Mar 28, 2026
* add guidance for OpenAI-compatible cloud providers

* add provider list and xref link

* add xref

* add provider name value column
phanisaimunipalli and others added 6 commits March 29, 2026 11:21
Add scripts/discord-bridge.js following the same architecture as
the Telegram bridge. Runs on the host because the sandbox proxy
does not support CONNECT tunneling for WebSockets, which the
Discord gateway requires (see NVIDIA#409).

- Per-channel session continuity via channel-scoped session IDs
- Message chunking at Discord's 2000-character limit
- NEMOCLAW_MODEL and SANDBOX_NAME env var support
- ALLOWED_GUILD_IDS for access control
- !reset command to clear a channel session
- Typing indicator while the agent runs

Updates start-services.sh to start/stop the Discord bridge
alongside the Telegram bridge when DISCORD_BOT_TOKEN is set.

Adds docs/deployment/set-up-discord-bridge.md with setup
instructions for creating a Discord application, inviting the
bot, and configuring access control.

Adds discord.js ^14.16.3 to package.json dependencies.

Closes NVIDIA#422.
scripts/discord-bridge.js:
- Fix SANDBOX default to "default" to match start-services.sh
- Fix session ID logic: reuse stored ID per channel, use timestamped
  unique ID so !reset truly starts a fresh session
- Fix SSH config race: use unique confPath per invocation
  (timestamp + random suffix) to avoid concurrent call collisions
- Redact message content from logs by default; gate full-text
  logging behind DEBUG_DISCORD=true env var
- Add JSDoc to runAgentInSandbox() and sendChunked()

docs/deployment/set-up-discord-bridge.md:
- Add NVIDIA_API_KEY export to environment variables section
- Add intro sentence under Prerequisites, Create a Bot, Invite Bot H2s
- Rename "Related Topics" to "Next Steps" with intro sentence
- Replace em dash with period ("Store it securely. Discord...")
scripts/discord-bridge.js:
- Add allowedMentions: { parse: [], repliedUser: false } to Client
  constructor to prevent unintended pings from bot responses
- Add per-channel serialization queue (channelQueues Map) so only one
  agent call runs per channel at a time, preventing interleaved replies
- Revert session ID to stable ch-<channelId> by default so sessions
  survive restarts; !reset now sets a timestamped ID to force fresh history
- Add JSDoc for enqueueForChannel()

docs/deployment/set-up-discord-bridge.md:
- Add SANDBOX_NAME and NEMOCLAW_MODEL optional env var documentation
- Fix bare code block for !reset to use text language tag
- Replace colon with period on lead-in sentences before code blocks
Split the two-sentence token note onto separate lines and complete
the sentence fragment in the Stop the Services section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace "(default: `...`)" with "The default is `...`" on the SANDBOX_NAME
and NEMOCLAW_MODEL description lines, and rephrase the passive startup
condition sentence to active voice.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use correct casing — NemoClaw, OpenClaw, OpenShell — in the keywords
and tags frontmatter arrays.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
docs/deployment/set-up-discord-bridge.md (1)

75-76: ⚠️ Potential issue | 🟡 Minor

Split into one sentence per line.

Lines 75 and 76 each contain two sentences on the same line, which violates the formatting rule requiring one sentence per line for readable diffs. As per coding guidelines, "One sentence per line in source (makes diffs readable). Flag paragraphs where multiple sentences appear on the same line."

📝 Proposed fix
-`SANDBOX_NAME` selects which sandbox the bridge connects to. The default is `default`.
-`NEMOCLAW_MODEL` sets the model the agent uses for inference. The default is `nvidia/nemotron-3-super-120b-a12b`.
+`SANDBOX_NAME` selects which sandbox the bridge connects to.
+The default is `default`.
+`NEMOCLAW_MODEL` sets the model the agent uses for inference.
+The default is `nvidia/nemotron-3-super-120b-a12b`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/deployment/set-up-discord-bridge.md` around lines 75 - 76, The two lines
mentioning SANDBOX_NAME and NEMOCLAW_MODEL contain two sentences each; split
each into separate lines so there is one sentence per line. Locate the lines
that reference SANDBOX_NAME and NEMOCLAW_MODEL and break them so the sentence
about what the variable selects/sets is on one line and the sentence about its
default value is on the following line (e.g., for SANDBOX_NAME: one line
"SANDBOX_NAME selects which sandbox the bridge connects to." and next line "The
default is `default`."; similarly for NEMOCLAW_MODEL).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docs/deployment/set-up-discord-bridge.md`:
- Around line 75-76: The two lines mentioning SANDBOX_NAME and NEMOCLAW_MODEL
contain two sentences each; split each into separate lines so there is one
sentence per line. Locate the lines that reference SANDBOX_NAME and
NEMOCLAW_MODEL and break them so the sentence about what the variable
selects/sets is on one line and the sentence about its default value is on the
following line (e.g., for SANDBOX_NAME: one line "SANDBOX_NAME selects which
sandbox the bridge connects to." and next line "The default is `default`.";
similarly for NEMOCLAW_MODEL).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1a458628-681d-4348-acb4-043c567040da

📥 Commits

Reviewing files that changed from the base of the PR and between f0dbb20 and 6457de0.

📒 Files selected for processing (5)
  • docs/deployment/set-up-discord-bridge.md
  • docs/index.md
  • package.json
  • scripts/discord-bridge.js
  • scripts/start-services.sh
✅ Files skipped from review due to trivial changes (3)
  • docs/index.md
  • package.json
  • scripts/discord-bridge.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/start-services.sh

@wscurran

Copy link
Copy Markdown
Contributor

Thanks for the Discord bridge implementation. There's overlapping work in #422 from another contributor — before we can review either PR, it would help to align on a single approach. Could you rebase against main, review #422, and coordinate with that contributor on which implementation to move forward? Happy to facilitate if useful.

@bysejin

bysejin commented Apr 14, 2026

Copy link
Copy Markdown

Thanks, I’ll rebase #422 onto the current main and review #458 for overlap.

From an initial comparison, the overlap looks substantial: both PRs add a host-side Discord bridge using discord.js, integrate it with the service startup flow, add deployment/setup docs, and maintain channel-scoped session continuity.

One notable difference is that #422 extends beyond the bridge implementation itself by also updating the deploy/CLI flow for forwarding the Discord bot token, while #458 appears to focus more on Discord-specific runtime refinements such as reset handling, typing indicators, message chunking, allowlisting, and queueing/safety follow-ups.

Once I’ve rebased and rechecked against main, I’m happy to coordinate on a single landing path with @phanisaimunipalli.

@wscurran

Copy link
Copy Markdown
Contributor

Thanks for this — a host-side Discord bridge is a frequently requested integration. We're doing an evaluation of the Discord bridge approach before committing to a merge path, as there's also a competing implementation in #422.

We'll be reviewing both PRs together to decide on direction. We'll follow up here once we have a clearer picture. No action needed on your end for now — we'll reach out if we need more information.

@wscurran

Copy link
Copy Markdown
Contributor

Thanks for contributing this. The underlying limitation this PR worked around — the sandbox proxy not supporting CONNECT tunneling for WebSocket connections to gateway.discord.gg — was fixed in PR #438, which switched the Discord and Slack policy presets to use CONNECT tunnels. Discord now runs natively inside the sandbox via OpenShell-managed channels and the discord.yaml policy preset; scripts/discord-bridge.js is no longer part of the codebase.

Closing as superseded. Feel free to reopen if you see gaps in the current Discord channel support.

@wscurran wscurran closed this Apr 21, 2026
@wscurran wscurran added area: integrations Third-party service integration behavior feature PR adds or expands user-visible functionality and removed enhancement: feature labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: integrations Third-party service integration behavior feature PR adds or expands user-visible functionality integration: discord Discord integration or channel behavior integration: telegram Telegram integration or channel behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants