Adds client-capability-gated tool availability: gateway clients declare
capabilities at connect (new inline-widgets cap), chat.send stamps them into
the run context, and every tool assembly path (embedded runner, queued
followups, Codex app-server harness, plugin-only construction plans) drops
tools whose requiredClientCaps the originating client did not declare. The
Canvas plugin ships the first such tool, show_widget: agents pass SVG or an
HTML fragment plus a title; the plugin hosts it as a bounded, retention-scoped
Canvas document and returns the existing canvas preview handle, which web chat
renders as a sandboxed iframe fitted to the widget's reported content height.
Widget frames never get allow-same-origin (per-preview sandbox ceiling,
including the sidebar path) and the Canvas host serves widget documents with a
CSP sandbox header so direct navigation runs in an opaque origin. Verified
live end-to-end on a Testbox with gpt-5.5 (screenshots on the PR). CLI-backed
model backends do not carry client caps yet and stay fail-closed (#102577).
Closes#101790
Feedback pass on the sessions redesign: status pills become a plain colored
dot + label (green pulse for live, green dot with neutral label for done,
muted idle, red failed) in both the roster and the drawer hero, and the
Runtime column moves out of the table into the row drawer as Runtime (agent
runtime) plus Run duration. Runtime stays searchable. Responsive breakpoints
reflow for the 7-column roster and the actions column shrinks to the chevron
and row menu that landed in #102564.
Closes#102654
* fix(ui): align split-view toolbar segments with the pane edges they label
* fix(ui): track pane gutter and horizontal overflow in the split toolbar
* fix(ui): reset split toolbar scroll offset on mode flips and reserve docked terminal space
* fix(ui): scroll clipped split panes into view when their toolbar segment gains focus
Corner radius is owned by themes; drop the browser-local multiplier that
scaled all --radius-* variables (added in #49436). Stale borderRadius
localStorage keys are ignored and dropped on next settings write.
* fix(ui): keep workboard capture text truncation UTF-16 safe
Replace raw .slice(0, N - 3) with truncateUtf16Safe in
clampSessionCaptureText and clampSessionCaptureTitle to prevent
lone surrogates when emoji cross the truncation boundary.
Completes the UTF-16 hardening started in #101685, which added the
import but only covered buildCardSessionLabel.
* test(ui): cover workboard UTF-16 boundaries
* docs(changelog): note Workboard UTF-16 fix
* docs(changelog): leave release notes to release flow
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ui): add session context menu to sessions page rows
Extract the sidebar session menu into a shared <openclaw-session-menu>
component and open it from a kebab button or row right-click on the
Sessions page, replacing the per-row unread/fork/pin/archive/workboard
icon buttons. Adds Open chat, Rename, Move to group, and per-row Delete
to the page; guards (main/global/active-run) now flow through
canArchiveSessionRow on both surfaces.
* fix(ui): retarget gateway session after deleting the current session from the sessions page
Deleting the currently selected session via the new row menu (or the
bulk bar) left gateway.snapshot.sessionKey pointing at the deleted key;
fall back to the agent main session, mirroring the archive path and the
sidebar delete path.
* fix(ui): keep streamed talk voice-turn transcripts readable
Talk voice turns collapsed to one character per line in the macOS app
(WKWebView) while streaming: WebKit never re-runs shrink-to-fit intrinsic
sizing for a grid flex-item as its content grows, so the turn froze at
first-delta width. Convert the turn to a flex row (same visual layout).
Assistant transcript deltas also merged through the user-ASR word-boundary
spacing heuristic, mangling verbatim fragments ("Chat G PT"). Assistant
entries now concatenate fragments verbatim, space only sentence-punctuation
joins, and accept both full-transcript and tail-fragment finals.
Fixes#102556
* fix(ui): keep assistant talk fragments strictly verbatim
Drop the sentence-punctuation separator: it invented characters in real
content (Version 1. 2, docs.openclaw. ai) and Google Live tail-finals
could never heal it. The punctuation-spacing heuristic remains user-only.
The Control UI sessions roster now leads with operator telemetry: overview
tiles (sessions / live / unread / tokens), per-kind row avatars with a live-run
dot, compact token values with context-usage meters (ok/warn/danger at
65%/85%), initial-load skeleton rows, and icon-led empty states. Stale token
snapshots render as ~approximations with a neutral meter and never drive
warning tones, matching the chat composer convention; the overview token sum
counts only known snapshots (~ when partial, n/a when none). Label chips
ellipsize instead of overflowing sticky columns; <=500px drops the avatar to
preserve the key column budget.
Closes#102517
The sidebar brand row (logo + wordmark) returned in 8295a12a83 but the
macOS-only titlebar mark from #100648 stayed, so the Mac app showed the
claw icon twice. Delete the sidebar-native-brand element and its
CSS gate; the sidebar brand row is the single brand surface and the
native titlebar strip holds only the traffic lights.
* fix(ui): keep clampText/truncateText surrogate-safe at emoji boundaries
clampText and truncateText in format.ts used String.prototype.slice(0, N)
to truncate user-visible text. When the truncation boundary falls inside
a UTF-16 surrogate pair (emoji, CJK extended, etc.), the resulting
string contains a dangling surrogate that browsers render as U+FFFD (�).
Replace .slice(0, ...) with truncateUtf16Safe(...) which skips past
incomplete surrogate pairs so the truncation point always falls on a
code-point boundary.
Affected UI surfaces: markdown rendering, command descriptions, skill
summaries, exec-approval rules, tool-stream output previews.
Add surrogate-safe truncation test cases for both functions using a
real emoji at the exact truncation boundary.
* test(ui): tighten UTF-16 truncation coverage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix: keep emoji intact at remaining text truncation boundaries
Replace .slice(0, N) with truncateUtf16Safe() in five remaining
user-visible text truncation sites that were missed by earlier
UTF-16 safety sweeps:
- restart.ts: restart reason text (2 sites)
- chat-composer.ts: reply target text preview
- chat-thread.ts: thread text preview + message text extraction
All sites truncate user-generated content where emoji or other
supplementary-plane characters could produce lone surrogates.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: complete UTF-16-safe restart and chat boundaries
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
First connects backed by stored auth (URL/session token, password, or an
approved device token) now paint a small wiggling OpenClaw mark (delayed
fade-in, reduced-motion aware) until the hello resolves. The login gate
still owns credential-less first opens, rejected credentials, and manual
gate submissions.
Closes#101847
The idle Talk mic button used a full-strength accent ring that visually
competed with the recording (stop) state. Mute the idle ring to 40%
accent and ramp hover to 70% so idle -> hover -> recording escalates.
Formatting proof: oxfmt --check clean on ui/src/styles/chat/layout.css
on Testbox tbx_01kwyvx4pbdmb3yhjff6gvzvny (worktree has no node_modules,
so the format hook cannot run).