Skip to content

htekdev/agent-mesh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

agent-mesh

Cross-session agent communication for GitHub Copilot CLI — lightweight IPC mesh using SQLite.

License: MIT

The Problem

Each GitHub Copilot CLI session runs in complete isolation. If you have terminals open in different repos — say a frontend app and a backend API — those agents can't talk to each other. There's no built-in way for one session to ask another session a question, delegate work, or share results.

agent-mesh bridges that gap. It gives every Copilot CLI session the ability to discover peers and exchange messages across sessions, using nothing but a shared SQLite database on your machine.

How It Works

┌─────────────────────────────────────────────────────────────────┐
│                        Your Machine                             │
│                                                                 │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐      │
│  │  Terminal 1   │    │  Terminal 2   │    │  Terminal 3   │      │
│  │  my-frontend  │    │  my-api      │    │  infra       │      │
│  │  (Copilot CLI)│    │  (Copilot CLI)│    │  (Copilot CLI)│      │
│  └──────┬───────┘    └──────┬───────┘    └──────┬───────┘      │
│         │                   │                   │               │
│         │    ┌──────────────┴──────────────┐    │               │
│         └────┤   agent-mesh.db (SQLite)    ├────┘               │
│              │   WAL mode · lock-free      │                    │
│              │   ┌─────────────────────┐   │                    │
│              │   │  agent_sessions     │   │                    │
│              │   │  agent_messages     │   │                    │
│              │   └─────────────────────┘   │                    │
│              └─────────────────────────────┘                    │
└─────────────────────────────────────────────────────────────────┘
  1. Auto-registration: When the extension loads, it immediately registers the session in the agent_sessions table with its workspace name (derived from the git repo root). This happens at load time — not when the first user message arrives — so the agent is visible in the mesh right away.
  2. Heartbeat: Every 10 seconds, each session updates its heartbeat. Sessions with no heartbeat for 10+ minutes are marked stopped.
  3. Message passing: Sessions insert messages into agent_messages. The recipient's polling loop picks them up and routes them via session.send() for the LLM to process.
  4. Threading: Replies are linked to original messages via original_message_id. Follow-up messages within 10 minutes are auto-threaded.
  5. Cleanup: Read messages older than 24 hours are purged. Unread messages to stopped sessions older than 24 hours are also purged.

Installation

Prerequisites

  • GitHub Copilot CLI installed and working
  • Node.js 22+ (required for node:sqlite / DatabaseSync)

Setup

  1. Create the extension directory:

    mkdir -p ~/.copilot/extensions/agent-mesh
  2. Copy the extension file:

    cp extension.mjs ~/.copilot/extensions/agent-mesh/extension.mjs

    Or clone this repo directly:

    git clone https://github.com/htekdev/agent-mesh.git ~/.copilot/extensions/agent-mesh
  3. Restart your Copilot CLI sessions. The extension loads automatically.

That's it. No npm install, no config files, no environment variables. The SQLite database is created automatically at ~/.copilot/extensions/agent-mesh/agent-mesh.db.

Verify

After restarting, you should see in the CLI output:

🌐 Agent mesh: registered as "my-repo" — polling every 10s (early registration)

And the get_agents, send_message, reply_to_message, and get_message tools will be available.

Tools

get_agents

List all sessions registered in the mesh.

Parameter Type Required Description
status string No Filter: active, stopped, or all (default: all)

Example output:

🌐 Agent Mesh — 3 session(s):

🟢 my-frontend ← YOU
   Session: abc-123-...
   Repo: my-frontend
   Status: active
   Description: React frontend application

🟢 my-api
   Session: def-456-...
   Repo: my-api
   Status: active
   Description: Express API server

⚫ infra
   Session: ghi-789-...
   Repo: infra
   Status: stopped
   Last heartbeat: 2026-05-01T10:00:00Z

send_message

Send a message to another session. Target by workspace name (preferred) or session ID.

Parameter Type Required Description
workspace string No* Target workspace/repo name (e.g., my-api)
recipient_session_id string No* Target session ID (from get_agents)
content string Yes The message to send (max 10KB)
priority string No low, normal (default), high, or urgent

* Must provide either workspace or recipient_session_id.

Example:

send_message(workspace="my-api", content="What endpoints handle user authentication?", priority="normal")

reply_to_message

Reply to a received message. Creates a threaded response.

Parameter Type Required Description
message_id integer Yes The message ID to reply to
content string Yes Your reply (max 10KB)
priority string No Override priority (default: same as original)

get_message

Retrieve a specific message and any replies to it.

Parameter Type Required Description
message_id integer Yes The message ID to retrieve

Usage Examples

Cross-repo debugging

You're working in my-frontend and hit an API error. Ask the API agent:

User: "I'm getting a 403 on /api/users. Can you ask the API agent what middleware checks that route?"

→ send_message(workspace="my-api", content="What authentication middleware guards the /api/users endpoint? The frontend is getting 403 errors.")

The API session receives the message, analyzes its codebase, and sends a reply back through the mesh.

Multi-repo coordination

You need to update a shared type definition that both repos use:

User: "Tell the API agent to update the UserProfile type to include avatarUrl"

→ send_message(workspace="my-api", content="Please add 'avatarUrl: string' to the UserProfile type in src/types/user.ts. The frontend needs it for the new profile page.", priority="high")

Checking message status

After sending a message, check if you got a reply:

→ get_message(message_id=42)

📨 Message 42:
From: my-frontend
Content: What auth middleware guards /api/users?

