Repository navigation
Description
Summary
Let sessions be assigned to a chat-group, and have CodeShellManager host an MCP server that the agents in that group connect to. Members can then discover each other and exchange messages — agent-to-agent, not routed through the human.
Today CSM is a container for N independent agents: it can show them side by side, and (once #59 lands) type the same thing into all of them. It cannot let them talk. That's the gap.
Why this is different from broadcast (#59)
Broadcast is one-to-many from the user — the same keystrokes fanned out. Every agent still works alone and nothing comes back except to the human's eyes.
Chat-groups are many-to-many between agents, with CSM as broker. The point is the messages the user never types:
- A planner session hands a task to two implementer sessions and collects their results
- An implementer asks the session that owns another repo "what's the signature of X?" instead of guessing
- A reviewer session watches a build session and comments when it goes red
- Two agents working different worktrees of one repo coordinate before both edit the same file
Shape
Group = channel
Reuse the existing SessionGroup (group strip, drag-to-assign, per-group colour are already shipped). A group optionally becomes a chat channel; membership in the group is membership in the channel. Sessions not in a group, or in a non-chat group, see nothing — this must be opt-in per group, because an agent that can message its neighbours is a meaningfully larger blast radius than one that cannot.
CSM hosts the MCP server
Relates to #22, which proposes an MCP connection for CSM generally. These should share one server rather than growing two: #22 is "let an agent drive CSM", this is "let agents reach each other through CSM". Same transport, same auth, different tool namespace.
Tools, roughly:
| Tool | Purpose |
|---|---|
list_peers |
who else is in my chat-group (name, folder, branch, idle/busy) |
send_message |
to one peer or to the whole group |
read_messages |
drain my inbox since last read |
whoami |
my session identity, so an agent can address replies |
Each session's MCP client is scoped to its own identity by the server — an agent cannot spoof another member or read a channel it isn't in. Scoping is server-side, never a parameter the agent supplies.
Delivery — the real design question
Two options, and this needs deciding before implementation:
- Pull (mailbox). Messages queue; the agent calls
read_messageswhen it chooses. Safe and simple, but an idle agent never notices it has mail, so conversations stall until something else wakes it. - Push (inject into the PTY). CSM types the message into the recipient. Immediate, but it interleaves with whatever the agent is mid-way through, and it's indistinguishable from the user typing — which is exactly the confusion fix(terminal): promote on real key events, not on anything xterm sends #106 was about on the input path.
A hybrid is probably right: queue by default, and push a short "you have 2 messages from planner" nudge only when the recipient is idle (AlertDetector already knows this — IsWaitingForInput). Worth prototyping both before committing.
Visibility for the human
Non-negotiable: if agents are talking, the user must be able to see it. A per-group transcript pane — who said what to whom, timestamped — and ideally a kill switch that suspends a channel mid-conversation. An agent conversation that only exists inside the agents' contexts is not debuggable and not trustworthy.
Risks worth naming up front
- Loops. Two agents can trivially ping-pong forever, burning tokens with nobody watching. Needs a rate limit per channel and probably a depth/turn cap.
- Cost. Every message is tokens in someone's context window. A chatty channel across 6 agents multiplies spend invisibly.
- Prompt injection across sessions. A message from peer B lands in agent A's context as text. If B is compromised or just confused, it is now injecting into A. Messages must be clearly delimited and attributed as untrusted peer input, never presented as user instruction.
- Blast radius. Opt-in per group, off by default.
Open questions
- Does the agent connect to CSM's MCP server itself (needs the CLI's MCP config pointed at us), or does CSM inject the config when launching a session in a chat-group? The latter is far better UX and probably required for this to be usable at all.
- Transport: stdio won't work for a server shared by N clients, so HTTP/SSE on localhost with a per-session token.
- Persistence: does a channel transcript survive restart, and does it go in
state.jsonor the SQLite DB? (Note Cap output.db by size with FIFO eviction — the 30-day prune never reclaims disk space #124 — the DB already has an unbounded-growth problem; a chat transcript should not be added to it without a retention story.)
Related
- MCP connection for CodeShellManager #22 — MCP connection for CodeShellManager (should be the same server)
- Broadcast input to every session in a group (groups themselves already shipped) #59 — broadcast input to a group (the user-to-agents direction of the same idea)
- Cap output.db by size with FIFO eviction — the 30-day prune never reclaims disk space #124 — DB growth, if transcripts get persisted
Activity
AThraen commentedon Sep 9, 2026
Related: #132, which takes this issue's "one server, one transport, one auth, separate tool namespaces" conclusion and adds two more namespaces on the same footing —
self(a session controls its own appearance/notes/run-commands inside CSM) andfleet(overview of the other sessions, plus gated orchestration verbs). The chat tools specified here become thechatnamespace; delivery mechanics stay decided here and are inherited there.#132 also inherits this issue's identity rule — server-assigned, never a tool argument — and argues it matters more once
fleet.close_sessionexists.