Skip to content
← Back to Articles

Agent Mesh: How I Made My Copilot CLI Sessions Talk to Each Other

· 7 min read
GitHub Copilot Multi-Agent Systems Open Source Automation AI

The Problem: Every Session Is an Island

If you run GitHub Copilot CLI in multiple terminals — say one for a frontend repo, one for an API, and another for infrastructure — those sessions have no idea the others exist. Each one is completely isolated. No shared context. No way to ask another session a question. No way to delegate work across repos.

I hit this wall the moment my setup grew beyond one terminal. I have a home assistant system managing my family’s daily life in one repo, a work assistant handling Microsoft sales data in another, and a video pipeline processing content in a third. These agents needed to coordinate — my personal calendar needed to block time on my work calendar, my content pipeline needed to notify my home assistant when a video was published, and I needed a single command to ask “who’s online?”

So I built agent-mesh. It’s a single-file Copilot CLI extension that creates a lightweight message bus between sessions using nothing but SQLite. No external dependencies. No config. No server to run. Copy one file and your sessions can talk to each other.

How It Works

The concept is deliberately simple. Every Copilot CLI session that loads the extension automatically registers itself in a shared SQLite database the moment the extension is loaded — before any user message is sent. Each session gets a heartbeat, a workspace name (derived from your git repo), and a polling loop that checks for incoming messages every 10 seconds.

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  Terminal 1   │    │  Terminal 2   │    │  Terminal 3   │
│  my-frontend  │    │  my-api      │    │  infra       │
│  (Copilot CLI)│    │  (Copilot CLI)│    │  (Copilot CLI)│
└──────┬───────┘    └──────┬───────┘    └──────┬───────┘
       │                   │                   │
       └───────────┬───────┴───────────────────┘

          ┌────────┴────────┐
          │  agent-mesh.db  │
          │  SQLite · WAL   │
          └─────────────────┘

That’s it. SQLite in WAL mode handles concurrent reads and writes from multiple processes — readers never block writers. The database has two tables: agent_sessions (who’s online) and agent_messages (the message queue). Messages are polled, routed to the LLM via session.send(), and cleaned up automatically — read messages are purged after 24 hours, and unread messages to stopped sessions expire after 24 hours too.

Setup: One File, Three Steps

This is the kind of thing I want to be dead simple to set up — especially since an AI agent might be the one reading these instructions and doing the setup itself. Here’s the full process:

1. Create the extension directory:

mkdir -p ~/.copilot/extensions/agent-mesh

2. Copy the extension file:

# Clone the repo directly:
git clone https://github.com/htekdev/agent-mesh.git ~/.copilot/extensions/agent-mesh

3. Restart your Copilot CLI sessions.

That’s it. No npm install. No config files. No environment variables. No API keys. The SQLite database is created automatically on first run. When you restart a session, you’ll see:

🌐 Agent mesh: registered as "my-repo" — polling every 10s

The only prerequisite is Node.js 22+ because the extension uses the built-in node:sqlite module — no third-party SQLite bindings needed. Note that node:sqlite is still experimental in Node 22–23, but it works reliably for this workload and the API surface is stable enough for production use.

The Tools

The extension exposes four tools that become available in every session:

get_agents — See Who’s Online

get_agents(status="active")

Returns a list of all registered sessions with their workspace name, status, and description. The description is auto-derived from the first line of each repo’s .github/copilot-instructions.md, so other agents can understand what each session does at a glance.

send_message — Talk to Another Session

send_message(
  workspace="my-api",
  content="What authentication middleware guards the /api/users endpoint?",
  priority="normal"
)

Target by workspace name (stable across restarts) or session ID (exact targeting). Messages support four priority levels: low, normal, high, and urgent. The recipient’s polling loop picks up the message, the LLM processes it with full context of the recipient’s codebase, and a reply comes back through the mesh automatically.

reply_to_message — Threaded Responses

reply_to_message(message_id=42, content="The /api/users endpoint uses JWT validation from src/middleware/auth.ts")

Replies are linked to the original message, creating conversation threads. Follow-up messages within 10 minutes are auto-threaded.

get_message — Check for Replies

get_message(message_id=42)

Retrieves a message and all its replies. Useful for checking if someone answered your question.

What Makes It Powerful

