# Live Status v4

Real-time Mattermost progress updates for OpenClaw agent sessions.

Version 4 replaces the manual v1 live-status CLI with a transparent infrastructure daemon.
Agents no longer need to call `live-status`. The watcher auto-updates Mattermost as they work.

## Architecture

```
OpenClaw Gateway
  Agent Sessions
    -> writes {uuid}.jsonl as they run

  status-watcher daemon (SINGLE PROCESS)
    -> fs.watch recursive on transcript directory (inotify, Node 22)
    -> Multiplexes all active sessions
    -> SessionState map: sessionKey -> { postId, pendingToolCalls, lines[] }
    -> Shared HTTP connection pool (keep-alive, maxSockets=4)
    -> Throttled Mattermost updates (leading edge + trailing flush, 500ms)
    -> Circuit breaker for API failure resilience
    -> Graceful shutdown (SIGTERM -> mark all boxes "interrupted")
    -> Sub-agent nesting (child sessions under parent status box)

  gateway:startup hook
    -> hooks/status-watcher-hook/handler.js
    -> Checks PID file; spawns daemon if not running
    -> Daemon starts automatically with gateway

  Mattermost API
    -> PUT /api/v4/posts/{id} (in-place edits, unlimited)
    -> Shared http.Agent (keepAlive, maxSockets=4)
    -> Circuit breaker: open after 5 failures, 30s cooldown
```

## Install

### Prerequisites

- Node.js 22.x
- OpenClaw gateway running
- Mattermost bot token

### One-command install

```sh
cd /path/to/MATTERMOST_OPENCLAW_LIVESTATUS
bash install.sh
```

This installs npm dependencies and deploys the `gateway:startup` hook.
The daemon starts automatically on the next gateway restart.

### Manual start (without gateway restart)

Set required env vars, then:

```sh
node src/watcher-manager.js start
```

## Configuration

All config via environment variables. No hardcoded values.

### Required

| Variable         | Description                                           |
| ---------------- | ----------------------------------------------------- |
| `MM_TOKEN`       | Mattermost bot token                                  |
| `MM_URL`         | Mattermost base URL (e.g. `https://slack.solio.tech`) |
| `TRANSCRIPT_DIR` | Path to agent sessions directory                      |
| `SESSIONS_JSON`  | Path to sessions.json                                 |

### Optional

| Variable                     | Default                   | Description                                |
| ---------------------------- | ------------------------- | ------------------------------------------ |
| `THROTTLE_MS`                | `500`                     | Min interval between Mattermost updates    |
| `IDLE_TIMEOUT_S`             | `60`                      | Inactivity before marking session complete |
| `MAX_SESSION_DURATION_S`     | `1800`                    | Hard timeout per session (30 min)          |
| `MAX_STATUS_LINES`           | `15`                      | Max lines in status box (oldest dropped)   |
| `MAX_ACTIVE_SESSIONS`        | `20`                      | Concurrent status box limit                |
| `MAX_MESSAGE_CHARS`          | `15000`                   | Mattermost post truncation limit           |
| `HEALTH_PORT`                | `9090`                    | Health endpoint port (0 = disabled)        |
| `LOG_LEVEL`                  | `info`                    | Logging level (pino)                       |
| `PID_FILE`                   | `/tmp/status-watcher.pid` | PID file location                          |
| `CIRCUIT_BREAKER_THRESHOLD`  | `5`                       | Failures before circuit opens              |
| `CIRCUIT_BREAKER_COOLDOWN_S` | `30`                      | Cooldown before half-open probe            |
| `TOOL_LABELS_FILE`           | _(built-in)_              | External tool labels JSON override         |
| `DEFAULT_CHANNEL`            | _none_                    | Fallback channel for non-MM sessions       |

## Status Box Format

```
[ACTIVE] main | 38s
Reading live-status source code...
  exec: ls /agents/sessions [OK]
Analyzing agent configurations...
  exec: grep -r live-status [OK]
Writing new implementation...
  Sub-agent: proj035-planner
    Reading protocol...
    Analyzing JSONL format...
    [DONE] 28s
Plan ready. Awaiting approval.
[DONE] 53s | 12.4k tokens
```

