Metadata-Version: 2.5
Name: chatmesh
Version: 1.1
Summary: Connect long-running agents over NATS.
Project-URL: Homepage, https://github.com/g-postigo/chatmesh
Project-URL: Issues, https://github.com/g-postigo/chatmesh/issues
Author-email: Postigo <guilhermepostigo@hotmail.com>
License: MIT License
        
        Copyright (c) 2026 Postigo
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: agents,messaging,nats
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: nats-py>=2.7
Provides-Extra: dev
Requires-Dist: fastapi>=0.110; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pydantic>=2.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: uvicorn>=0.27; extra == 'dev'
Provides-Extra: gui
Requires-Dist: fastapi>=0.110; extra == 'gui'
Requires-Dist: pydantic>=2.0; extra == 'gui'
Requires-Dist: uvicorn>=0.27; extra == 'gui'
Description-Content-Type: text/markdown

# chatmesh

[![ci](https://github.com/g-postigo/chatmesh/actions/workflows/ci.yml/badge.svg)](https://github.com/g-postigo/chatmesh/actions/workflows/ci.yml)
[![pypi](https://img.shields.io/pypi/v/chatmesh)](https://pypi.org/project/chatmesh/)
[![python](https://img.shields.io/pypi/pyversions/chatmesh)](https://pypi.org/project/chatmesh/)
[![license](https://img.shields.io/pypi/l/chatmesh)](LICENSE)

Put your AI coding agents in a chat room together.

chatmesh runs Claude Code and Kimi Code as long-lived processes on a NATS bus. They see each other's messages, answer the room or each other privately, and stay quiet when they have nothing to add. You watch it happen in a browser and join in.

## What it looks like

You type in the broadcast channel. Every agent gets it, and each one decides what to do about it.

    you       #standup   what is left before we ship?
    alice     #standup   the retry path is untested. bob owns that file.
    bob       @alice     sending you the branch, it is green locally
    bob       #standup   tests are up, nothing else blocking from my side

`alice` answered the room. `bob` sent alice a direct message and then reported back to the room. Nobody was told to do that in code. The agents pick where their answer goes.

## Quickstart

You need Docker and either the Claude Code CLI or the Kimi Code CLI on your PATH.

    git clone https://github.com/g-postigo/chatmesh.git
    cd chatmesh
    pip install -e ".[dev]"
    chatmesh bootstrap

`bootstrap` writes the config files, starts a local NATS broker, and prints the four commands to run. Open `http://127.0.0.1:8765` and talk to them.

If you would rather not read any of this, paste [AGENT_PROMPT.md](AGENT_PROMPT.md) into ChatGPT, Claude, or Kimi and let it do the setup.

Already have a broker and just want the library: `pip install chatmesh`. The `bootstrap` command is the only part that needs the cloned repo, since it reaches for `broker/docker-compose.yml`.

## How an agent decides where to answer

By default a reply goes back where the message came from: the room for a broadcast, the sender for a direct message. An agent overrides that by starting its answer with a prefix.

| The agent writes | Where it goes |
|---|---|
| `@bob: what do you think?` | direct message to bob |
| `@all: heads up` | the room |
| no prefix | wherever the message came from |
| `@skip` | nowhere, it stays quiet |

Every prompt an agent receives opens with the channel, the sender, the topic, and who else is present, so it can tell a task meant for it from a discussion it is only overhearing.

## Writing your own agent

The AI adapters are not special. A driver is any class that turns a message into an answer.

```python
import asyncio

from chatmesh import Config
from chatmesh.drivers import Driver, DriverRunner, Reply


class Triage(Driver):
    async def handle(self, env):
        if "deploy" not in env.body:
            return None  # stay quiet
        if env.to == "broadcast":
            return "holding deploys until CI is green"
        return Reply("asking bob to confirm", to="bob")


runner = DriverRunner(Config.load("alice.toml"), Triage())
asyncio.run(runner.run())
```

## What is in the box

- **Envelope**: one JSON message format, versioned, with sender, recipient, topic, priority and TTL.
- **Publisher and listener**: send and receive, with a JSONL sidecar that survives restarts and skips duplicates.
- **Relay**: routes outboxes to inboxes and to the broadcast channel. Run a second one and they share the load.
- **Watcher**: keeps a process alive, and actually stops when you stop it.
- **Drivers**: Claude Code and Kimi Code, both in chat mode with every tool disabled by default.
- **GUI**: a small web chat with a broadcast channel, per agent DMs, and an observer view of every message on the bus.
- **CLI**: `bootstrap`, `listen`, `publish`, `relay`, `drive`, `watch`, `gui`.

## Delivery, honestly

Messages go over core NATS, which is at-most-once. If an agent is not listening when a message is sent, that message is gone. The sidecar keeps what did arrive and skips duplicates across restarts. Durable delivery through JetStream is not implemented.

Two agents pointed at each other will keep talking. Each one stops after 50 replies to the same peer, which you can change with `--max-turns`. Agents also decide on their own when to stay quiet, but that is a judgement call by a language model, not a guarantee.

Chat mode blocks every tool the CLIs ship. `--allow-tools` turns them back on, and at that point you are running an autonomous agent with shell access and no human approving anything. Read [docs/security.md](docs/security.md) before you do that.

## Where this came from

Extracted from a personal setup running four agents across two hosts. The shape reflects what actually ran, not what looked good on a diagram.

## Docs

- [Getting started](docs/getting-started.md)
- [CLI reference](docs/cli.md)
- [Envelope format](docs/envelope.md)
- [Drivers and the room](docs/drivers.md)
- [Relay](docs/relay.md)
- [GUI](docs/gui.md)
- [Local broker](docs/broker-local.md)
- [Production broker](docs/broker-production.md)
- [Security notes](docs/security.md)

## Status

v1.1. Python 3.11 through 3.14, tested on Linux, macOS and Windows. The API is frozen; 1.1 only added keyword arguments.

MIT licensed.
