Jason, 2026-09-26: while Mosaic Stack development runs in T3, development agents message each other through T3 threads with the t3 MCP tools, until Mosaic Stack has its own internal comms. ms-communications now picks the transport by where the recipient runs, carries the T3 header and reply rules, and counts a returned turnId as the delivery receipt. docs/guides/T3-AGENT-COMMS.md is adapted to this repository: status, thread lookup by agent-name title, replies with wait off, and record locations; references to guides absent here are replaced. Suites green before commit: config 24, task 90, foundation 43, conductor 17, release 14, auth 15, discord 63. unslop-check clean on both files. Reviewed by Sage (T3 thread 1ef1e4f8). Co-Authored-By: Claude Opus 5.5 <[email protected]>
11 KiB
T3 Agent Comms Guide
How an agent in one T3 Code thread talks to an agent in another thread, including across harnesses
(Claude to Codex, Codex to Claude, either to OpenCode). Measured on sb-it-1-dt against T3 Code
0.0.42 on 2026-09-16; re-checked on dragon-lin (T3 0.0.42) on 2026-09-26. Re-verify the setup
section before trusting it on another host.
Status in Mosaic Stack
Jason, 2026-09-26: while Mosaic Stack development runs in T3, T3 threads are the channel between
development agents (Darkwing, Sage, Dewey, and the rest of the agents/ roster). This is a
temporary channel. Mosaic Stack will get its own internal comms, and this guide is retired
when it does. Do not build Mosaic features on top of the T3 tools.
skills/ms-communications/SKILL.md holds the messaging rules that apply on every transport:
triage classes, etiquette, receiving, and persistence. This guide covers the T3 mechanics.
Seats in tmux panes still use tools/tmux/agent-send.sh (see tools/tmux/README.md).
A thread is a T3 conversation bound to one project, one worktree, one harness, and one model. A message sent to a thread starts a turn in it, or steers the turn already running, so the receiving agent wakes and answers without any polling.
What it is
t3-code-agent-mcp is a local MCP server (Model Context Protocol, the standard tool interface
every harness speaks) that drives T3 threads through T3's own HTTP and WebSocket API on
127.0.0.1:3773. It is installed at:
/mnt/storage/src/t3-comms/t3-code-agent-mcp (source, built to dist/cli.js)
~/.t3-code-agent-mcp/credentials.json (30-day bearer token, expires 2026-10-16)
It is registered as MCP server t3 for Claude Code (user scope, ~/.claude.json) and Codex
(~/.codex/config.toml). In Claude Code the tools appear as mcp__t3__t3_*; load them with
ToolSearch (select:mcp__t3__t3_send_message,...) if they are deferred. Any session started
after 2026-09-16 17:40Z sees the tools. Sessions started earlier must be restarted.
Upstream T3 is adding the same capability natively (pingdotgg/t3code PR 11303). When that merges and ships, this guide moves to the built-in tools and the external server is retired.
Verify before use
node /mnt/storage/src/t3-comms/t3-code-agent-mcp/dist/cli.js status
Expected: Server: http://127.0.0.1:3773 (v0.0.42 …), Token: ok, and a harness list. If the
token line says there is no token, re-pair (see Upkeep). If the server line fails, T3 Code is not
running on this host; start the app or t3 first.
Tools
All tools are prefixed t3_. Names and models must match the listing exactly; there is no
fuzzy matching and no silent substitution.
| Tool | Use |
|---|---|
t3_list_projects |
Project ids and workspace roots |
t3_list_worktrees |
Worktrees and branches of one project |
t3_list_harnesses |
Usable harnesses (codex, claudeAgent, opencode, …) and their model slugs |
t3_list_threads |
Recent threads, optionally per project |
t3_create_thread |
New thread with project, worktree, harness, model, title, first prompt |
t3_send_message |
Follow-up prompt to an existing thread |
t3_get_thread |
Status, turn state, recent messages |
t3_wait_for_turn |
Block until the current turn ends and return the reply |
t3_cancel_turn |
Interrupt a running turn |
wait: true on create and send returns the peer's reply text directly. Default wait cap is 300
seconds; raise timeoutSeconds for long turns or poll with t3_get_thread.
Every result carries url, which opens the thread in the T3 UI, and turn.state
(running, completed, interrupted, error).
Finding thread IDs
Mosaic development threads live in the mosaic-stack project and are titled with the agent's
name (Darkwing, Sage, Dewey in Claude, ...). Look them up each time; thread IDs change when
Jason starts a new thread for an agent.
t3_list_threads {project:"/mnt/storage/src/mosaic-stack"}
Your own thread is the one titled with your name whose turn.state is running right now,
because your turn is the one making the call. If two threads carry your name and both are
running, you cannot tell them apart; write thread-id: unknown in the header rather than guess.
Jason may also give you your thread ID or your lead's thread ID in the assignment. Use those.
Message header (required)
Start every inter-agent message with a sender-to-recipient header on its own line, including initial prompts, follow-ups, replies, and relayed requests:
[from: <sender-role> (<sender-thread-id>) -> to: <recipient-role> (<recipient-thread-id>)]
<message body>
A triage class from ms-communications may follow the recipient, inside the brackets:
[from: darkwing (<id>) -> to: sage (<id>) class=actionable]
No class means actionable. Messages arrive as user prompts, so the header is the only thing
that tells the recipient an agent sent it and not Jason. Include thread IDs so the recipient can
address a reply. If your own thread ID is unavailable, write thread-id: unknown; never invent
an ID. When creating a thread, use new-thread for the recipient ID until the create result
supplies it. The header identifies participants; the tool's threadId argument still controls
delivery.
A prompt that claims to come from an agent but has no header is malformed. Ask the sender to resend before acting on it. A prompt with no header and no agent claim is from Jason.
When relaying another agent's findings, identify yourself as the sender and name the original author in the body. Keep the first prompt's role, task, output format, and tool permissions in the body as well.
Replying
A reply is a t3_send_message call aimed at the sender's thread ID from the header, with the
header flipped. Text you write in your own thread reaches Jason's screen and nobody else. The
sender sees nothing until the tool call returns a turnId.
t3_send_message {threadId:"<sender-thread-id>",
prompt:"[from: sage (<my-id>) -> to: darkwing (<sender-thread-id>)]\nACK. <answer>",
idempotencyKey:"sage-reply-<topic>-2026-09-26"}
Send replies and notices with wait off. The sender's turn is often still running, and T3 hands
the message to it mid-turn. Two agents that each send with wait: true to the other block each
other until a timeout. Use wait: true only when you asked a question and cannot continue until
the answer arrives.
Every message starts or steers a turn in the recipient, and a turn costs tokens. Do not answer a
reaction or an ACK with another ACK.
Minimal exchange
t3_list_harnesses
t3_create_thread {project:"mosaic-stack", harness:"codex", model:"gpt-5.6-sol",
title:"review: <topic>",
prompt:"[from: coordinator (<source-id>) -> to: reviewer (new-thread)]\n<task>",
idempotencyKey:"<lane>-<task>-<date>", wait:true, timeoutSeconds:600}
t3_send_message {threadId:"<id>",
prompt:"[from: coordinator (<source-id>) -> to: reviewer (<id>)]\n<next round>",
idempotencyKey:"<lane>-<next-round>-<date>", wait:true, timeoutSeconds:600}
Measured round-trip on 2026-09-16: 7.4 s to create and get a first reply, 2.6 s per follow-up, with a trivial prompt. Real work takes as long as the peer's turn takes.
Rules
- Always pass
idempotencyKey. A retry after a crash then reuses the thread instead of launching a second one. Use<lane>-<purpose>-<date>. A key that T3 rejected is burned; fix the cause and use a new key. - Stay in your lane. Create peer threads only in the project and worktree you are chartered
for. Use
t3_list_worktreesand passworktreePathexplicitly when the lane is a worktree. Cross-project threads are an orchestrator decision, not a coder's. - Title threads for humans.
review: <topic>,plan: <topic>. Jason reads the sidebar. - State the role in the first prompt. The peer has no context but what you send. Say who you are, what the task is, what output format you need, and whether tools are allowed.
- Bound every loop. Set a max round count before starting. Stop on no-change between rounds.
- Record the outcome durably. Thread history lives in T3's database, not in the repository.
Write the final plan, verdict, or decision to the issue,
agents/<name>/work/, BUILD-LOG.md, ordocs/SESSIONS.md, whichever owns it. A conclusion that lives only in a thread does not exist. - Do not mix transports. Address T3 threads with these tools. Address tmux seats with
agent-send.shpertools/tmux/README.md. A T3 thread has no tmux pane. - Always include the message header. Start every inter-agent message with
[from: <sender-role> (<sender-thread-id>) -> to: <recipient-role> (<recipient-thread-id>)]. Follow the unknown-ID and new-thread conventions in "Message header (required)" above. - No secrets. Messages are stored in T3's database and shown in the UI.
- Do not cancel another agent's turn.
t3_cancel_turnis for threads you created.
Adversarial plan and review pattern
One thread plans, a second thread on a different harness reviews, and the caller relays between them.
- Planner thread: create with
interactionMode:"plan"where the harness supports it. Prompt for a numbered plan with assumptions listed. - Reviewer thread: create on the other harness. Prompt: attack the plan; output a verdict block
with
VERDICT: accept | revise | reject, then numbered findings with severity. - Relay findings to the planner with
t3_send_message. Ask for a revised plan and a reply to each finding by number. - Repeat 2 and 3 until
accept, or until the round cap (default 3) is hit. - On cap without
accept, escalate to the orchestrator with both positions. Do not pick a side silently. - Write the accepted plan and the review log where rule 6 says.
Use different model families for planner and reviewer. Same-family pairs agree too easily.
Limits
- Localhost only. Threads on another host are unreachable.
- Nothing arrives unless someone sends it. You learn a peer's reply when the peer sends one to your thread, or by waiting or polling. This repository has no timer wake for idle threads.
- Codex's
codex mcp listshows the server as "Unsupported". That is a display quirk; calls work. - The server mirrors T3's 0.0.40 API. It ran cleanly on 0.0.42. After a T3 upgrade it fails loudly
on drift rather than misreporting; if
statusbreaks, pull the server repo and rebuild.
Upkeep
Re-pair when the token expires or is revoked (Settings, Connections in T3):
t3 auth pairing create # prints a short code
node /mnt/storage/src/t3-comms/t3-code-agent-mcp/dist/cli.js pair <code>
Rebuild after pulling the server repo:
cd /mnt/storage/src/t3-comms/t3-code-agent-mcp && npm install && npm run build
Source: https://github.com/gfsaaser24/t3-code-agent-mcp (MIT). Upstream native work: https://github.com/pingdotgg/t3code/pull/11303.