* feat(gateway): task-suggestion acceptance modes with compact split-button card
- taskSuggestions.accept gains additive mode (worktree default | local | cloud | session) + cloudProfileId; operator.admin unchanged; worktree requests stay {taskId}-only for older gateways
- local reuses the plain-cwd create; cloud mirrors the Control UI create-empty -> sessions.dispatch -> send sequence with pre-claim profile validation and full rollback; session delivers the prompt into the source session (steer single active run, plain turn when idle, ambiguous refused) and can only cancel the chip on failure, never touch the source session
- Control UI card redesigned per owner reference: repo-basename eyebrow, clamped summary, instructions expander, split button with per-profile cloud items or a disabled "No cloud environment configured" hint
- TUI gains the same modes as selector rows with capability gating and double-Enter arming
Release-note context: operators now choose where an accepted follow-up runs — isolated worktree (default), the project checkout, a configured cloud worker, or as the next instruction in the current session — from a compact suggestion card.
* fix(gateway): negotiate task-suggestion acceptance modes and enforce steerable-run invariant
- Gate non-worktree task-suggestion actions on the negotiated Gateway capability.
- Require exactly one steerable run ID before delivering into an active source session.
* test(gateway): boot a minimal test gateway in the gateway lane
A gateway startup stall (#120926, awaited chat-metadata refresh in minimal
mode) shipped with green CI and first surfaced by hanging every checks-ui-e2e
suite that boots a minimal test gateway: no gateway-lane test booted one with
bundled plugins enabled (gateway.test.ts disables them, which masked the
stall), and the changed-scope classifier only selects the ui-e2e lane for
ui-touching diffs.
Add a minimal-gateway boot smoke in src/gateway that mirrors the ui-e2e boot
environment (bundled plugins enabled, minimal skips, strict time budget).
Selection is automatic: the smoke imports the gateway server, so the changed
node test plan picks it up through the import graph for any diff that can
affect startup, and full-suite plans run it in the
agentic-control-plane-startup-core shard. Reintroducing the #120926 hunk makes
the smoke fail (watchdog kill) in the gateway lane; healthy code boots in ~10s.
A planner regression test keeps the smoke classified as a gateway-server test
file and import-graph-reachable from gateway startup sources, so a rename or
graph-invisible import shape cannot silently drop the coverage again.
* test(ui): resolve labs rows by registry id, not positional index
Unblocks landing onto red main: 8fdf7570a1 (#120727) added the Cloud Worker
Desktop labs entry with a hardcoded row index that collides with Message audit
metadata, so enabling it toggled the neighboring row and the full-suite lane
failed deterministically (labs-page.test.ts, exposed only on PRs that run the
compact full suite). Derive each table case's row index from LAB_FEATURES by
feature id — the idiom the rest of the suite already uses — so a new labs
entry can no longer silently retarget an existing case.
* perf(gateway): per-transcript broadcast lanes
Transcript-update broadcasts were serialized behind one global promise queue,
so one session's pending async seq read head-of-line blocked every other
session's session.message and sessions.changed delivery. Ordering is a
per-transcript contract, so key the queue by transcript identity and drop
drained lanes.
Per-message transcript usage stays derived: session.message payloads carry
live totalTokens/estimatedCostUsd as a dashboard API contract (#50101).
* fix(ui): target the Cloud Worker Desktop labs row in its toggle test
The row added in #120727 reused index 6, which belongs to Message audit
metadata, so the case toggled the audit row and asserted its patch. Labs
features render in registry order, and workerDesktop is the eighth entry.
* feat(gateway): live desktop observer for cloud workers
Adds live observation for cloud worker desktops through the gateway and Crabbox plugin, including desktop provisioning, persisted desktop metadata, tunneled WebSocket proxying, and the worker.desktop.observe protocol method.
The gateway, Crabbox plugin, and gateway protocol surfaces remain off by default behind the cloudWorkers.desktop Labs flag.
* feat(ui): Desktop panel for cloud worker observation
* docs(gateway): document cloud worker desktop lab
* fix(ci): regenerate contract baselines after rebase
* fix(protocol): regenerate Android gateway methods
* fix(ci): align rebased SDK and lint baselines
* fix(gateway): enforce view-only RFB boundary and fence desktop teardown
* fix(gateway): tighten RFB filter surface
* fix(state): keep pre-desktop databases readable and harden view-only RFB
* fix(gateway): fence desktop observer upgrades behind work admission
* fix(gateway): bind desktop observer tokens to their owner epoch
* fix(ci): regenerate config and SDK baselines after rebase
* fix(ci): regenerate native protocol and SDK baselines
* fix(ci): regenerate contracts after main rebase
* fix(state): register desktop metadata as lazy additive
* fix(ci): regenerate SDK baseline after final direct-merge rebase
Let the runtime config patch owner complete its authoritative refresh instead of starting a second Labs-page refresh. Tighten unit and mocked browser coverage around the single refresh and visible settings state.
Punchcard-Session: amber-workshop-workshop-36
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(ui): hide inert task-suggestion chips and strip bidi controls
- render no suggestion card for operators who can neither accept nor dismiss (matches TUI behavior)
- strip RTL/bidi control characters from chip title, summary, project path, and instructions before rendering
- document the git-checkout requirement for spawn_task project directories (gateway enforcement landed in #120940)
* fix(ui): gate task-suggestion actions on negotiated gateway methods
Mirror the TUI capability rule: accept requires the taskSuggestions.accept method plus operator.admin, dismiss requires taskSuggestions.dismiss plus operator.write. List-only gateways render no chip; e2e fixtures now advertise the mutation methods they exercise, and a new regression proves a pending suggestion renders no chip when only listing is advertised. Addresses the ClawSweeper P2 on this PR.
* fix(ui): prevent dark widget loading flashes
Dashboard Canvas and MCP App frames now paint their theme-derived widget surface while sandbox content loads, avoiding white flashes in dark mode.
* test(ui): avoid shadowing widget fixture
* fix(ui): report dev checkout update status
Show tracked-upstream commit lag and verified install/commit timestamps in Settings > Updates. Verify the post-restart Git revision before reporting success, and surface same-revision updates as an explicit no-op unless plugin convergence changed the install.
* test(ui): expect authoritative update reconciliation
* fix(ui): retain dashboards across view switches
Keep visited dashboard runtimes alive across Chat, Split, and Dashboard transitions, suspend hidden work, and avoid same-face persistence requests.
Refs #120639
* fix(ui): preserve session owner during route loads
Carry the last successful route match through pending loads so face-only navigation retains dashboard runtimes without stranding session or catalog switches.
Refs #120639
* fix(ui): preserve sandbox bridge while hidden
Suspend hidden board frame work without disposing the one-shot sandbox handshake, loaded document, or MessagePort.
Refs #120639
* fix(ui): forward sandbox readiness while hidden
Keep the constrained sandbox message channel alive during dashboard suspension so one-shot proxy readiness reaches the retained host without resuming widget work.
Refs #120639
* fix(ui): pause hidden plugin widgets
Propagate board activity through trusted plugin renderers so Workboard widgets suppress hidden refresh and mutation work, then refresh once without remounting.
Refs #120639
The rewind-only confirmation popover kept "delete" naming from the removed
local-only hide feature (#120681). Rename identifiers and DOM/CSS classes to
confirmed-action, drop the dead right-side placement variant, collapse the
single-caller renderConfirmedActionButton indirection into renderRewindButton,
and localize the hardcoded Cancel / Don't ask again strings.
The openclaw:skip-rewind-confirm localStorage preference key is intentionally
unchanged so users' "Don't ask again" choice survives.
* fix(codex): pin catalog CODEX_HOME onto terminal resume spawns
The Codex session catalog lists threads through the plugin's supervision
app-server connection, which can run on the agent-scoped Codex home
(homeScope: "agent"). The terminal resume plan spawned a bare
'codex resume <threadId>' under the operator login shell, so the CLI
resolved its default ~/.codex and failed with "No saved session found".
Terminal plans now carry the catalog connection's CODEX_HOME (local and
paired-node paths), the gateway merges plan env into the PTY spawn, and
the triplicated home-scope resolution collapses into one shared
resolveCodexAppServerLocalHomeDir.
* feat(ui): add main-content terminal placement
The terminal panel gains a third dock placement, "main", filling the
content region next to the sidebar like the transcript pane. The panel
header gets a bottom/right/main placement switcher (persisted in the
existing openclaw.terminal.panel.v1 store), and opening a Codex/Claude
session from the session catalog selects main mode so resumed sessions
appear as a full window instead of a bottom strip.
Remove the localStorage-backed per-browser hide action ("Hide in this browser only") so the transcript mirrors agent context. The action was un-undoable because restore() had no UI path.
Stale openclaw:deleted:* localStorage keys become inert.
Question panels now use normal footer flow. Resize following respects explicit backscroll intent, and the latest-message affordance stays above footer surfaces.