feat(ui): Claude Channels TUI — message metadata, status surfaces, dialog, docs (3/3) - #3347
Draft
joestump-agent wants to merge 16 commits into
Draft
feat(ui): Claude Channels TUI — message metadata, status surfaces, dialog, docs (3/3)#3347joestump-agent wants to merge 16 commits into
joestump-agent wants to merge 16 commits into
Conversation
This was referenced Jul 17, 2026
joestump-agent
force-pushed
the
upstream/channels-3-tui
branch
from
July 17, 2026 18:41
f29988d to
a0e8b01
Compare
Contributor
Author
|
Heads-up for review: rebased over the newly-merged scrollable sidebar (#3294). The Channels status section is now much simpler — it renders as one more section in |
joestump-agent
force-pushed
the
upstream/channels-3-tui
branch
2 times, most recently
from
July 24, 2026 22:11
57db925 to
ad5cc91
Compare
… needed Incoming channel messages are dispatched into the active session as user prompts. When no session exists yet (fresh TUI on the landing page) a session is auto-created for the channel event instead of dropping it. Assisted-by: Claude Fable 5
In client/server mode channel notifications now flow through the server's event stream, so detached clients receive them the same way the in-process backend does. Assisted-by: Claude Fable 5
…pace Channel routing moves out of the TUI and into the backend so each workspace that opted in receives a channel event exactly once, picking the viewed session (or most recent top-level session, creating one if none) as the target. Assisted-by: Claude Fable 5
MCP servers that declared the claude/channel capability and are enabled via --channels show a channel marker in the MCP status list, so it is visible which servers can push into the session. Assisted-by: Claude Fable 5
Sessions record which channel started them (new channel column on sessions), and the agent's channel reply tool targets exactly that channel instead of broadcasting to every connected one. Covers the in-process and client/server paths. Assisted-by: Claude Fable 5
Turns that originate from a channel push (e.g. Signal MCP) previously answered only in terminal output; the person messaging the channel saw nothing unless the model chose to call a send tool on its own. A new per-server channel_reply config maps direct and group pushes onto the server's reply tools, and Crush deterministically sends the turn's final assistant text through the matching route — to the sender for DMs, to the group for group messages — skipping the auto-send when the model already replied through the channel itself during the turn. Local turns and channels without the config are unchanged. Fixes #160 Assisted-by: Claude Fable 5
… main The charmbracelet#3345 review added a filter that keeps channel events out of the shared SubscribeEvents fan-out (they carry no workspace identity on the process-global MCP broker), deferring delivery wiring to this PR. Wire it: a channel-only SubscribeChannelEvents feeds the backend router, and each app scopes its own subscription to servers its workspace declares and opted in — restoring in-process injection and SSE delivery without reopening the cross-workspace leak. Drop the events_test stopgap that asserted channel events have no SSE representation; they do now. Assisted-by: Claude Fable 5
Address the reaping question from review: the sessions.channel column now reflects the most recent turn's origin rather than accumulating forever. Every turn passes through the coordinator, which (re)binds the session on a channel-originated turn — newest push wins — and clears the binding on a local turn, since a session the user takes over is no longer channel-driven. The column is dropped with the session row on delete, so no separate reaping pass exists or is needed. This also closes a gap where the server-side inject path never persisted the binding at all (only the in-process TUI did): the coordinator is shared by both paths. Assisted-by: Claude Fable 5
Add a channel_enabled boolean to the per-server MCP config so channels can be declared persistently in crush.json instead of requiring --channels on every launch. The new ChannelOptIn helper ORs the config flag with the existing --channels override at every gate site — session creation, renewal, OAuth reconnect, the app's scoped channel event forwarder, and backend routing — so either source works and they compose. The claude/channel capability gate is unchanged: config replaces the CLI opt-in, not the handshake requirement. Ported from #181. Assisted-by: Claude Fable 5
db.ResetPool() closes every pooled connection process-wide, so the cleanup was closing the databases of agent-package tests still running in parallel (TestPersistCanceledTurn failed with 'sql: database is closed' in CI). Releasing this test's own dataDir is the scoped cleanup. Assisted-by: Claude Fable 5
Conflict-resolution follow-ups from rebasing onto main's config-tracking updateState: restore the stateOpt/withConfig/withPending helpers, gate runAuthFlow's connect on ChannelOptIn so the config opt-in applies to OAuth reconnects, and teach mcpConfigEqual about ChannelEnabled so a config toggle reconciles the server (ChannelReply stays excluded: it is read live at reply time, not baked into the session).
…pt queue With channels dispatching work into background sessions, global agent busy state made the viewed session look busy (and left the queued-prompt pill stale). Track busy/queue state per session so activity elsewhere does not leak into the current view. Assisted-by: Claude Fable 5
User messages that arrived via a channel render a sender/via/at metadata line as a separate list item below the message body, so channel traffic is distinguishable from typed input. Assisted-by: Claude Fable 5
Shows connected channel servers (and their enabled/connected state) alongside the existing LSP/MCP/Skills status sections. Assisted-by: Claude Fable 5
Adds a filterable dialog (via the command palette) showing every MCP server that declared the claude/channel capability, its connection state, and whether it is enabled for this session. Assisted-by: Claude Fable 5
joestump-agent
force-pushed
the
upstream/channels-3-tui
branch
2 times, most recently
from
August 16, 2026 06:24
09bcfd7 to
d5f1bc4
Compare
Covers the claude/channel capability, the --channels per-session opt-in, payload validation and escaping, delivery semantics in-process and against crush serve, two-way channels, deterministic channel_reply routing, and a Signal MCP example. Assisted-by: Claude Fable 5
joestump-agent
force-pushed
the
upstream/channels-3-tui
branch
from
August 16, 2026 07:12
d5f1bc4 to
3ca5e63
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 3/3 of the Claude Channels series (#3298, #3304). Posted by
@joestump-agentat@joestump's direction.Note
Stacked on #3346 — rebased onto the post-#3345 stack: the first 9 commits here belong to #3346; please review commits 10–14 (
40ae13d7..ad5cc91c). Marked draft until #3346 merges, then this rebases to just its own commits and flips to ready.Summary
The TUI layer for channels, plus docs:
sender / via channel / at timemetadata line as a separate list item below the body, so channel traffic is distinguishable from typed input.--channelsandchannel_enabledopt-ins, payload security posture, delivery semantics, two-way channels,channel_replyrouting, and a Signal MCP walkthrough (signal-mcp).Screenshots
Channels status on first boot:
Channel-originated message with the metadata line, and the sidebar section:
Notes
Test plan
internal/ui/chat/user_test.go)internal/ui/model/channels_test.go)internal/ui/dialog/channels_test.go)internal/ui/modeltestsgo build ./...,gofmtclean; full suite matches upstreammainbaseline💘 Generated with Crush
Assisted-by: Claude Fable 5