orfloat

ORF-R-2026-001 · Tool

cc-dm: peer-to-peer messaging between Claude Code sessions

The higher-order prompt: Claude prompting Claude.

Finding

A 500ms poll over a shared SQLite bus delivers session-to-session messages as native context events. 140 tests, zero network.

Isn’t it about time Claude prompted Claude inside Claude Code? That question is the whole origin of this. A higher-order function takes a function as its argument; we wanted the higher-order prompt, one session directing another instead of a human relaying context between terminals. cc-dm (claude code direct messaging) is the experiment it became: parallel sessions take on the roles of an engineering org and pass context directly.

What we built

A Claude Code plugin that lets any session direct-message any other session on the same machine. Messages arrive as native <channel> events inside the receiving session’s context window, within roughly 500ms, over the Claude Code Channels protocol. It ships as an installable plugin and an npm package, MIT licensed, and was reviewed and published through Anthropic’s official Claude Code plugin submission process (published 22 Mar 2026).

Method

No daemon, no ports, no network. Each session spawns a channel server over stdio; every server connects to one shared SQLite database (WAL mode) and polls it on a fixed interval.

  Session A (planner)  ──┐
  Session B (backend)  ──┼──→  ~/.cc-dm/bus.db  (SQLite WAL)
  Session C (tests)    ──┘          ↑
                               500ms poll per session
                               → <channel> event pushed into context
Figure 1. the shared bus. parallel Claude Code sessions write to and poll one SQLite file; no daemon, no ports, no network.
  session A                 bus.db                  session B
     │                        │                        │
     │  dm to B               │                        │
     ├─── write a row ───────▶│                        │
     │                        │◀─── poll, every 500ms ─┤
     │                        │─── row, marked read ──▶│
     │                        │                        ├─▶ <channel> event
     │                        │                        │   in B's context
Figure 2. one message’s life. a dm writes a row; B’s 500ms poll pulls it and marks it delivered, surfacing it as a native channel event in B’s context window.

The design splits into small, independently tested units: the message bus (read/write, delivery marking, schema migration), session heartbeat and liveness, permission relay (opt-in remote tool approval across sessions), input sanitization, and the MCP tools surface. Message metadata (priority, type, thread id) is stored as JSON and spread before the routing fields on delivery, so user data cannot spoof the routing envelope.

The guarantee is one object literal. On delivery the sender’s own metadata is spread first, then the real routing fields are written over it, so a message cannot forge who it is from or who it is for.

// the delivered <channel> event: spread the sender's meta first, then stamp the
// real routing fields over it, so from/to cannot be forged.
await server.notification({
  method: "notifications/claude/channel",
  params: {
    content: message.content,
    meta: {
      ...message.meta,
      from_session: message.from_session,
      to_session: sessionName,
      message_id: String(message.id),
      sent_at: message.created_at,
    },
  },
});

Findings

The suite is 140 tests, zero failing, 296 assertions, across six independently tested units:

  tools        55  ██████████████████
  bus          35  ███████████
  permission   20  ███████
  integration  17  ██████
  heartbeat     8  ███
  sanitize      5  ██
Figure 3. test coverage by unit. the MCP tools surface carries the most, the sanitizer the least.
  • The whole transport is a shared file and a poll loop: it reached a working v1.0.0 the day after the first commit, then 8 tagged releases (v0.1.0 through v1.3.1) hardened it: meta attributes, permission relay, ghost-name theft protection.
  • A local-only, file-backed bus sidesteps an entire class of failure modes (ports, sockets, auth, a daemon to supervise). The one deliberate trade is non-atomic read-then-mark in the bus: on a crash between the two, a message re-delivers rather than vanishes, the right default for a local tool.

Meaning

cc-dm is a small, sharp instance of the lab’s working thesis: multi-agent orchestration is becoming ordinary, and the plumbing between agents should be as boring and reliable as a Unix pipe. The interesting move is subtractive: choosing a SQLite file and a 500ms poll over anything that needs a server.