Repository navigation
/rc Discord bridge: thinking elapsed-status persists; mid-run /rc drops events; /rc <mode> restart silently breaks streaming #974
Description
Summary
Three related defects in the built-in Discord /rc remote-control bridge (no user mod involved): 1) The -# ✻ Thinking… Ns elapsed-time status line sometimes persists on a Discord reply, even though the agent is emitting new text and the task has completed. 2) Running /rc while the terminal session is already mid-run opens the Discord thread but no session events are ever forwarded to it. 3) Toggling permission mode with /rc yolo (or any /rc that goes through startRemote) restarts the bridge, after which no further session events are forwarded to Discord. I have local patches for all three (see Additional context). I am happy to file PRs once we agree on the fix direction; this issue is to track the bugs.
Expected Behavior
Thinking… Nsshould be cleared as soon as the first text_delta (or any terminal event like run_end / interrupted / run_error) is forwarded, so the user always sees the freshest assistant message without a stale thinking line. 2) Running /rc on a session that already has an in-flight run should attach to that run: subsequent run_start / text_delta / tool_* / run_end events should be forwarded to the Discord thread opened by the new bridge. 3) /rc should seamlessly continue forwarding events. From the user perspective, the Discord thread keeps updating through the mode change — the bridge restart is invisible.
Actual Behavior
Defect 1 — stale Thinking… Ns line. Inside createReplyStream.onEvent, the text_delta branch clears the elapsed-time status (i = void 0) but never calls stopTick(). setStatus(undefined) is the only path that cancels the 10 s elapsed-time refresh timer; text_delta does not. The orphan timer fires once, sees i === undefined, and silently dies — but the previously-emitted message containing the ✻ Thinking… Ns line stays on screen because compose() only suppresses the status line on the next n.update(...), which can be swallowed by createDiscordReply's 1 s debounce, by a Discord 429 (no retry on the channel-edit path), or simply by close() having shipped compose(finalText) while i was still set.
Defect 2 — /rc mid-run never attaches a stream. useRemoteBridge's useEffect subscribes to the session event stream and only calls M(chatId, flag) (the function which creates a fresh ReplyStream and assigns it to h.current) when run_start fires with h.current === null and y.current.shift() returns a queued Discord chat id. When the user runs /rc mid-run: no run_start has been emitted since the bridge was set up (the run is already in flight), y.current is empty (no Discord message has been received yet), so h.current stays null, and every subsequent event hits t && (t.onEvent(e), ...) with t === null — silently dropped.
Defect 3 — /rc restart drops events. /rc yolo calls startRemote, which builds a brand-new bridge, then setActiveRemote(...) stops the old one and assigns the new one. subscribeRemoteStopped then sets h.current = null, f.current = null, y.current = []. The new bridge's ownerChatId is undefined until the WebSocket connects, Discord sends READY, and onReady → announceReady → bridge.announce() → threadId() creates the thread (HTTP call). During that window, every session event hits Defect 2's branch, finds ownerChatId === undefined, and bails — events are dropped until the WS is ready. In practice the user observes messages stop and never resume: the old stream is gone, the new thread is invisible to useRemoteBridge until too late.
Steps to reproduce the issue
Defect 1 — start a session, send a Discord message, observe -# ✻ Thinking… Ns. Have the agent emit text_delta events that should remove the line. The line often stays.
Defect 2 — start a session, wait for the agent to begin a run (run_start fires), then run /rc from the terminal. The Discord thread is opened by onReady but no text_delta, tool_*, run_end, etc. is ever forwarded.
Defect 3 — start a session, send a Discord message, observe flowing updates. From the terminal, run /rc yolo (or /rc auto-accept, /rc plan, etc.). The bridge restarts. New updates stop appearing in the Discord thread.
Command Code Version
1.74.0
Operating System
Linux
Terminal/IDE
command-code (terminal TUI)
Shell
bash
Session file (optional)
No response
Fix prompt (optional)
Please fix the three defects in the bundled Discord /rc bridge inside dist/cli.mjs:
-
In
createReplyStream.onEvent, thetext_deltabranch must callstopTick()afteri = void 0, mirroringsetStatus(undefined)so the 10-second elapsed-time refresh timer is cancelled deterministically. -
In
useRemoteBridge, the events subscription should not only attempt to open a stream onrun_start. Whenever an event arrives withh.current === nullandy.currentis empty but a Discord bridge is active with anownerChatId, the handler must callM(ownerChatId, true)to attach a freshReplyStreamto the in-flight run. -
In
startRemote, aftersetActiveRemote(...)and befores.start(), eagerly create the Discord thread (so it is ready before the first session event after/rc <mode>). Cleanest upstream fix is to expose either the configuredchannelIdor awhenReady()promise on the bridge so we can wait for the thread to be created without sending a placeholder message. If exposing the API is too much, callings.announce("<one-line mode notice>")betweensetActiveRemote(...)ands.start()works as a workaround — the announce call resolves the bridge-ownedthreadId()promise (HTTP, no WS needed) and setsownerChatIddeterministically. A real message (e.g.-# Mode: <label>) reads better than a ZWSP.
Please add node --check validation in CI for the resulting bundle (the file is minified ESM, so new Function is not a viable parse check).
Additional context
Local patches are at ~/bin/patch-cmd-rc-discord.js. It applies three in-place edits to ~/.nvm/versions/node/v26.7.0/lib/node_modules/command-code/dist/cli.mjs (a backup at cli.mjs.pristine is kept). The bundle is minified, so the patch matches on stable text anchors:
-
Patch 1 (Issue 1):
createReplyStream'stext_deltabranch getsstopTick()afteri = void 0so the 10 s elapsed-time refresh timer is cancelled deterministically, mirroringsetStatus(undefined). Offset ~2446931. -
Patch 2 (Issue 2): the run-start check inside the events subscription in
useRemoteBridgeis generalised so that when any event fires withh.current === nullandy.currenthas no queued Discord chatId, the bridge'sownerChatIdis used as a fallback (in follow mode). Offset ~2695914. -
Patch 3 (Issue 3):
startRemoteeagerly callss.announce(\"-# Mode: ${modeLabel(t)}\")betweensetActiveRemote(...)ands.start(). This forcesthreadId()to resolve (HTTP, no WS needed) soownerChatIdis defined before any session event arrives. The deliver puts a one-line mode label into the thread so the user sees what mode they are in and Discord stops logging the old "Cannot send an empty message" error from an earlier revision. Offset ~2487983.
All three together restore the expected behaviour.
Activity
ObaidNadeem commentedon Oct 9, 2026
Thanks for creating the issue @pbtrudel, cc @bas3line pinging you for rc.
bas3line commentedon Oct 9, 2026
Hi, thanks for reporting taking a look at it now