* fix(mac): prerelease-exact launch resolution and policy-aware update ownership
* fix(ui): keep update affordance when nav is hidden and fall back on native decline
* chore(i18n): sync native inventory
* feat(control-ui): add chat selection popup with More details and Ask in side chat
* fix(control-ui): keep BTW pending card visible and clear it on resultless terminal runs
* fix(control-ui): route failed BTW runs to an error side-result card and ignore stale side results
* fix(control-ui): correlate BTW pending cards by pre-generated run id and suppress superseded runs
* fix(control-ui): retire abandoned BTW runs so late side events never reach the transcript
* chore(docs): regenerate docs map for btw selection-popup section; fix selection-popup test lint
The sidebar footer icon row got crowded with the inline branch label.
The dev-checkout branch now renders on a slim tinted strip above the
icon bar with a git-branch icon, keeping the full name readable and
the footer controls uncluttered.
* feat: sidebar update card (web + macOS) with app-first mac update flow and Sparkle beta track
Squashed from claude/update-notification-display-c6cfb9 after semantic merge
with #104178 (channel-aware CLI installs). See PR #104171 body for details.
* chore(i18n): resync generated inventories after rebase
* chore(i18n): resync locale metadata after rebase
Source-checkout (non-release) gateways now report their git branch in the
Control UI bootstrap config; the sidebar footer renders it in red next to
the connection dot. Package installs, main/master, and detached HEAD never
show it.
* fix(ui): move floating chat rail toggles into a single top row inside a titlebar band
The workspace and background-tasks openers stacked vertically over the
chat thread and covered the first message. They now sit side by side in
one 28px row, the thread reserves a ~44-52px top inset that keeps them
clear of bubbles, and empty band background starts a native window drag
in the macOS app (same beginNativeWindowDrag contract as pane headers).
Split panes keep their compact inset since the pane header already owns
the top row.
* test(ui): own the titlebar-band drag in native-window-drag and cover it with tests
Moves the top-inset drag logic from an inline chat-thread closure to
beginNativeWindowDragFromTopInset next to its sibling handler, and adds
jsdom regression tests for in-band background presses, below-band
presses, and presses on scrolled-under content.
Adds a git-backed session diff panel to the Control UI, complementing the existing PR status chips.
New sessions.diff gateway method (operator.read): resolves the session checkout and returns structured per-file diffs (status, renames, +/- counts, capped unified patch) of branch + uncommitted + untracked work against the default-branch merge base. Hardened against git textconv execution and hardlinked out-of-tree content leaks; handles repos before their first commit via the empty-tree base.
Control UI renders a "Changes" panel in the chat detail sidebar with collapsible per-file diffs, hunk-gap markers, stat chips, and untracked/binary badges, gated on gateway method advertisement. Schemas additive (Swift models regenerated), 20 locales translated.
Closes#104182
* fix(ui): resolve global-alias session kinds for chat avatar mode
* fix(ui): borrow only the session kind from alias-matched global rows
* fix(ui): gate the global-row kind fallback on configured global scope
* fix(ui): classify global-scope aliases without requiring a listed row
Hovering a model row in the chat model picker showed a tooltip that just
echoed the row's own label. The shared openclaw-tooltip now skips opens
whose content is already fully visible inside the trigger, while keeping
the full-text tooltip for clipped/ellipsized labels.
* feat(ui): add Open PR, Open in editor, and numbered group shortcuts to the session menu
* fix(ui): lift session menu to popover top layer and use shadow tokens
* feat(ui): hide chat avatars in direct sessions
* fix(ui): classify direct threads from full history, not the render window
* fix(ui): classify direct threads by canonical session kind
* fix(ui): match session metadata across equivalent alias keys in the chat thread
* fix(ui): classify unmatched chat sessions by canonical key shape
* fix(ui): keep avatars in global sessions that can aggregate group senders
* docs(ui): note global-alias limitation on chat avatar classification
* feat(ui): add gateway browser panel with annotate and inspect modes
* chore(i18n): translate browser panel strings
* fix(ui): address browser panel review findings (stable tab handles, stale view guard, inspect click suppression)
* fix(ui): make the browser panel reload button reload the remote page
* fix(ui): tile the browser dock against the terminal dock and re-probe evaluate per connection
* fix(ui): label page-reported prompt text as untrusted and accept host:port URL entries
* fix(ui): sanitize inspected selector fragments in the annotation prompt
* fix(i18n): retranslate annotation prompt keys so the page-reported provenance label survives localization
* docs: regenerate docs map for the browser panel section
* chore(i18n): reconcile locale bundles with latest main after rebase
* style(ui): format chat-pane imports after rebase
* chore(i18n): refresh locale metadata after rebase
The workspace rail and message detail panel keyed layout off viewport
media queries, so narrow split-view panes and compact windows got
crushed side-by-side columns, and below 1120px the rail was
display:none with no way to reach workspace files.
Chat panes now measure their own width with a ResizeObserver: under
800px the rail presents as the existing bottom-dock strip (side-dock
controls hide), and when the chat + detail split has under 680px the
detail panel stacks below the thread with a horizontal resize divider.
The phone full-screen detail takeover is unchanged. Bottom-strip path
and summary rows no longer flex-shrink into clipped text.
Closes#104023
* feat(webui): reintroduce opt-in AI purpose titles for tool calls
Restores the chat.toolTitles path removed in #103821, gated behind the new
gateway.controlUi.toolTitles opt-in (default false) so tool rendering stays
fully deterministic with no background model calls unless an operator enables
it. Disabled gateways answer { titles: {}, disabled: true } without loading
the completion runtime, and clients stop asking for the session.
When enabled, titles use canonical utility-model routing: an explicit
utilityModel (operator-chosen provider, like every utility task), else the
session provider's declared small-model default, honoring per-session model
overrides and auth profiles; utilityModel "" disables titles and malformed
refs fail closed — never the primary model. Tool inputs are redacted with the
tools-mode redactor before cache keys or prompts, caller ids are bounded and
never reach the model, and results cache in the per-agent SQLite
cache_entries so repeat views never re-bill.
Also completes two crestodian model-input mock factories that leaked into
sibling tests under shared-registry CI shards.
Fixes#103987
* fix(webui): redact tool-title inputs before truncation
* fix(macos): drag the window from split-view pane headers
In the macOS app, split-screen pane header rows (session title strip) now
start a native window drag instead of selecting the title text. The Control
UI posts an openclawWindowDrag script message on mousedown over passive
header chrome; the dashboard window validates the trusted main-frame source
and the in-flight left-mouse press before calling NSWindow.performDrag.
Pane headers are chrome, so they are also no longer text-selectable.
* refactor(macos): move the dashboard failure page out of the window controller
SwiftLint type_body_length: the drag-message handler pushed
DashboardWindowController past 800 lines; the failure-page HTML is
presentation-only and now lives in DashboardFailurePage.
* feat(ui): hover-revealed chat message meta with relative timestamps
* fix(ui): derive relative chat timestamps from the injected clock
* fix(ui): date far-future chat timestamps instead of clamping to just now
* fix(ui): invalidate memoized chat thread on relative-time ticks
* fix(ui): keep relative-time ticks out of the chat live region
* feat(ui): worktrees page owners and creation, structured snapshot failures, preserved-checkout cleanup
- Worktrees settings page gains an Owner column (manual/Workboard/session
with a link into the owning chat) and a New worktree form with repo
default, optional name, and a worktrees.branches-backed base picker
- non-forced worktrees.remove snapshot failures are a structured
{ removed: false, snapshotError } result (typed WorktreeSnapshotError in
the service) instead of error-string sniffing in the UI
- deleting a session whose dirty/unpushed checkout was preserved now
reports worktreePreserved through the session capability and offers an
explicit force removal instead of silently orphaning the checkout
Part of #103431
* fix(ui): report preserved checkouts from batch session deletes
deleteMany now aggregates worktreePreserved results, and the Sessions
page surfaces one notice pointing at Settings -> Worktrees instead of
silently orphaning dirty checkouts during bulk deletion.
Part of #103431
* fix(ui): reconcile generated locale artifacts after rebase
Open Settings from anywhere in the Control UI with Shift-Command-Comma while preserving browser-owned Command-Comma and existing shortcuts.
Prepared head SHA: 769a4fec5f
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
* feat(ui): sessions sidebar redesign with agent sections, smart groups, and draft sessions
- agents become collapsible top-level sidebar sections; expanding an agent
browses its sessions without navigating, replacing the hidden scope select
- built-in smart groups: one section per channel (rows keep their chat
titles), a Work section for worktree/exec-node sessions with a
repo/branch/node subtitle, custom groups, and Chats
- session names never show raw keys or peer ids; DM fallbacks shorten ids,
dashboard sessions read 'New session', unnamed work sessions read as
their checkout
- one + opens the new-session draft dialog: agent, exec host (paired
system.run nodes), folder, worktree toggle with base-branch picker
(worktrees.branches) and optional name; the first message creates the
session and starts the run in one sessions.create call
- custom group catalog/order moves to the gateway (sessions.groups.*) with
a one-time localStorage migration; rename/delete update members
server-side instead of client-side paging
Part of #103431
* fix(ui): resolve dragged sessions across browsed agent sections
Dropping a row dragged out of a non-active agent section now finds the
session in the per-agent row cache instead of only the active scope, so
the category patch is applied instead of silently doing nothing.
Part of #103431
* fix(ui): propagate group catalog changes to open clients
sessions.groups.put/rename/delete now always broadcast a groups-change
event, and the session capability reloads the gateway-owned catalog when
one arrives, so another browser's group create/reorder/rename/delete no
longer leaves this client on a stale snapshot for the rest of the
connection.
Part of #103431
* docs: restore new session dialog section after rebase
* fix(ui): translate new sidebar/session strings and refresh docs map
Real locale translations for the new-session dialog and sidebar keys
(the fallback gate requires zero recorded English fallbacks), plus the
regenerated docs map for the new Control UI section.
Part of #103431
* fix(ui): repair locale metadata after rebase conflict resolution
* fix(ui): merge mainline locale keys with the sidebar redesign strings
* fix(ui): refresh raw-copy baseline for mainline tool-card strings
* fix(ui): reconcile generated locale artifacts after rebase
Kind-aware tool rows (terminal-style commands with wrapper stripping and
display highlighting, file edits with inline numbered diffs and diffstat,
write previews, key-value args), aggregate group summaries with live run
status, and a new batched chat.toolTitles gateway RPC that titles complex
calls via the configured utilityModel or the OpenAI Luna default (gated to
OpenAI-primary agents, cached in the per-agent SQLite cache_entries).
Also fixes two transcript pairing bugs: result blocks now inherit call
id/name/details at merge time, and results pair with any open call in the
current tool run so parallel calls render as single rows.
Fixes#103554