* feat(linux): canvas via CLI-node + Tauri app IPC bridge
* refactor: extract gateway helper modules
* build(linux-canvas): register plugin package in lockfile
* fix(linux-canvas): move canvas advertise test out of core, regen docs/protocol/deadcode
* fix(gateway): break node-catalog/registry import cycle via leaf normalize module; add canvas glossary term
* style: oxfmt invoke.ts and runtime.ts after buildNodeEventParams extraction
* fix(linux): load Canvas WebView via dedicated data_directory context
Wry's Linux/WebKitGTK incognito mode discards Tauri's registered
WebContext (wry webkitgtk/mod.rs), so the Canvas window got a fresh
ephemeral context without the openclaw-canvas:// scheme handler — the
bundled A2UI page never committed (stayed about:blank) and every A2UI
command timed out. Use an isolated cache-backed data_directory instead,
which keeps the protocol handler while still isolating Canvas storage
from the dashboard window.
* fix(linux): keep Canvas WebView ephemeral via incognito + data_directory
Autoreview flagged that a dedicated data_directory alone persists Canvas
browser state (cookies, localStorage, IndexedDB, service workers) across
restarts, so an agent that navigates Canvas to a site could leak an
authenticated session into a later session. iOS uses a non-persistent
store; Linux should match.
Add .incognito(true) alongside .data_directory(): the distinct directory
gives Tauri a fresh WebContext key so it still attaches the
openclaw-canvas:// protocol closure, and incognito makes Wry swap in a
fresh *ephemeral* context carrying those protocols. Live-verified on a
Wayland/WebKitGTK box: the bundled page still loads
(location.href=openclaw-canvas://localhost/index.html, openclawA2UI
present, A2UI renders) and the canvas-webview dir holds no persistent
cookie/storage files.
User-facing name is now OpenClaw (the system speaks); internal code name is
system-agent. Gateway methods crestodian.* -> openclaw.chat/openclaw.setup.*,
agent tool -> openclaw, reserved agent ids openclaw + retired crestodian.
openclaw setup routes: onboarding flags -> onboard, -m/--yes -> system agent,
bare configured interactive -> OpenClaw chat, unconfigured -> onboarding.
Hidden crestodian CLI and /crestodian TUI aliases kept; docs moved to
docs/cli/openclaw.md with redirect stub. macOS/Android strings in lockstep.
Refs #107237
* feat(ui): redesign Channels page with guided channel setup wizard
Adds wizard.start flow=channels gateway RPC (additive protocol change) that
drives the shared channel-setup wizard (openclaw channels add) over the
session step protocol. Control UI Channels page becomes a card hub with
plugin-art covers, guided per-channel setup modal (WhatsApp QR pairing via
web.login.*, BotFather/Slack/Discord helper links), and a detail overlay
hosting the full schema config form.
* test(ui): cover channel wizard controller; refresh channels view test props
* chore(mock): use non-token-shaped wizard placeholder
* test(gateway): scanner-safe auth local in channels wizard test
* fix(ui): cancel gateway wizard session on channels page disconnect
* fix(ui): adopt browse-all channel pick and guard dirty config before setup
* fix(ui): cancel gateway session created by a stale wizard.start
* fix(ui): adopt multiselect channel picks; drop redundant String()
* fix(ui): track all wizard-adopted channels so WhatsApp QR runs regardless of pick order
* feat(gateway): report configured channels on terminal channel-wizard result
Replaces the Control UI's channel-adoption heuristic with the authoritative
outcome: the channels flow reports its actual selection after config commit,
the wizard session surfaces it as an additive channels field on the terminal
wizard.next result, and the UI keys WhatsApp QR linking off that.
* refactor(ui): align channels hub with the unified settings design language
Hub becomes settings-language rows (44px art tiles, status dots, uppercase
section headings, hairline groups) instead of a card gallery; the detail
overlay hosts the migrated per-channel settings sections with Run setup in
the header. Wizard dialog and tile/cover styles move to spacing tokens.
* chore(i18n): translate channels hub/setup strings; refresh raw-copy baseline
* refactor: satisfy LOC ratchet and dead-export gates
Split mock-dev channel/plugin fixtures into their own modules, move wizard
runner types/defaults to server-methods/wizard.ts, extract the channels page
wizard host and nostr profile HTTP ops, and unexport internal-only types
(incl. the unused config-form SECTION_META barrel re-export).
* chore(i18n): retranslate channels strings on rebased locale bundles
* fix(test): drop redundant String() in channels wizard e2e
* fix: address channel-wizard review findings
- lock wizard cancellation before durable installs/config writes
- report configured channel accounts on the terminal wizard result and
start WhatsApp QR pairing for that account (web.login accountId)
- forward request options through the browser gateway client so wizard
RPC timeouts apply
- render skipped setup as a no-change completion
- keep detail/advanced config reachable for unconfigured channels
- surface the dirty-config warning inside the detail overlay
- adopt the picked channel for wizard title/links in browse-all flows
* fix(ui): local wizard RPC timeout; translate no-change completion strings
* feat(gateway-protocol): add session placement schema
Closed state discriminator for session execution placement (local/requested/provisioning/syncing/starting/active/draining/reconciling/reclaimed/failed), sessions.dispatch params, and worker-admission transcript/live cursor extensions. Swift protocol models mirror the schema.
* feat(state): add worker session placement table
worker_session_placements rows carry placement state, transition generation, worker ownership metadata, ACK cursors, and the turn claim columns used for atomic admission.
* feat(cloud-workers): add durable placement state machine store
SQLite-backed placement store split by concern: state table (placement-state), discriminated record types + shape invariants (placement-record), row codec + CAS transition values (placement-row-codec), atomic turn-claim admission/release/waiters (placement-turn-claims), and lifecycle CAS transitions (placement-store).
* feat(cloud-workers): sync workspaces and attach sessions to worker environments
Environment service session attachment + turn credentials, tunnel workspace commands over a dedicated SSH runner, and git/plain workspace sync into $HOME/.openclaw-worker/workspaces with an immutable manifest. Symlink escapes are rejected locally before transfer (macOS openrsync stat-fails them opaquely) and again by the remote manifest guard.
* feat(worker): run one-shot embedded turns from launch descriptors
Worker runtime executes a single embedded turn from a stdin launch descriptor and reports completed/failed/fenced on stdout for the gateway launcher. Terminal lifecycle live events are deferred past the final transcript flush; transcript projection helpers are shared via transcript-message instead of duplicated in the runtime.
* feat(cloud-workers): dispatch placements and route worker turns
Dispatch service drives local->requested->provisioning->syncing->starting->active with failure teardown (placement-dispatch-failure) and restart/runtime recovery incl. lost-worker reclaim (placement-dispatch-recovery). Worker turn launcher claims the placement turn atomically, builds a windowed launch descriptor (worker-turn-payload), runs the remote one-shot worker, and reconciles the committed transcript; agent runners route turns through the session placement admission provider.
* feat(gateway): expose session placement RPCs and startup reconciliation
sessions.dispatch RPC with lifecycle admission barriers, operator-facing placement projection on session listings, placement-aware session reset guard, and startup/interval reconciliation wiring for worker placements.