📩 Replies (1):
  [43] my-api: The /api/users endpoint uses authMiddleware from src/middleware/auth.ts...

Architecture

Database Schema

agent_sessions — Registry of all CLI sessions

Column Type Description
session_id TEXT (PK) UUID assigned per CLI session
agent_name TEXT Workspace/repo name
agent_description TEXT First line of .github/copilot-instructions.md
cwd TEXT Working directory
repo TEXT Git repo folder name
status TEXT active or stopped
registered_at TEXT ISO 8601 timestamp
last_heartbeat TEXT ISO 8601 timestamp
metadata TEXT Reserved for future use

agent_messages — Message queue

Column Type Description
message_id INTEGER (PK) Auto-incrementing ID
sender_session_id TEXT (FK) Sender's session ID
recipient_session_id TEXT (FK) Recipient's session ID
content TEXT Message body (max 10KB)
original_message_id INTEGER (FK) Parent message for threading
priority TEXT low, normal, high, urgent
created_at TEXT ISO 8601 timestamp
read INTEGER 0 = unread, 1 = read
read_at TEXT When the message was read
expires_at TEXT Optional TTL

Message Flow

Sender                          SQLite DB                        Recipient
  │                                │                                │
  │  INSERT INTO agent_messages    │                                │
  │  ─────────────────────────────>│                                │
  │                                │    Poll (every 10s)            │
  │                                │<───────────────────────────────│
  │                                │    SELECT unread WHERE         │
  │                                │    recipient = me              │
  │                                │───────────────────────────────>│
  │                                │                                │
  │                                │    session.send(prompt)        │
  │                                │    ─── LLM processes ───      │
  │                                │                                │
  │                                │    Mark read                   │
  │                                │<───────────────────────────────│
  │                                │                                │
  │                                │    INSERT reply                │
  │                                │<───────────────────────────────│
  │    Poll picks up reply         │                                │
  │<───────────────────────────────│                                │

Safety Features

  • Rate limiting: Max 10 messages between any pair of sessions within 60 seconds
  • Message size limit: 10KB per message
  • Self-message prevention: Cannot send messages to yourself
  • Stale session cleanup: Sessions with no heartbeat for 10+ minutes are marked stopped
  • Message TTL: Read messages are purged after 24 hours
  • Queue depth warning: Console warning when >50 unread messages queue up
  • Graceful degradation: If node:sqlite is unavailable (Node < 22), the extension loads as a no-op stub with a clear error message

Configuration

The extension has sensible defaults and requires no configuration. If you need to customize, edit these constants at the top of extension.mjs:

Constant Default Description
POLL_INTERVAL_MS 10000 How often to check for messages (ms)
MAX_MESSAGES_PER_POLL 5 Max messages processed per poll cycle
MAX_MESSAGE_SIZE 10240 Max message content size (bytes)

The database path is automatically set to agent-mesh.db in the same directory as the extension file.

Hooks

The extension uses Copilot CLI extension hooks for lifecycle management and context injection:

Hook Purpose
(load time) Auto-register, clean stale sessions, start polling (runs immediately when extension loads)
onSessionStart Re-register if cwd differs from initial detection, inject peer list into context
onSessionEnd Stop polling, mark session as stopped
onPostToolUse Inject contextual guidance after tool calls (e.g., "don't wait for replies, they arrive automatically")

Addressing

Sessions can be addressed in two ways:

  1. By workspace name (recommended): send_message(workspace="my-api", ...) — resolves to the most recently active session in that workspace. Stable across restarts.
  2. By session ID: send_message(recipient_session_id="abc-123-...", ...) — exact targeting. Session IDs change every time the CLI restarts.

The workspace name is derived from the git repository root folder name. If you have ~/repos/my-api/.git, the workspace is my-api.

Agent Description

Each session's description is auto-derived from the first non-empty line of .github/copilot-instructions.md in the repo. This helps other agents understand what each session does when they call get_agents.

If no instructions file exists, the workspace name is used as the description.

Limitations

  • Single machine only: The SQLite database is local — no network/cloud support. All sessions must run on the same machine.
  • No authentication: All sessions on the same machine are trusted equally. This is a single-user tool.
  • No persistence across reboots: Session registrations are cleaned up when sessions stop. Messages older than 24h are purged.
  • Node.js 22+ required: Uses node:sqlite (DatabaseSync) which is only available in Node 22+.

FAQ

Q: Do I need to install any npm packages? No. The extension uses only Node.js built-in modules (node:sqlite, node:path, node:fs, node:url) and the @github/copilot-sdk/extension package that ships with Copilot CLI.

Q: What happens if the recipient session is offline? The message is stored in the database. If the session comes back online within 24 hours, it will pick up the message. After 24 hours, unread messages to stopped sessions are purged.

Q: Can I use this with agents in different programming languages? Yes — each Copilot CLI session is language-agnostic. The mesh doesn't care what language the repo uses. A Python project's agent can talk to a TypeScript project's agent.

Q: How do I see who's online? Use the get_agents(status="active") tool in any Copilot CLI session.

Q: What if two terminals are open in the same repo? The workspace-based addressing (send_message(workspace="...")) resolves to the most recently active session. If you need to target a specific terminal, use recipient_session_id instead.

Contributing

Issues and PRs welcome. This is a simple, single-file extension — the entire implementation is in extension.mjs.

License

MIT

About

Cross-session agent communication for GitHub Copilot CLI — lightweight IPC mesh using SQLite

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors