TL;DR Claude Code sessions on the same machine can now list and message each other. That is the right tool for a session already mid-task, and the wrong one for starting a new task: a fresh session takes about ninety seconds to bind the socket that makes it addressable, and the message then queues behind the first-run trust dialog. To hand a side quest to a new session, launch it with the prompt already attached instead. I tested the quoting and I have listed where it breaks.
I was three hours into a secret rotation when a side quest turned up. Listing pods for something unrelated, two failed cronjob pods scrolled past, minutes apart, same job ID. A retry loop in a batch job that had nothing to do with what I was doing.
This is the moment I usually get wrong. The task is worth looking at, it is not worth derailing the session I am in, and the context I would need to explain it is already in front of me. A subagent handles the isolation but I lose sight of it: no terminal to watch, no way to steer it halfway through, and the answer arrives as a wall of text whenever it finishes.
A second terminal tab, already briefed, would have covered it.
The feature I reached for first Link to heading
Cross-session messaging landed in Claude Code recently. Sessions on the same machine can discover each other and pass text: ListAgents to see who is reachable, SendMessage to write to one by name. Each session binds a unix socket under /tmp/cc-socks/, and messages travel over it without touching anyone’s servers.
My first attempt failed in a way that took a while to understand, because the tools half-existed. SendMessage was there. ListAgents was not. Every name I tried came back “no agent named X is reachable”, which reads like a typo rather than a missing capability.
The reason was mundane. SendMessage does double duty: it talks to subagents inside a session as well as to peer sessions, so it shows up regardless. ListAgents is the peer-discovery half, and it appears only when the session has an inbox. Mine did not. The process had been running for sixteen days, since before the feature existed, and a long-lived session never picks it up. The installed binary was current; the running one was not.
The tell is two environment variables:
echo "${CLAUDE_CODE_MESSAGING_SOCKET:-<unset>}"
ls /tmp/cc-socks/
Every other session on my machine had a socket in that directory. Mine had none. Restarting the session fixed it, and /status then showed a Peer address row.
The part I got wrong anyway Link to heading
With messaging working, I opened a new tab, started a bare session, and messaged it the brief. It sat there doing nothing.
Two things were in the way. A brand-new session takes a while to bind its socket, so for the first ninety seconds or so it is invisible to ListAgents and unaddressable. I checked too early, concluded it had failed, and checked again only after poking at the process list.
Then, once it was addressable, the message was delivered into a session that was itself blocked on the first-run “trust this directory” dialog. The status field says so if you look:
{ "name": "side-quest", "status": "waiting", "waitingFor": "dialog open" }
So the brief was queued behind a prompt only a human could clear. I had built a delivery mechanism that needed me to walk over and click something before it delivered anything.
Cross-session messaging is for sessions that already exist and are already working. Using it to start work is backwards.
Attaching the prompt at launch Link to heading
The simpler thing is to pass the prompt as the session’s first turn. Write the brief to a file, then launch with it:
claude --name side-quest "$(cat /tmp/brief.md)"
The naive way to drive that from a script is to inline it into the AppleScript that opens the tab, which dies on the first double quote or backtick in the brief. A launcher script sidesteps the nesting:
cat > /tmp/launch.sh <<'EOF'
#!/bin/bash
# $1 = working dir, $2 = session name, $3 = brief file
cd "$1" || exit 1
exec claude --name "$2" "$(cat "$3")"
EOF
chmod +x /tmp/launch.sh
osascript <<APPLESCRIPT
tell application "iTerm2"
activate
tell current window
create tab with default profile
tell current session
write text "/tmp/launch.sh '$HOME/code/some-repo' 'side-quest' '/tmp/brief.md'"
end tell
end tell
end tell
APPLESCRIPT
I did not trust the quoting, so I tested it with a brief written to be hostile: double quotes, backticks, $HOME, ${PATH}, a Windows path with backslashes, an apostrophe, and blank lines. It arrived byte-identical as the first user turn. "$(cat file)" passes the content as a single argument and does not re-expand it.
The --name matters more than it looks. It is the address other sessions use later, so a session launched without one gets a name derived from its folder, something like some-repo-3f, and you end up guessing. Naming it up front means I can message it mid-task, which is what messaging is for.
Where it breaks Link to heading
Worth knowing before relying on it:
- Untrusted working directory. The trust dialog blocks the prompt just as it blocked the message. Launching into a directory the machine has seen before avoids it; otherwise expect one click.
- No iTerm window open.
create tabhas nothing to attach to. Usecreate window with default profileas the fallback. - Very large briefs. The prompt becomes one argument, so it is bounded by
ARG_MAX, about a megabyte here. Keep the brief to a page and point at files for the detail. - Paths with spaces. Single-quote each argument in the
write textline. claudenot on the tab’s PATH. The launcher fails inside the tab with nothing on screen, so confirm withpgrep -f "claude --name side-quest"rather than assuming.
Check it worked rather than assuming. pgrep and ls -lt /tmp/cc-socks/ are reliable straight away; ListAgents lags behind. To confirm the prompt landed, and not just that the session started, read the newest transcript under ~/.claude/projects/<slug>/ and look at its first user turn.
What I ended up with Link to heading
Two mechanisms, for two different moments.
To start a side quest, launch a named session with the brief attached. It gets its own tab I can watch and interrupt, its own working directory, and none of my current context. Writing the brief is the useful part anyway: it forces me to say what I saw, which in this case was two pod names, a timestamp and an explicit note that the two adjacent theories I had were context rather than conclusions.
To talk to a session already running, use SendMessage. When one session learns something another needs, it can say so without me copying text between terminals.
I have been doing this for a day, so I do not know yet whether I will end up with six tabs of half-finished side quests. The rotation I was working on is still not finished.