← Back to Plugins
Tools

Pulse

justfeltlikerunning By justfeltlikerunning 👁 68 views ▲ 0 votes

⚡ Real-time WebSocket messaging hub for AI agent fleets — built for OpenClaw, works anywhere

GitHub

Install

npm install

#

Configuration Example

{
  "agent": "my-agent",
  "port": 18800,
  "apiPort": 18801,
  "peers": {
    "agent-a": { "ip": "10.0.0.10", "port": 18800, "token": "secret-a" },
    "agent-b": { "ip": "10.0.0.11", "port": 18800, "token": "secret-b" }
  },
  "groups": {
    "all": ["agent-a", "agent-b"]
  }
}

README

# agent-pulse

Real-time WebSocket messaging hub for AI agent fleets.

Each agent runs one instance of this hub. Hubs maintain persistent bidirectional WebSocket connections to all peer agents. Messages flow in sub-millisecond time instead of HTTP round-trips.

## Architecture

- WebSocket server on port 18800 (configurable) - accepts inbound connections from peers
- WebSocket clients dialing all known peers in the registry
- HTTP POST fallback when WebSocket connection is down
- REST API on port 18801 for CLI tools and local integrations
- SSE stream of all incoming messages (for dashboards and bridges)
- Conversation state stored on disk in `state/conversations/`
- Audit log written to `logs/pulse-audit.jsonl`

## Quick Start

```bash
npm install

# Copy and edit the example config
cp config/agent-registry.json.example config/agent-registry.json
# Edit agent name, port, peers, tokens

node src/hub.js
```

## Configuration

`config/agent-registry.json`:

```json
{
  "agent": "my-agent",
  "port": 18800,
  "apiPort": 18801,
  "peers": {
    "agent-a": { "ip": "10.0.0.10", "port": 18800, "token": "secret-a" },
    "agent-b": { "ip": "10.0.0.11", "port": 18800, "token": "secret-b" }
  },
  "groups": {
    "all": ["agent-a", "agent-b"]
  }
}
```

The `token` for each peer is the shared secret used during the WebSocket identity handshake. Both sides must have matching tokens for the connection to authenticate.

## Message Envelope

All messages use this JSON format:

```json
{
  "protocol": "pulse/1.0",
  "id": "msg_<uuid>",
  "conversationId": "conv_<uuid>",
  "from": "my-agent",
  "to": "agent-a",
  "type": "request",
  "timestamp": "2026-02-27T10:00:00.000Z",
  "payload": {
    "subject": "optional subject",
    "body": "message body"
  }
}
```

Use `to: "*"` for broadcast to all peers.

## REST API (port 18801)

| Method | Path | Description |
|--------|------|-------------|
| GET | /status | Hub status, connected peers, latency |
| GET | /peers | Peer list with connection state |
| POST | /send | Send a message (JSON body: `{ to, type, payload }`) |
| GET | /events | SSE stream of all incoming messages |
| GET | /conversations | List all conversations |
| GET | /conversations/:id | Get conversation state |
| POST | /conversations | Create a new conversation |

## CLI

```bash
# Install globally (optional)
npm link

pulse status
pulse peers
pulse send agent-a request "Hello from CLI"
pulse broadcast "Attention all agents"
pulse conversations
```

## Connection Lifecycle

1. Hub starts and reads `config/agent-registry.json`
2. Dials outbound WebSocket to each peer
3. On connect, sends identity message: `{ type: "identify", agent, token }`
4. Peer authenticates and acknowledges
5. Heartbeat ping/pong every 30s keeps connections alive
6. On disconnect, reconnects with exponential backoff (1s, 2s, 4s ... 30s max)
7. If WebSocket unavailable, falls back to HTTP POST

## Conversation State

Conversations are stored as JSON files in `state/conversations/<id>.json`:

```json
{
  "conversationId": "conv_abc123",
  "type": "request",
  "participants": ["my-agent", "agent-a"],
  "status": "active",
  "createdAt": "2026-02-27T10:00:00.000Z",
  "rounds": [
    {
      "round": 1,
      "from": "my-agent",
      "to": "agent-a",
      "type": "request",
      "timestamp": "2026-02-27T10:00:00.000Z",
      "payload": { "body": "Hello" }
    }
  ]
}
```

Status values: `active`, `parked`, `complete`, `timeout`

## Audit Log

All messages are appended to `logs/pulse-audit.jsonl`, one JSON object per line:

```json
{"timestamp":"2026-02-27T10:00:00.000Z","id":"msg_xxx","from":"my-agent","to":"agent-a","type":"request","conversationId":"conv_xxx","status":"delivered","latencyMs":2}
```

## Running Tests

```bash
npm test
# or
node test/test-hub.js
```

The test suite starts two in-process hub instances on ephemeral ports and verifies end-to-end messaging, broadcast, reconnect, audit logging, and the REST API.

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| PULSE_REGISTRY | config/agent-registry.json | Path to registry config |
| PULSE_AUDIT_LOG | logs/pulse-audit.jsonl | Audit log file path |
| PULSE_CONV_DIR | state/conversations | Conversation state directory |
| PULSE_AGENT_NAME | hub | Agent name (fallback if no config) |
| PULSE_WS_PORT | 18800 | WebSocket server port (fallback) |
| PULSE_API_PORT | 18801 | REST API port (fallback) |

## Systemd Service

```ini
[Unit]
Description=agent-pulse WebSocket Hub
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/agent-pulse
ExecStart=/usr/bin/node src/hub.js
Restart=always
RestartSec=5
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
```

## Related Projects

- **[agent-pulse](https://github.com/justfeltlikerunning/agent-pulse)** — Framework-agnostic version. Works with CrewAI, LangGraph, AutoGen, LlamaIndex, and any language.
- **[openclaw-mesh](https://github.com/justfeltlikerunning/openclaw-mesh)** — Structured messaging protocol with retry, circuit breaking, and HMAC signing.
- **[agent-mesh](https://github.com/justfeltlikerunning/agent-mesh)** — Framework-agnostic version of openclaw-mesh.
- **[PulseNet](https://github.com/justfeltlikerunning/pulsenet)** — Multi-agent chat interface built on agent-pulse.
- **[OpenClaw](https://github.com/openclaw/openclaw)** — AI agent runtime with native pulse extension support.
tools

Comments

Sign in to leave a comment

Loading comments...