## Daemon Management

```sh
# Start
node src/watcher-manager.js start

# Stop (graceful shutdown)
node src/watcher-manager.js stop

# Status
node src/watcher-manager.js status

# Pass-through via legacy CLI
live-status start-watcher
live-status stop-watcher

# Health check
curl http://localhost:9090/health
```

## Deployment Options

### Hook (default)

The `gateway:startup` hook in `hooks/status-watcher-hook/` auto-starts the daemon.
No configuration needed beyond deploying the hook.

### systemd

```sh
# Copy service file
cp deploy/status-watcher.service /etc/systemd/system/

# Create env file
cat > /etc/status-watcher.env <<EOF
MM_TOKEN=your_token
MM_URL=https://slack.solio.tech
TRANSCRIPT_DIR=/home/node/.openclaw/agents/main/sessions
SESSIONS_JSON=/home/node/.openclaw/agents/main/sessions/sessions.json
EOF

systemctl enable --now status-watcher
```

### Docker

```sh
docker build -f deploy/Dockerfile -t status-watcher .
docker run -d \
  -e MM_TOKEN=your_token \
  -e MM_URL=https://slack.solio.tech \
  -e TRANSCRIPT_DIR=/sessions \
  -e SESSIONS_JSON=/sessions/sessions.json \
  -v /home/node/.openclaw/agents:/sessions:ro \
  -p 9090:9090 \
  status-watcher
```

## Upgrade from v1

v1 required agents to call `live-status create/update/complete` manually.
AGENTS.md contained a large "Live Status Protocol (MANDATORY)" section.

### What changes

1. The daemon handles all updates — no manual calls needed.
2. AGENTS.md protocol section can be removed (see `docs/v1-removal-checklist.md`).
3. `skill/SKILL.md` is now 9 lines: "status is automatic".
4. `live-status` CLI still works for manual use but prints a deprecation notice.

### Migration steps

1. Run `bash install.sh` to deploy v4.
2. Restart the gateway (hook activates).
3. Verify the daemon is running: `curl localhost:9090/health`
4. After 1+ hours of verified operation, remove the v1 AGENTS.md sections
   (see `docs/v1-removal-checklist.md` for exact sections to remove).

## Troubleshooting

**Daemon not starting:**

- Check PID file: `cat /tmp/status-watcher.pid`
- Check env vars: `MM_TOKEN`, `MM_URL`, `TRANSCRIPT_DIR`, `SESSIONS_JSON` must all be set
- Start manually and check logs: `node src/watcher-manager.js start`

**No status updates appearing:**

- Check health endpoint: `curl localhost:9090/health`
- Check circuit breaker state (shown in health response)
- Verify `MM_TOKEN` has permission to post in the target channel

**Duplicate status boxes:**

- Multiple daemon instances — check PID file, kill extras
- `node src/watcher-manager.js status` shows if it's running

**Session compaction:**

- When JSONL is truncated, the watcher detects it (stat.size < lastOffset)
- Offset resets, status box shows `[session compacted - continuing]`
- No crash, no data loss

## Development

```sh
# Run all tests
npm test

# Run unit tests only
npm run test-unit

# Run integration tests only
npm run test-integration

# Lint + format + test
make check
```

## Project Structure

```
src/
  watcher-manager.js    Entrypoint; PID file; graceful shutdown
  status-watcher.js     JSONL file watcher (inotify)
  session-monitor.js    sessions.json poller (2s interval)
  status-box.js         Mattermost post manager (throttle, circuit breaker)
  status-formatter.js   Status box text renderer
  circuit-breaker.js    Circuit breaker state machine
  config.js             Env var config with validation
  logger.js             pino wrapper
  health.js             HTTP health endpoint
  tool-labels.js        Tool name -> label resolver
  tool-labels.json      Built-in tool label defaults
  live-status.js        Legacy CLI (deprecated; backward compat)

hooks/
  status-watcher-hook/  gateway:startup hook (auto-start daemon)

deploy/
  status-watcher.service  systemd unit file
  Dockerfile              Container deployment

test/
  unit/                 Unit tests (59 tests)
  integration/          Integration tests (36 tests)
```