The simplicity is the point. Because the mesh is just SQLite, it’s fast, reliable, and zero-maintenance. But what you can do with cross-session communication is where it gets interesting.

Instant Discovery

A subtle but important detail: agents register the moment the extension loads — not when the first user message arrives. This means if you open three terminals, all three are immediately visible to each other via get_agents() before anyone types a single prompt. Early registration makes the mesh feel alive from the instant you launch your sessions, and it means automated workflows (like cron jobs) can discover and message agents that haven’t received any user input yet.

Multi-Repo Coordination

Working on a full-stack feature? Your frontend agent can ask the backend agent about API contracts, shared types, or auth flows — and get answers grounded in the actual backend code, not guesses:

send_message(
  workspace="my-api",
  content="I need the exact TypeScript type for the UserProfile response from GET /api/users/:id. Can you check src/types/ and give me the interface?"
)

The backend agent reads its own codebase, finds the type definition, and sends it back. No copy-pasting between terminals. No losing context.

Work-Life Calendar Sync

This is the use case that sold me on building the mesh in the first place. My personal home assistant (rocha-family) manages my family calendar. My work assistant (msix-home) manages my Microsoft Outlook calendar. When a doctor’s appointment gets added to Google Calendar, the home assistant can tell the work agent to block that time on Outlook:

send_message(
  workspace="msix-home",
  content="Block 2-3 PM on Thursday as Out of Office — personal appointment. Don't include details."
)

No manual calendar juggling. The agents handle the cross-domain coordination because they can talk to each other.

Content Pipeline Orchestration

When my video pipeline agent finishes processing a video — transcription, captions, clips — it can notify my home assistant to create social media scheduling tasks:

send_message(
  workspace="rocha-family",
  content="Video 'Copilot CLI Extensions Deep Dive' is processed. Transcript and clips are ready. Please create content scheduling tasks for TikTok, YouTube Shorts, and LinkedIn."
)

This is real multi-agent orchestration happening on a single developer machine with zero cloud infrastructure.

Under the Hood: The SQLite Schema

For developers who want to understand (or extend) the internals, here’s the core schema:

CREATE TABLE agent_sessions (
    session_id      TEXT PRIMARY KEY,
    agent_name      TEXT,
    agent_description TEXT,
    cwd             TEXT,
    repo            TEXT,
    status          TEXT DEFAULT 'active',
    registered_at   TEXT NOT NULL,
    last_heartbeat  TEXT NOT NULL,
    metadata        TEXT
);

CREATE TABLE agent_messages (
    message_id          INTEGER PRIMARY KEY AUTOINCREMENT,
    sender_session_id   TEXT NOT NULL,
    recipient_session_id TEXT NOT NULL,
    content             TEXT NOT NULL,
    original_message_id INTEGER,
    priority            TEXT DEFAULT 'normal',
    created_at          TEXT NOT NULL,
    read                INTEGER DEFAULT 0,
    read_at             TEXT,
    expires_at          TEXT
);

Two tables. That’s the entire data model. The extension handles everything else — auto-registration at extension load time, heartbeats every 10 seconds, stale session cleanup after 10 minutes of no heartbeat, rate limiting (max 10 messages per pair per minute), and message purging after 24 hours.

The database runs in WAL mode with busy_timeout = 5000 and synchronous = NORMAL — optimized for concurrent multi-process access where readers never block writers. The extension also creates indexes on the message queue for efficient polling — check the source for the full DDL. SQLite handles this IPC workload trivially.

Safety Built In

This isn’t a toy. The extension includes real safety guardrails:

The Bigger Picture

I’ve been running this mesh in production for weeks now. My home assistant, work assistant, and content pipeline all communicate through it daily. The Copilot CLI extension system makes it possible — extensions are just JavaScript files that get loaded automatically, with full access to the Copilot SDK’s session lifecycle hooks.

What I like most about this pattern is that it’s composable. You don’t need to adopt a framework. You don’t need a cloud service. You don’t need to restructure your workflow. Drop one file into your extensions directory and your existing sessions gain a new superpower. That’s the beauty of building on top of a platform like Copilot CLI — the extension model makes it trivial to add capabilities without touching anything else.

The full source is on GitHub — one file, MIT licensed, zero dependencies. If you’re running Copilot CLI across multiple repos, give it a try. And if you build something cool with it, I’d love to hear about it — find me on LinkedIn or X.


← All Articles