npm package: @m64/nats-pi-channel
Description: NATS messaging channel for PI Agent. Discoverable, addressable, streaming AI agent communication over NATS.
Author: m64
Latest version: 0.1.1

# @m64/nats-pi-channel

NATS messaging channel for [PI Agent](https://github.com/badlogic/pi-mono). Makes every PI coding agent session discoverable and addressable on a NATS network. Remote users or other agents can send prompts via NATS request/reply and receive the agent's response as streaming chunks.

This is the PI sibling of [`@m64/nats-channel`](https://www.npmjs.com/package/@m64/nats-channel) (OpenClaw). The implementations share the same wire protocol, so callers can address any agent runtime uniformly.

## How it works

When installed, PI loads this extension on every session start. The extension:

1. Connects to NATS using a configured context (or `demo.nats.io` by default).
2. Registers a `pi-channel` microservice instance with an endpoint on `agents.pi.<owner>.<session>`.
3. Subscribes to `agents.pi.<owner>.<session>.inspect` for discovery metadata.
4. When a request arrives, injects the body into PI as a user message via `pi.sendUserMessage()` and forwards the agent's streaming text output back to the caller's reply subject. Empty payload signals end-of-stream.
5. Shows connection status in PI's footer.

Multiple PI sessions register as instances of the same `pi-channel` service — `nats micro info pi-channel` aggregates across every running PI session.

## Install

```bash
# From npm (once published)
pi install npm:@m64/nats-pi-channel

# From a local clone during development
pi install /absolute/path/to/nats-pi-channel
```

Then start PI normally:

```bash
pi
```

You should see a notification like `Connected to NATS on agents.pi.<you>.<project>` and a footer status `NATS: agents.pi.<you>.<project>`.

## Configure

Config lives at `~/.pi/agent/nats-channel.json`:

```json
{
  "context": "my-nats-context",
  "sessionName": "my-session",
  "org": "acme"
}
```

| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `context` | no | `demo.nats.io` | Name of a NATS CLI context in `~/.config/nats/context/` |
| `sessionName` | no | sanitized basename of CWD | Override the session segment in the subject |
| `org` | no | — | Optional namespace for subject isolation on shared servers |

Environment variables override the file:
- `NATS_CONTEXT` — select a NATS CLI context
- `NATS_SESSION_NAME` — override the session name

### In-PI commands

- `/nats-status` — show current subject, service, and pending/queued request counts
- `/nats-configure` — print current config
- `/nats-configure <context>` — switch NATS context
- `/nats-configure session <name>` — override session name
- `/nats-configure session clear` — revert to CWD basename
- `/nats-configure org <name>` — set org namespace
- `/nats-configure org clear` — drop org segment

Changes take effect after restarting PI.

## Subject naming

```
agents.pi.<owner>.<session>              # default
agents.pi.<org>.<owner>.<session>        # when org is set
```

- `owner`: sanitized `$USER`
- `session`: sanitized basename of CWD, overridable
- If two sessions would collide (same dir on the same owner+org), the second one auto-suffixes `-2`, `-3`…

## Usage — talking to a running PI session

From any machine that can reach the same NATS server:

```bash
# Discover running PI sessions
nats micro list
nats micro info pi-channel

# Get inspection metadata for one session
nats req agents.pi.<owner>.<session>.inspect "" --timeout 5s

# Send a prompt (plain text)
nats req agents.pi.<owner>.<session> "What files are in the current directory?" --wait-for-empty --timeout 120s

# Or with a JSON envelope that identifies the sender
nats req agents.pi.<owner>.<session> '{"from":"alice","body":"What files are here?"}' --wait-for-empty --timeout 120s
```

The response streams back as chunks on the reply subject. An empty payload signals the end of the stream.

## Wire protocol

- **Inbound**: plain text OR a JSON envelope `{"from":"sender","body":"text"}`.
- **Outbound**: streaming text chunks published to the caller's reply subject.
- **Completion**: empty payload on the reply subject signals end-of-stream.
- **Discovery**: `nats micro list` / `nats micro info pi-channel`.
- **Inspect**: `agents.pi.<owner>.<session>.inspect` → JSON with capabilities.

This matches the wire protocol of `@synadia/nats-channel` (OpenClaw) — callers can address any of these agent runtimes uniformly.

## Concurrency

Each PI session processes one NATS request at a time. Additional requests queue until the agent is idle. The user's local TUI input and inbound NATS prompts share the same agent session.

## Troubleshooting

- **`NATS: disconnected` in footer:** the extension failed to connect. Check `/nats-status`, verify the context file at `~/.config/nats/context/<context>.json`, and make sure the NATS server is reachable.
- **`NATS: reconnecting…`:** the connection dropped and is retrying. It will restore automatically.
- **Collision: my session got a `-2` suffix.** Another PI session in the same directory (same owner, same org) is already running. Use `/nats-configure session <name>` to pick a different name.
- **`nats req` returns nothing:** make sure you pass `--wait-for-empty`, since the protocol uses empty payload for end-of-stream rather than a single response.

## License

MIT