- add bounded timeout+retry helper for /api/setup checks
- show actionable setup error state with Retry button
- avoid blank-screen fallback when setup status check fails
- add unit tests for retry helper
Fixes#456
* feat: add system monitor panel with live CPU, memory, disk, and GPU metrics
New btop-inspired monitoring page in the OBSERVE sidebar group.
Polls /api/system-monitor every 2s and renders rolling Recharts area
charts for CPU/memory/GPU and progress bars for disk usage.
* feat: add network I/O monitoring to system monitor panel
Add live network throughput (rx/tx bytes/sec) as a dual area chart.
API returns cumulative byte counters per interface (stateless), frontend
computes rates from deltas between 2s polls. Supports Linux (/proc/net/dev)
and macOS (netstat -ib).
* feat: add top processes list to system monitor
Shows top 8 processes by CPU usage in a compact table with CPU%, Mem%,
and RSS columns. Color-codes high usage values. Uses ps on both Linux
and macOS with fallback for platforms without --sort support.
* fix: normalize process CPU% to 0-100 and filter out ps itself
CPU% from ps is per-core (e.g. 200% on 4 cores), now divided by core
count to show total system percentage. Also filters out the ps command
that briefly appears while collecting process data.
---------
Co-authored-by: Dan <github@bookkept.com.au>
Add ability to hide agents from the dashboard without deleting them.
Hidden agents still receive heartbeats and exist in the DB — they are
purely hidden from the default UI view.
- Migration 042: adds `hidden` column to agents table
- API: POST/DELETE /api/agents/[id]/hide to toggle visibility
- GET /api/agents filters hidden by default, ?show_hidden=true to include
- UI: "Show hidden" toggle in panel header, Hide/Unhide button on cards
Co-authored-by: Dan <github@bookkept.com.au>
Add min-h-0 to the kanban board flex container and its column children
so that overflow-y-auto on column bodies can actually trigger. Without
this, flexbox min-height:auto causes containers to grow unbounded
instead of constraining height and enabling scroll.
Fixes#376
Co-authored-by: Dan <github@bookkept.com.au>
- make /api/spawn compatible with gateway-managed default models
- add regression coverage for gateway dashboard registration
- publish official multi-arch images to Docker Hub when configured
Add awaiting_owner column to task board with keyword-based detection
for tasks requiring human action. Tasks matching keywords like
"waiting for", "needs human", "approval needed" are automatically
placed in a dedicated column with orange styling.
Supersedes #397
Add a CSS-only Dunk It button to task cards with a 4-phase state
machine (idle, success, error, dismissing). Uses inline CSS
transitions for animations with no external dependencies.
Supersedes #373
Dynamically scan workspace-* directories under the openclaw state dir
to discover per-agent skill roots. Display them in the Skills Hub with
agent-specific labels and violet badge styling.
Closes#412
Supersedes #413
Replace style-src nonce directive with unsafe-inline to support
reagraph's runtime <style> injection. Add style-src-elem and
style-src-attr directives for CSP Level 3 compliance. Extend
fitNodesInView retries from 2 to 4 for more reliable canvas sizing.
Closes#414
Supersedes #415
Adds install.ps1 — a PowerShell port of install.sh for Windows users. Supports local and Docker deployment modes with secure credential generation, standalone server setup, and OpenClaw fleet checks. Adds Windows install section to README.
Adds 70 missing translation keys across 3 namespaces (logViewer, pipeline, debug) to messages/en.json. These namespaces are referenced by existing components but were absent, causing raw keys to render.\n\nFixes #416
Fixes#410 — getSkillRoots() in skill-sync.ts was not updated when #408 added workspace support to route.ts, causing the scheduler sync to never populate workspace skills (always showing 0).
- Add `workspace` skill root (~/.openclaw/workspace/skills) for
workspace-local skill discovery alongside the existing 5 roots
- Make group cards clickable to filter the installed skills list by root
- Add `workspace` as valid target for skill creation and registry install
- Add `showAllRoots` i18n key to all 10 locale files
Closes#364
Replace all `runClawdbot(['-c', ...])` invocations with
`callOpenClawGateway()` RPC calls in session control, spawn, and delete
routes. This eliminates the deprecated CLI dependency and uses the
structured gateway protocol instead.
Also add `formatSessionLabel()` helper and `useAgentSessions()` hook to
task-board-panel for better session dropdown display labels.
Closes#401, closes#406
Implements Phase 1 of GNAP (Git-Native Agent Protocol) integration:
- SQLite stays primary, GNAP repo is an optional sync target
- Push task create/update/delete to .gnap/tasks/{id}.json with git commits
- Status/priority mapping between MC and GNAP formats
- Management API at /api/gnap (status, init, manual sync)
- GNAP badge in task board header with click-to-sync
- 15 unit tests covering mapping, init, push, remove, pull, and status
Enable with GNAP_ENABLED=true in .env. Follows the same fire-and-forget
pattern as the existing GitHub sync engine.
Closes#374
Supersedes #389
Always merge both gateway and local (Claude Code, Codex, Hermes) sessions
in the sessions API instead of gating on `include_local`. The conversation
list now renders all sources unconditionally, using each session's `source`
field for display logic rather than the global `dashboardMode`.
- Add `localSessionsAvailable` store flag, set from capabilities `claudeHome`
- Remove `include_local` query param and early-return in sessions API
- Remove source-based filter and `dashboardMode` branching in conversation list
- Show both gateway and local active/recent groups when data exists
* fix(gateway): probe /api/health instead of root URL for health checks (#390)
The server-side gateway health probe was fetching the root URL (/)
which returns HTTP 400 on OpenClaw 2026.3.13 gateways. The gateway
exposes a dedicated /api/health endpoint that returns 200 with status
info.
The WebSocket ping RPC 'unknown method' issue is already handled —
websocket.ts detects the INVALID_REQUEST and falls back to passive
heartbeat mode. The actual bug was this HTTP probe hitting the wrong
endpoint.
Fixes#390
* fix(gateway): ensure gateways table exists before health probe
The gateways table is created lazily by the gateways API (ensureTable).
The health route was querying it directly without CREATE IF NOT EXISTS,
causing SqliteError: no such table: gateways in fresh databases (E2E tests,
Docker first-boot).
Add ensureGatewaysTable() inline to mirror the pattern in route.ts.
* fix: update health-utils test to match /api/health probe path
The test file has its own copy of buildGatewayProbeUrl — update it to
append /api/health instead of / to match the route.ts change.
---------
Co-authored-by: Nyk <0xnykcd@googlemail.com>
* feat: add task session targeting - dispatch tasks to specific agent sessions (#395)
When assigning a task to an agent, users can now optionally select an
existing session to dispatch the task to instead of creating a new one.
Changes:
- task-dispatch.ts: When target_session is set in task metadata, use
gateway chat.send to dispatch to that specific session instead of
creating a new one via call agent
- task-board-panel.tsx: Add session selector dropdown in both Create
and Edit task modals that appears when an agent is selected and has
active sessions
- store/index.ts: Add agent and channel fields to Session interface
Closes#395
* fix(gateway): ensure gateways table exists before health probe
The gateways table is created lazily by the gateways API (ensureTable).
The health route was querying it directly without CREATE IF NOT EXISTS,
causing SqliteError: no such table: gateways in fresh databases (E2E tests,
Docker first-boot).
Add ensureGatewaysTable() inline to mirror the pattern in route.ts.
* fix: reduce server memory footprint across session parsing, caching, and rate limiting
- Stream-parse Claude JSONL session files instead of loading entire files into memory
- Add 50MB file size cap to skip oversized session transcripts
- Add 30s TTL cache to getAllGatewaySessions() to avoid redundant disk reads per scheduler tick
- Cap rate limiter maps at 10,000 entries with oldest-first eviction
- Add request.signal abort listener to SSE route for defense-in-depth cleanup
- Add test for rate limiter maxEntries eviction behavior
* fix: address audit findings across all memory-fix files
claude-sessions.ts:
- Wrap readline loop in try/finally to ensure rl.close() on error paths
- Guard statSync in file loop to handle files deleted between readdir and stat
- Fix variable shadowing: rename inner `now` to `nowSec` to avoid confusion
- Update throttle timestamp on empty scan results (prevents repeated disk scans)
- Skip sidechain-only sessions (zero user+assistant messages)
sessions.ts:
- Decouple `active` flag from cache — compute at read time to prevent stale data
- Remove activeWithinMs from cache key to eliminate cache thrashing between
callers using different windows (Infinity vs default)
- Add invalidateSessionCache() and call it after pruneGatewaySessionsOlderThan
events/route.ts:
- Null out cleanup after first call to prevent double-invoke
- Remove unnecessary `if (request.signal)` guard (always defined on NextRequest)
rate-limit.test.ts:
- Rewrite eviction test with maxRequests=1 to actually prove eviction occurred
- Add assertion that non-evicted entries remain tracked
- Remove duplicate title check from POST /api/tasks (closes#368)\n- Scope recurring tasks duplicate check by project_id\n- Fix task deletion error handling — show errors to user (closes#369)\n- Enable vertical scrolling on Kanban board (closes#376)\n- Refactor nodes route to use RPC via callOpenClawGateway\n- Handle read-only filesystem in gateway config registration\n- Add screenshot-drift CI workflow and guide\n- Docker compose: add host-gateway for reaching host gateway
Switches notification delivery from sessions_send to gateway call agent command. No longer requires session_key — uses agent name (recipient) directly. Timeout increased 10s to 30s.
When memoryAllowedPrefixes is configured, scan only those subdirectories instead of the entire MEMORY_PATH. Falls back to full scan if no prefixes are set.\n\nCloses #366
- Remove unconditional `dangerouslyDisableDeviceAuth = true` from MC
origin registration. MC should only add its origin to allowedOrigins,
not silently downgrade the gateway's device auth security posture.
- Replace invalid `sandbox` value with `allowlist` in security scan
and auto-fix for `tools.exec.security`. Current OpenClaw validates
only: deny, allowlist, full. The old `sandbox` value was rejected.
Closes#357, closes#356
- Move pids limit from service-level `pids_limit` to
`deploy.resources.limits.pids` for Compose v5+ compatibility.
Some Compose versions normalize both into the same field, causing
"can't set distinct values" errors.
- Clear OpenClaw update banner when `/api/openclaw/version` reports
`updateAvailable: false`. Previously the banner could persist after
a successful update because only the `true` path was handled.
Closes#353, closes#351
* fix: show all agents in command select, disable those without active session
Previously the agent select in the OrchestrationBar only listed agents
with a session_key set (agents.filter(a => a.session_key)), causing the
dropdown to appear completely empty when agents exist but none have an
active session. This was confusing — users thought no agents were
registered.
Fix: show all registered agents in the dropdown. Agents without an
active session_key are shown as disabled with a '— no session' suffix
and a tooltip explaining why they can't be messaged. The 'No agents
registered' placeholder is shown only when the agents array is truly
empty.
The send button remains correctly disabled when no valid agent is
selected.
Fixes#321
* test: add gateway health history e2e + utility unit tests
- Add tests/gateway-health-history.spec.ts: Playwright e2e tests for
GET /api/gateways/health/history and POST /api/gateways/health
covering auth guards, response shape validation, chronological
ordering, and gateway name resolution.
- Add src/app/api/gateways/health/health-utils.test.ts: 30 vitest
unit tests for the pure utility functions in the health probe route:
ipv4InCidr, isBlockedUrl (SSRF protection), buildGatewayProbeUrl,
parseGatewayVersion, and hasOpenClaw32ToolsProfileRisk.
All 30 unit tests pass. Covers the health history feature introduced
in feat(gateways): add health history logging and timeline (#300).
* test: add Docker-mode integration tests for gateway connectivity regressions
Covers three post-fix regression scenarios:
- 404 on gateway health (wizard used GET; endpoint is POST-only) — #334
- EROFS / build-phase DB eager init in Docker — #337
- Gateway connect resolves ws_url without OPENCLAW_HOME env var
Adds tests/docker-mode.spec.ts with four describe blocks:
1. Gateway health endpoint contract (POST=200, GET=405, no-auth=401)
2. Lazy DB init on first runtime request (read + write paths)
3. Gateway connect without home env vars
4. Onboarding gateway-link step lifecycle
* fix: show all agents in command select, disable those without active session
Previously the agent select in the OrchestrationBar only listed agents
with a session_key set (agents.filter(a => a.session_key)), causing the
dropdown to appear completely empty when agents exist but none have an
active session. This was confusing — users thought no agents were
registered.
Fix: show all registered agents in the dropdown. Agents without an
active session_key are shown as disabled with a '— no session' suffix
and a tooltip explaining why they can't be messaged. The 'No agents
registered' placeholder is shown only when the agents array is truly
empty.
The send button remains correctly disabled when no valid agent is
selected.
Fixes#321
* test: add gateway health history e2e + utility unit tests
- Add tests/gateway-health-history.spec.ts: Playwright e2e tests for
GET /api/gateways/health/history and POST /api/gateways/health
covering auth guards, response shape validation, chronological
ordering, and gateway name resolution.
- Add src/app/api/gateways/health/health-utils.test.ts: 30 vitest
unit tests for the pure utility functions in the health probe route:
ipv4InCidr, isBlockedUrl (SSRF protection), buildGatewayProbeUrl,
parseGatewayVersion, and hasOpenClaw32ToolsProfileRisk.
All 30 unit tests pass. Covers the health history feature introduced
in feat(gateways): add health history logging and timeline (#300).
The two-step agent → agent.wait pattern used in dispatchAssignedTasks and
runAegisReviews only returns lifecycle metadata (runId, status, timestamps).
The agent's actual response text is only available via --expect-final on the
initial agent call, which blocks until completion and returns the full payload
including result.payloads[0].text.
Without this fix:
- Task resolution is stored as the raw wait JSON instead of the agent's response
- Aegis cannot parse a VERDICT from the resolution, so it always defaults to rejected
- Tasks are permanently stuck in a reject/retry loop and never complete
Fix: replace the two-call pattern with a single --expect-final call in both
dispatchAssignedTasks and runAegisReviews. Also improve sessionId extraction
to use the agentMeta path from the final payload.
Co-authored-by: Tom Watts <tom@techteamup.com>
Previously the agent select in the OrchestrationBar only listed agents
with a session_key set (agents.filter(a => a.session_key)), causing the
dropdown to appear completely empty when agents exist but none have an
active session. This was confusing — users thought no agents were
registered.
Fix: show all registered agents in the dropdown. Agents without an
active session_key are shown as disabled with a '— no session' suffix
and a tooltip explaining why they can't be messaged. The 'No agents
registered' placeholder is shown only when the agents array is truly
empty.
The send button remains correctly disabled when no valid agent is
selected.
Fixes#321
Replace the unhelpful "POST /api/agents with X-Api-Key header" message
with a user-friendly hint explaining that agents are auto-discovered
from Claude, Codex, and Hermes directories, and that gateway mode shows
registered agents.
Adds missing noAgentsHint translation key across all 10 locales.
Closes#321
- Filter out cron jobs with missing/empty schedule before processing,
preventing TypeError on .toLowerCase() (#342)
- Add defensive .localeCompare() guard in sort comparator
- Exclude positive/instructional lines ("No ... warnings detected",
"Run: ...") from doctor issue extraction (#331)
- Strip negated warning phrases before keyword detection so "No channel
security warnings detected" doesn't trigger warning level
- Add 2 tests for the doctor parser fix
* docs: add screenshot drift prevention guide and CI workflow
- Add docs/SCREENSHOT-GUIDE.md with step-by-step instructions for
capturing, optimising, and committing fresh README screenshots
- Add .github/workflows/screenshot-drift.yml that detects UI file
changes in PRs, applies a 'screenshot-drift' label, and posts a
checklist comment reminding contributors to verify screenshots
Addresses: README screenshots may drift as the UI evolves quickly
(setup wizard, health history timeline, and onboarding were added
after the last screenshot refresh on 2026-03-11)
* fix(gateway): resolve Docker connectivity — 404 endpoints, EROFS write, missing OPENCLAW_HOME
Fixes#332, #333
Root causes
-----------
1. nodes/route.ts called non-existent HTTP REST endpoints on the gateway
(/api/presence, /api/devices, /api/rpc). The OpenClaw gateway only
exposes HTTP at /health; all other operations use WebSocket RPC.
2. gateway-runtime.ts attempted fs.writeFileSync on openclaw.json which
fails with EROFS when the container or mount is read-only (docker-compose
uses read_only: true by default).
3. install.sh never wrote the detected OPENCLAW_HOME or Docker host gateway
IP into .env, leaving OPENCLAW_HOME empty after a fresh install.
Fixes
-----
- nodes/route.ts: replace HTTP calls to /api/presence and /api/devices
with gateway /health reachability check + callOpenClawGateway() RPC
(node.list / device.pair.list). When the openclaw CLI is unavailable
inside Docker, the RPC falls back gracefully and returns connected=true
with an empty list rather than a misleading 'Gateway offline' error.
POST device actions likewise use callOpenClawGateway instead of /api/rpc.
- gateway-runtime.ts: catch EROFS / EACCES / EPERM on config write and
emit a warn-level log with a hint, instead of an error. This eliminates
the 'Failed to register MC in gateway config EROFS: read-only file
system' log spam on Docker installs where openclaw.json is read-only.
- install.sh: after generating .env, write the detected OPENCLAW_HOME.
In --docker mode also detect the Docker host IP (host-gateway or bridge
default) and set OPENCLAW_GATEWAY_HOST so the container can reach a
gateway running on the host out of the box.
- docker-compose.yml: add extra_hosts host-gateway so the container can
resolve the Docker host IP; add a commented volume snippet for mounting
the OpenClaw state directory read-only.
- Add busy_timeout = 5000 pragma to prevent SQLITE_BUSY errors under
concurrent Next.js route-handler requests (WAL mode helps but is not
sufficient without a retry budget).
- Guard module-level getDatabase() call with !isBuildPhase to prevent
build-time vs runtime SQLite state conflicts on cold starts.
- Add tests covering both pragmas and build-phase skip behaviour.