Phase 0: - Synced latest live-status.js from workspace (9928 bytes) - Fixed 43 lint issues: empty catch blocks, console statements - Added pino dependency - Created src/tool-labels.json with all known tool mappings - make check passes Phase 1 (Core Components): - src/config.js: env-var config with validation, throws on missing required vars - src/logger.js: pino singleton with child loggers, level validation - src/circuit-breaker.js: CLOSED/OPEN/HALF_OPEN state machine with callbacks - src/tool-labels.js: exact/prefix/regex tool->label resolver with external override - src/status-box.js: Mattermost post manager (keepAlive, throttle, retry, circuit breaker) - src/status-formatter.js: pure SessionState->text formatter (nested, compact) - src/health.js: HTTP health endpoint + metrics - src/status-watcher.js: JSONL file watcher (inotify, compaction detection, idle detection) Tests: - test/unit/config.test.js: 7 tests - test/unit/circuit-breaker.test.js: 12 tests - test/unit/logger.test.js: 5 tests - test/unit/status-formatter.test.js: 20 tests - test/unit/tool-labels.test.js: 15 tests All 59 unit tests pass. make check clean.
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. Containsid(UUID),version: 3,cwdmodel_change—provider,modelIdchange eventsthinking_level_change— thinking on/offcustom— Subtypes:model-snapshot,openclaw.cache-ttl(turn boundary marker)message— Main event type.role=user,assistant, ortoolResult
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 → idlestopReason: "toolUse"→ agent waiting for tool results → NOT idlecustom.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 sessionagent:main:mattermost:channel:{channelId}— channel sessionagent:main:mattermost:channel:{channelId}:thread:{threadId}— thread sessionagent:main:subagent:{uuid}— SUB-AGENT SESSION (hasspawnedBy,spawnDepth,label)agent:main:hook:gitea:{repo}:issue:{n}— hook-triggered sessionagent:main:cron:{name}— cron session
Sub-agent entry fields relevant to watcher:
sessionId— maps to{sessionId}.jsonlfilenamespawnedBy— 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 commandscommand— all commandsagent:bootstrap— before workspace files injectedgateway: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:
n73636eit7bg3rgmpsj693mwno(default/main bot account) - 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/completemanually deploy-to-agents.shinjects 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/deletedeploy-to-agents.sh— AGENTS.md injection approachskill/SKILL.md— manual usage instructionssrc/agent-accounts.json— agent→bot account mapping
- Remote repo (ROOH/MATTERMOST_OPENCLAW_LIVESTATUS):
src/live-status.jsis 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.