Files
sol c724e57276 feat: Mattermost plugin + daemon integration (Phases 2-5)
Plugin (Go server + React webapp):
- Custom post type 'custom_livestatus' with terminal-style rendering
- WebSocket broadcasts for real-time updates (no PUT, no '(edited)')
- KV store for session persistence across reconnects
- Shared secret auth for daemon-to-plugin communication
- Auto-scroll terminal with user scroll override
- Collapsible sub-agent sections
- Theme-compatible CSS (light/dark)

Daemon integration:
- PluginClient for structured data push to plugin
- Auto-detection: GET /health on startup + periodic re-check
- Graceful fallback: if plugin unavailable, uses REST API (PUT)
- Per-session mode tracking: sessions created via plugin stay on plugin
- Mid-session fallback: if plugin update fails, auto-switch to REST

Plugin deployed and active on Mattermost v11.4.0.
2026-03-07 22:11:06 +00:00
..

Discovery Findings: Live Status v4

Overview

Planner sub-agent (proj035-planner) conducted inline discovery before drafting the plan. Key findings are documented here.

Discovery 1: JSONL Transcript Format

Confirmed format (JSONL, version 3):

Each line is a JSON object with type field:

  • session — First line only. Contains id (UUID), version: 3, cwd
  • model_changeprovider, modelId change events
  • thinking_level_change — thinking on/off
  • custom — Subtypes: model-snapshot, openclaw.cache-ttl (turn boundary marker)
  • message — Main event type. role = user, assistant, or toolResult

Message content types:

  • {type: "text", text: "..."} — plain text from any role
  • {type: "toolCall", id, name, arguments: {...}} — tool invocations in assistant messages
  • {type: "thinking", thinking: "..."} — internal reasoning (thinking mode)

Assistant messages carry extra fields: api, provider, model, usage, stopReason, timestamp

ToolResult messages carry: toolCallId, toolName, isError, content: [{type, text}]

Key signals for watcher:

  • stopReason: "stop" + no new lines → agent turn complete → idle
  • stopReason: "toolUse" → agent waiting for tool results → NOT idle
  • custom.customType: "openclaw.cache-ttl" → turn boundary marker

Discovery 2: Session Keying

Session keys in sessions.json follow the pattern: agent:{agentId}:{context}

Examples:

  • agent:main:main — direct session
  • agent:main:mattermost:channel:{channelId} — channel session
  • agent:main:mattermost:channel:{channelId}:thread:{threadId} — thread session
  • agent:main:subagent:{uuid} — SUB-AGENT SESSION (has spawnedBy, spawnDepth, label)
  • agent:main:hook:gitea:{repo}:issue:{n} — hook-triggered session
  • agent:main:cron:{name} — cron session

Sub-agent entry fields relevant to watcher:

  • sessionId — maps to {sessionId}.jsonl filename
  • spawnedBy — parent session key (for nesting)
  • spawnDepth — nesting depth (1 = direct child of main)
  • label — human-readable name (e.g., "proj035-planner")
  • channel — delivery channel (mattermost, etc.)

Sessions files: /home/node/.openclaw/agents/{agentId}/sessions/

  • sessions.json — registry (updated on every message)
  • {uuid}.jsonl — transcript files
  • {uuid}-topic-{topicId}.jsonl — topic-scoped transcripts

Discovery 3: OpenClaw Hook Events

Available internal hook events (confirmed from source):

  • command:new, command:reset, command:stop — user commands
  • command — all commands
  • agent:bootstrap — before workspace files injected
  • gateway:startup — gateway startup (250ms after channels start)

NO session:start or session:end hooks exist. The hooks system covers commands and gateway lifecycle only, NOT individual agent runs.

Sub-agent lifecycle hooks (subagent_spawned, subagent_ended) are channel plugin hooks, not internal hooks — not directly usable from workspace hooks.

Hook handler files: workspace hooks support handler.ts OR handler.js (both discovered automatically via handlerCandidates in workspace.ts).

Discovery 4: Mattermost API

  • PostEditTimeLimit = -1 — unlimited edits on this server
  • Bot token: <redacted> (default/main bot account, set via MM_BOT_TOKEN env var)
  • Multiple bot accounts available per agent (see openclaw.json accounts)
  • API base: https://slack.solio.tech/api/v4
  • Post update: PUT /api/v4/posts/{id} — no time limit, no count limit

Discovery 5: Current v1 Failure Modes

  • Agents call live-status create/update/complete manually
  • deploy-to-agents.sh injects verbose 200-word protocol into AGENTS.md
  • Agents forget to call it (no enforcement mechanism)
  • IDs get lost between tool calls (no persistent state)
  • No sub-agent visibility (sub-agents have separate sessions)
  • Thread sessions create separate OpenClaw sessions → IDs not shared
  • Final response dumps multiple status updates (spam from forgotten updates)

Discovery 6: Repo State

  • Workspace copy: /home/node/.openclaw/workspace/projects/openclaw-live-status/
    • src/live-status.js — 283 lines, v2 CLI with --agent, --channel, --reply-to, create/update/complete/delete
    • deploy-to-agents.sh — AGENTS.md injection approach
    • skill/SKILL.md — manual usage instructions
    • src/agent-accounts.json — agent→bot account mapping
  • Remote repo (ROOH/MATTERMOST_OPENCLAW_LIVESTATUS): src/live-status.js is outdated (114 lines v1)
  • Makefile with check/test/lint/fmt targets already exists in remote repo

Synthesis

The transcript-tailing daemon approach is sound and the format is stable. The key implementation insight is: watch sessions.json to discover new sessions, then watch each JSONL file for that session. Sub-agents are automatically discoverable via spawnedBy fields. The hook system can auto-start the daemon on gateway startup via gateway:startup event. No new OpenClaw core changes are needed.