Commit Graph

5746 Commits

Author SHA1 Message Date
RoboClaw af36abd6a0 fix(ui): center compact permission icons (#130137)
Center icon-only permission triggers in compact chat and new-session composer layouts, with browser regression coverage for both responsive states.

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-26 07:35:31 -07:00
Peter Steinberger c91edb5cd5 fix(ui): restore visible gateway status in the sidebar footer and announce restarts (#130025)
* fix(ui): restore visible gateway offline status and announce restarts in the sidebar footer

The sidebar footer has been the canonical offline indicator since #112600
(which deleted the top connection banner in its favor), but #125070's footer
compaction made it sr-only + aria-hidden - invisible to everyone. Restore the
visible red offline pill (click-to-retry, queued count, redacted error
tooltip) and add a restart-aware amber state: the gateway already broadcasts
shutdown { restartExpectedMs } before restarting and refuses drain-phase work
with reason "gateway-restarting", so the Control UI now shows "Restarting..."
immediately - before the socket drops - and degrades to the offline pill when
a restart overruns max(3x restartExpectedMs, 15s).

One shared renderSidebarConnectionStatus serves the main and settings
sidebars; the dead sr-only spans and clipped live region are deleted. The
drain reason strings become shared gateway-protocol constants. The e2e mock
now passes Vite's vite-hmr client socket through to the native WebSocket so
going offline in source-served suites no longer triggers a dev-client page
reload mid-test.

* refactor(ui): delete dead sr-only footer subtitle spans and fit the startup budget

The connected-state identity subtitle spans (gateway health dot, name,
build subtitle) were the same sr-only + aria-hidden dead-markup class the
previous commit fixed for offline: invisible to sighted users and screen
readers, with the identity button's aria-label already carrying the same
information. Delete them plus their orphaned CSS; tests now assert the
aria-label contract.

The e2e mock's Vite dev-client socket passthrough keys on the vite-hmr
subprotocol. The gateway store drops validation the shared timer clamp
already owns and re-arms the restart deadline on every fresh drain
rejection (server still says restarting, so amber stays honest). The
startup-budget baseline refresh (340901 -> 341446, jitter-safe max of
three builds) records the ~530 B this feature legitimately adds over the
512 B per-change tolerance; reason stored in the baseline file.

* fix(ui): keep ordinary gateway stops on the offline pill path

ClawSweeper finding: every shutdown broadcast armed the amber Restarting
state, so a deliberate stop hid the Offline/Retry action for the 15s
deadline floor. The amber state now arms only when the broadcast carries a
numeric restartExpectedMs; ordinary stops flow through the normal offline
pill. The server omits restartExpectedMs on non-restart shutdowns instead
of broadcasting null, aligning the wire with ShutdownEventSchema's
optional integer.

Also rewires build-info-unicode.e2e to the identity aria-label (the
visible subtitle span it asserted was deleted as dead markup) and splits
the restart-state store tests into gateway-store.restart.test.ts with a
shared test-support harness to respect the max-lines cap.

* chore(ui): tighten startup gzip baseline after composer-queue rebase

The combined tree measures 340754-340770 B across builds, below the
341489 B baseline main recorded for the composer stack; pin 340810 B
(max observed + jitter headroom) so the ratchet reflects reality.

* fix(ui): unexport the test-support fake gateway client (knip dead export)
2026-08-26 07:31:30 -07:00
Jesse Merhi f8550bc11d fix(ui): support slash commands and skills inside normal drafts (#122425)
* feat(ui): support slash commands inside normal drafts

* fix(ui): preserve inline skill colon completion

* refactor(ui): extract slash menu DOM ids

* fix(ui): account for Absolutely theme startup CSS

* fix(ui): sync typing after inline command dispatch

* test(commands): preserve fs-safe exports in agent delete mocks
2026-08-26 14:27:32 +00:00
Peter Steinberger ba8e03fb9c fix(workers): remote sessions preserve destination and access policy (#130148)
* fix(workers): preserve remote placement and delegated authority

* test(commands): preserve filesystem exports in deletion mocks
2026-08-26 07:26:58 -07:00
RoboClaw f09ec76de4 fix(ui): keep limited-access dismissal through reconnect (#130127)
* fix(ui): preserve access snooze through reconnect

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* test(commands): preserve fs-safe mock exports

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-26 07:06:06 -07:00
Vyctor H. Brzezowski 1d2651e886 improve(ui): simplify chat image preview overlay (#129990)
* improve(ui): simplify image lightbox chrome

* fix(ui): polish image lightbox interactions

* fix(ui): harden image lightbox interactions

* fix(ui): align image overlay with viewport

* fix(ui): label managed image preview honestly
2026-08-26 10:53:21 -03:00
Ayaan Zaidi 6a8278d822 feat(cron): schedule the skill collection review as a system-owned job (#130030)
* feat(skills): expose the collection review as a single cron-driven run

The weekly Skill Workshop collection review lived in a bespoke gateway
maintenance loop with its own interval, due-state check, and gateway admission
wrapper. Replace it with `runSkillCollectionReviewForAgent`, one run that keeps
the workspace claim, identity check, and status recording and reports
`{ status, summary }` to its caller. The cron timer that will own the schedule
already runs under gateway root work admission, so the extra wrapper and its
drain test go away.

* feat(cron): schedule the skill collection review as a system-owned job

Fold the weekly Skill Workshop review into cron the same way the heartbeat
monitor is: a `skillCollectionReview` payload kind, one system-owned job per
workspace (first agent of each canonical workspace) reconciled at gateway
startup, enabled only when the workshop runs in `auto` mode, on a 7-day
`every` schedule with the heartbeat phase spread. The timer invokes the
review runner directly; cron clients cannot create, patch, or remove
system-owned payloads, and the job shows up in `openclaw cron list`.

* fix(cron): warn when skill reviews are disabled

* fix(cron): preserve skill review failure details

* fix(cron): narrow serialized payload variants

* test(cron): tighten skill review coverage

* fix(gateway): reconcile skill reviews on config reload

* fix(cron): fence revoked skill reviews

* fix(cron): satisfy system payload type coverage

* fix(ui): protect system-owned cron jobs

* test(gateway): prove review revocation fence

* fix(gateway): revoke skill reviews before config publish
2026-08-26 18:43:54 +05:30
Vyctor H. Brzezowski 18beca5350 improve(control-ui): attach the message queue to the composer (#127823)
* improve(control-ui): align attached composer queue with current main

* fix(ui): preserve attached queue state

* improve(ui): refine composer interactions

* fix(ui): stabilize composer skill references

* fix(ui): restore composer queue and override states

* fix(ui): refine composer model and permission pickers

* fix(ui): unify composer stack borders and underlaps

* fix(ui): merge context compaction control

* fix(ui): polish new-session composer states

* fix(ui): retire removed composer contracts

* test(ui): assert model trigger loading skeleton

* fix(ui): complete composer review round two

* fix(ui): bound transcript under composer stack

* fix(ui): keep model picker open during refresh

* Revert "fix(ui): bound transcript under composer stack"

This reverts commit 4cffa4314f.

* revert(ui): restore transcript clip above composer

* fix(ui): lightly underlap transcript behind composer

* fix(ui): fade transcript beneath composer stack

* fix(ui): surface dictation failures in composer

* fix(ui): deepen transcript underlap safely

* fix(ui): move voice availability alerts into picker

* fix(ui): span transcript fade beneath stack corners

* fix(ui): consume dictation hold click tail

* test(ui): cover unavailable dictation hold tail

* fix(ui): anchor microphone hover reveal

* fix(ui): keep microphone picker stable during hold

* fix(ui): align dictation actions with composer send

* fix(ui): release dictation composer before remote close

* fix(ui): stabilize model picker during catalog refresh

* fix(ui): unify switch accent colors

* fix(ui): accelerate hold to dictate

* feat(ui): shimmer dictation listening status

* fix(ui): make dictation shimmer seamless

* fix(ui): reconcile composer stack with current main

* test(ui): drop obsolete reasoning reset expectation

* fix(ui): reconcile queue rebase with current main

* test(ui): align composer controls with current main

* fix(ui): reconcile composer rebase checks

* style(ui): format dictation control test

* fix(ui): keep model catalog state acyclic

* fix(ui): align composer e2e contracts

* test(ui): align full composer e2e suite

* test(ui): align remaining composer e2e contracts

* fix(ui): stabilize queued message editing

* test(ui): select queued edit text with a portable chord

Control+A is caret-to-line-start on macOS textareas, so the edit tests
inserted at position 0 and saved concatenated text on Mac rigs while
Linux CI selected-all and passed. ControlOrMeta+A selects on every
platform.

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 06:08:59 -07:00
Vyctor H. Brzezowski 1c1673cc35 fix(ui): structure chat transcript attachments by kind (#127076)
* fix(ui): structure chat transcript attachments by kind

* Refine attachment previews

* Refine attachment cards and previews

* Refine audio attachment controls

* Refine attachment cards and previews

* Move audio timer before progress

* Add attachment link fixture

* Make attachment downloads secondary

* Add attachment file icon system

* Tune attachment icon sizing

* Compact attachment preview headers

* fix(ui): align large attachment card inset

* feat(ui): color attachment glyphs by family

* feat(ui): use icon-only attachment actions

* fix(ui): space compact attachment metadata

* fix(ui): finalize attachment card contracts

* Revert "fix(ui): finalize attachment card contracts"

This reverts commit 41b28970b5.

* fix(ui): reduce mobile waveform density

* fix(ui): restore native image rendering

* fix(ui): align and enlarge chat images

* fix(ui): restore open labels on file cards

* fix(ui): simplify labeled attachment actions

* fix(ui): size waveforms from container width

* fix(ui): update attachment open icon

* fix(ui): use continuous corners for chat images

* fix(ui): preserve sidebar video playback

* fix(ui): retain attachment kind in sidebar

* fix(ui): refresh open sidebar attachments

* fix(ui): bound delimited attachment previews

* fix(ui): harden attachment preview lifecycle

* fix(ui): reset lazy attachment previews

* style(ui): format attachment preview code

* fix(ui): align attachment sidebar content

* fix(ui): constrain CSV attachment previews

* fix(ui): keep text attachments compact

* fix(ui): block external document previews

* fix(ui): verify document iframe loads

* fix(ui): render honest audio waveforms

* test(ui): align attachment player expectations

* test(ui): exercise same-origin CSV previews

* test(ui): use browser-origin CSV fixtures

* style(ui): format attachment fixes

* fix(ui): normalize document preview URLs

* test(ui): clarify CSV fixture indices

* fix(ui): stabilize audio time label

* fix(ui): preserve external attachment downloads

* fix(ui): avoid eager video attachment loading

* fix(ui): parse TSV previews by attachment type

* fix(ui): retry managed attachment resolution

* style(ui): format attachment review fixes

* fix(ui): pass attachment type to table preview

* test(ui): normalize compact card label

* fix(ui): preserve sidebar attachment downloads

* test(ui): align attachment card expectations

* style(ui): order attachment imports

* fix(ui): isolate document attachment previews

* fix(ui): release Files attachment ownership

* fix(ui): resolve sidebar attachments from live context

* fix(ui): preserve attachment video aspect ratios

* style(ui): format attachment surfaces

* fix(ui): apply renewed video tickets on interaction

* fix(ui): restore visible attachment waveforms

* fix(ui): fade truncated CSV previews

* fix(ui): lazy load attachment previews in view

* fix(ui): preview image attachments inline

* test(ui): remove link attachment fixture

* fix(ui): block network in HTML attachment previews

* fix(ui): keep image hover actions stable

* feat(ui): preview SVG attachments safely

* perf(ui): split attachment preview styles

* fix(ui): harden attachment preview ownership

* refactor(ui): deslop attachment rendering

* refactor(ui): render transcript attachments as cards

* fix(ui): keep remote SVG attachments CSP-safe

* test(ui): keep chat pane suite within lint limit

* test(ui): cover Files shortcut cleanup

* fix(ui): normalize structured SVG metadata

* fix(ui): classify SVG attachments by source

* fix(ui): preserve attachment type precedence

* perf(ui): keep attachment CSS out of startup
2026-08-26 09:59:11 -03:00
RoboClaw baf06ed4f9 fix: keep Control UI routes usable across updates (#129922)
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-26 05:47:54 -07:00
Peter Steinberger 3b22a6cdd3 fix(gateway): keep loaded conversations responsive under heavy load (#130071)
* fix(gateway): keep loaded sessions responsive under heavy load

* test(gateway): narrow concurrency lane and stabilize inherited lint

* fix(ui): retain presented snapshots during background prefetch

* chore: respect release-owned root changelog policy
2026-08-26 05:42:01 -07:00
Peter Steinberger 7b1e562b86 fix(gateway): keep session events within their owning agent (#130039)
* fix(gateway): preserve authoritative session owner in events

* fix(gateway): preserve legacy session event scopes

* test(gateway): verify recipient authority before session broadcasts

* test(gateway): complete session broadcaster fixture types

* test(ui): type session goal ownership fixture
2026-08-26 05:39:00 -07:00
RoboClaw ed56f3c001 fix(ui): let mobile workboard scroll with toolbar (#129497)
* fix(ui): let mobile workboard scroll with toolbar

* test(ui): split mobile workboard scroll coverage

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>
Worked on by:
- @Solvely-Colin

* fix(ui): narrow mobile workboard scroll ownership

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>

Worked on by:
- @Solvely-Colin

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Colin Johnson <colin@solvely.net>
Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>
2026-08-26 08:11:48 -04:00
Peter Steinberger f6c15df332 fix(ui): clear stale session-list errors after Gateway recovery (#130004)
* fix(ui): clear stale filtered-session errors after recovery

* fix(ui): keep session error owners within lint boundaries
2026-08-26 05:08:59 -07:00
RoboClaw 44d60a29e6 fix: preserve unread reminder for open sessions (#129386)
* fix: preserve manual unread markers in open sessions

* fix: distinguish explicit session reads

* fix(ui): gate unread contract on gateway capability

* perf(ui): keep server capabilities out of startup bundle

* test(gateway): keep agent fixtures roster-consistent

* fix(sessions): preserve legacy read compatibility

* test(gateway): type agent fixture configs

* fix(ui): remove unread gateway fallback

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* test(infra): avoid fixed SSH tunnel port

* fix(ui): acknowledge unread after history commit

* docs: clarify unread upgrade boundary

* test(ui): drive mobile session menu by tap

* fix(ios): remove stale read reconciliation call

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-26 05:04:39 -07:00
RoboClaw e3a3de0fe7 fix(ui): keep Inbox clear of title text (#129992)
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
2026-08-26 03:20:32 -07:00
Peter Steinberger 54195510fa perf(ui): keep startup bundles within enforced budgets (#130037) 2026-08-26 03:05:45 -07:00
Peter Steinberger 8bf8e80e1d feat(cron): link chat notifications to Control UI run inspection (#130049)
* feat(cron): link chat notifications to Control UI run inspection

When gateway.publicOrigin is configured and the Control UI is enabled,
channel-delivered automation and task notifications append an
'Inspect: <url>' line so operators can click straight to the run record:

- cron failure alerts and command/script completion announcements link to
  /automations?job=<id>&run=cron:<id>:<startedAt> (appended after summary
  redaction so the redactor cannot strip the link)
- isolated agent run completions link to the run's exact session route
  (appended only after silent/NO_REPLY suppression, immutably, so a
  suppressed run cannot become a visible announcement)
- direct-channel task terminal notifications link to the task's child
  session; session-queued events stay link-free
- the automations page adopts ?job=&run= deep links (loader search pass-
  through, one-shot adoption after jobs load, History tab + run highlight)

No new config surface: the URL derives from the existing
gateway.publicOrigin + gateway.controlUi owner in control-ui-link-base.

* fix(cron): match linked runs by execution id and dedupe delivery runtime type

Live verification caught that ledger run ids are public run ids (receipt
UUIDs, manual:<...>) while notifications embed the execution id
(cron:<jobId>:<startedAtMs>), so the linked-run highlight never matched:
the automations page now resolves the execution id against the entry's
recorded runAtMs, with regression tests for both id spaces.

Also: move the isolated-delivery inspection-link append into the payload
normalization owner (keeps delivery-dispatch under the max-lines limit),
and replace the duplicate TaskRegistryDeliveryRuntime declaration in
task-registry.test-support with a type-only import of the canonical type
(the duplicate hid the new optional resolver from the tsgo test shard).
2026-08-26 02:59:51 -07:00
Peter Steinberger f4cbdefc79 test(ui): trim duplicate routing coverage and stabilize cron E2E (#129958)
* test(ui): remove duplicate browser fallback case

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b5-3918-749f-90bd-5c9ac1dced16

* test(ui): observe cron save outside CDP promise

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b5-3918-749f-90bd-5c9ac1dced16

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 02:49:29 -07:00
Peter Steinberger 68f17b01cc fix(gateway): permission modes work on rootless sessions via workspace default root (#129994)
* fix(gateway): permission modes work on rootless sessions via workspace default root

Session permission modes hard-required a recorded sessionRoot, which only the
sessions.create RPC ever records. Channel sessions, pre-#124909 rows, and
exec-node sessions are rootless, so runs failed closed with 'session permission
mode requires a recorded session root' (rows poisoned before the #128149 guards
were permanently bricked) and operators could not set a mode on channel
sessions at all.

The mode semantics are root-free; every containment consumer already falls
back to the workspace, and the remote worker launcher already derives its
containment root from placement.remoteWorkspaceDir. Make that the canonical
rule: a recorded sessionRoot pins worktree/explicit-cwd boundaries, and a
rootless session's boundary is the agent's canonical workspace resolved where
the run is prepared.

- embedded runner + compaction: default policy root to realpath(workspace)
  instead of throwing/dropping the mode; bricked rows self-heal
- gateway: delete the patch/create/reset rejection guards and
  session-permission-policy.ts entirely
- codex plugin: helpers take a required defaultRoot (agent workspace fact,
  never a requested thread cwd); delete the four root throws; sandbox context
  for conversation bindings now derives from the agent workspace
- ui: drop the rootless-rejection surface from the permission picker
- docs: permission-modes boundary contract updated

* fix(agents): split workspace path identity out of the kysely state store

Heals main: #116650 made memory-host-sdk/dreaming.ts import
resolveWorkspaceStateIdentity from workspace-state-store, statically pulling
kysely into the memory-core doctor-contract closure and failing the
doctor-contract-closure-guard lane on origin/main. The identity helpers are
pure path/crypto code; move them to workspace-state-identity.ts and repoint
all importers (dreaming, event-store, onboarding recommendations, workspace
migrations, legacy state).

* fix(ui): drop the unused permission picker sessionRoot prop and dead i18n keys

check-prod-types caught the New Session dialog still passing the removed
sessionRoot picker prop; the prop and the permissionControls.rootLabel /
sessionRoot strings have had no consumers since #124301.

* fix(codex): resolve rootless permission boundaries from agent config, not bind cwd

ClawSweeper P1: /codex bind --cwd stores the requested path as the binding
workspaceDir, which the previous commit then trusted as defaultRoot — letting
a bound thread's requested cwd become (and widen) the rootless permission
boundary. resolveConversationAppServerRuntime now owns the fact: it resolves
the selected agent's canonical workspace from config (falling back to the
plugin's configured default workspace), and the caller-supplied
agentWorkspaceDir parameter chain is deleted so no call site can pass a
tainted value. Recovery of recorded roots is unchanged (pinned roots stay
pinned); the moved-history test now proves the bind-cwd clamp lands on the
agent workspace.
2026-08-26 02:25:57 -07:00
Peter Steinberger 33a24b6d8e fix(ui): remove stale startup CSS (#130032)
Amp-Thread-ID: https://ampcode.com/threads/T-01a03a19-be71-77a7-a886-4c688012c709

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 02:18:28 -07:00
Peter Steinberger 9b3445de4d fix(ui): remove unused startup styles to restore css budget (#130027) 2026-08-26 01:58:20 -07:00
Peter Steinberger 1605dbd3ef chore(deps): refresh dependencies after seven-day cooldown (#129941)
* chore(deps): refresh dependencies after cooldown

* chore(deps): refresh sherpa-onnx runtime to v1.13.6

* test(ios): refresh Fastlane pin expectations
2026-08-26 01:37:37 -07:00
Peter Steinberger 1097b6f896 refactor(ui): centralize audio blob leases (#129980)
Amp-Thread-ID: https://ampcode.com/threads/T-01a037b7-55c9-77ca-b84a-467a6aed97fb

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 01:13:35 -07:00
Peter Steinberger 81fb7e69c4 improve(ui): fold the sidebar update action into the Inbox (#129977)
* improve(ui): fold the sidebar update action into the Inbox

The footer update circle duplicated the Inbox's update entry: same
attention state, same dismissal, one extra permanently-tinted control
plus layout shims reserving space for it. The update now surfaces only
as an Inbox entry — badge for attention, full action row (update, hold,
review, dismiss-until-restart) in the panel.

Deletes the footer button, its hover-dismiss, sidebar-footer-update.css,
and the 80px/88px width compensations; moves the floating-cluster rules
to sidebar-attention-floating.css and the build-chip text rules next to
their base class in layout.css. Removes the dead startSidebarUpdateAttention
shortcut and two orphaned i18n keys.

* fix(ui): integrate #129920 top-left alignment with the Inbox-only footer

Ports the web-chrome floating-cluster geometry to sidebar-attention-floating.css
and points the titlebar alignment test at the always-present Inbox button; the
update no longer changes footer geometry, so its scenario plumbing goes.
2026-08-26 01:12:29 -07:00
Peter Steinberger 80ecc7c8b5 feat(control-ui): add the Absolutely theme with self-hosted typography (#129885)
* chore(control-ui): vendor Space Grotesk and Lora webfont subsets

Adds the woff2 faces the Absolutely theme paints in, plus the stylesheet that
declares them. Both families are SIL Open Font License 1.1 and ship unmodified,
with their upstream license text beside the files as the license requires.

Latin and latin-ext subsets only, 184 KB total. Serving these from the gateway
rather than a font CDN keeps font-src 'self' intact, avoids a third-party
request on every load, and works on an offline or LAN-only gateway.

The stylesheet is a static asset rather than a bundled import: it is linked at
runtime only while a theme that declares faces is active, so it stays out of
the startup CSS budget and out of every other theme's load.

* feat(control-ui): add the Absolutely theme with self-hosted typography

Adds a fourth built-in Control UI theme: terracotta clay on warm graphite in
dark mode, burnt clay on ivory in light mode, with its own typography — Space
Grotesk for chrome and Lora for chat prose.

Typography is theme-owned rather than global. A new --font-chat token defaults
to --font-body, so every other theme is byte-for-byte unchanged, and the face
declarations live in a static stylesheet that the theme applier links only
while a theme that declares fonts is active. Themes without declared faces
issue no font request at all.

The faces themselves are vendored in the preceding commit.

Also fixes two defects found while building it:
- Theme cards overflowed their border on long labels: the grid's 1fr label
  track floors at min-content, so a long name pushed the trailing column past
  the card edge. The track is now minmax(0, 1fr) with an ellipsis, which also
  covers imported tweakcn labels. The selected-state checkmark that made the
  overflow visible is gone; the accent border, tint, and ring already carry
  selection, and aria-pressed owns the semantics.
- index.html's first-paint bootstrap only knew claw/knot/dash, so any new theme
  flashed the default palette before the app corrected it. It now resolves the
  new theme and has a matching pre-paint background, with mount-fallback
  coverage for both modes.

Startup JS grows 198 B for the theme wiring, appearance option, locale
strings, and font loader, which fits the existing ratchet tolerance, so no
budget baseline moves. Startup CSS stays within its 45 KiB ceiling, which is
why the faces are a linked static asset rather than a bundled import.

* style(control-ui): format the vendored font stylesheet

The generated @font-face block kept its upstream single-line unicode-range
declarations, which oxfmt wraps. Repo-wide `pnpm format:check` is clean.

* test(control-ui): type the chat runId in the typography e2e

Reuses the chat-flow requireRecord/requireString helpers instead of asserting
the params shape, so runId is a real string rather than an unknown coerced by
String() (oxlint no-base-to-string).

* fix(control-ui): resolve theme fonts against the configured UI mount

The theme font stylesheet and its faces used root-absolute URLs, so a gateway
served under a configured Control UI base path (gateway.controlUi.basePath,
for example /openclaw) fetched them from the wrong origin path. Both requests
404ed there and Absolutely silently fell back to system fonts while its palette
still applied, which reads as "the theme looks a bit off" rather than as a
broken asset.

The link href now goes through inferControlUiPublicAssetPath, the same helper
plugin art and provider icons already use, and the stylesheet's own url()
references are relative to it, so both levels follow the mount without the
loader knowing the base path. Adds an e2e case under /openclaw asserting the
resolved href and that the browser fetches below the mount; it fails on the
previous code with the root-absolute path.

Reported by ClawSweeper review on #129885.

* test(slack): stop racing the aborted read request in the Web API bound test

`bounds dedicated reads without timing out shared clients` asserted the mock
server received exactly two requests, but the read client aborts at 20ms and
the server records on arrival, so whether the aborted request lands first is a
race. It failed on a loaded CI runner with 1 received.

The bound is still asserted directly (read rejects, shared resolves); the count
now only requires the shared client's arrival, and checks its token so the
dedicated client is still proven not to have carried the call.

Unrelated to this PR's theme work; fixed here rather than left red.
2026-08-26 01:06:41 -07:00
Peter Steinberger 079f8190ed fix(workers): keep remote execution on its selected node (#129929)
* fix(workers): preserve remote execution authority and cleanup

* test(workers): narrow optional gateway request payloads

* test(workers): extract reusable tunnel fixtures
2026-08-26 00:36:49 -07:00
Peter Steinberger 0482cbf1c0 fix(ui): preserve chat URL state during session draft cleanup (#129949)
* fix(ui): preserve chat URL state during session draft cleanup

* refactor(ui): consolidate chat session route identity checks
2026-08-26 00:35:39 -07:00
RoboClaw d3af646e29 fix(ui): align top-left controls (#129920)
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-26 00:19:40 -07:00
Peter Steinberger 860e8e301f fix(ui): reset /model default through server directive (#129895)
* fix(ui): reset model through server directive

* test(ui): target visible mobile transcript message
2026-08-26 00:08:42 -07:00
Peter Steinberger 6d1c1c9f57 refactor(cron): unify heartbeat automations under cron ownership (#129862)
* refactor(cron): unify heartbeat automations under cron ownership

* test(cron): align heartbeat automation fixtures and snapshots

* test(heartbeat): preserve rebased manual-wake coverage

* test(ui): scope mobile session actions to active chat pane
2026-08-26 00:08:18 -07:00
RoboClaw 1ab9852320 improve(ui): collapse mobile composer controls (#129853)
* improve(ui): collapse mobile model controls

* refactor(ui): align mobile composer controls

* test(ui): capture mobile composer states

* fix(ui): preserve mobile settings semantics

* fix(ui): restore mobile picker focus

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* test(ui): await sidebar narration preload

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-26 00:07:50 -07:00
Peter Steinberger 3d81c85f2a fix(ui): model picker no longer collapses to one model after refresh (#129878)
* fix(models): stop models.list from silently collapsing to the harness default

Explicit models.list refresh reads now run to completion instead of racing
the 750ms browse deadline, and implicit reads that do hit the deadline fall
back to the published prepared catalog instead of returning an empty
snapshot that the Codex harness augmentation refilled with only the default
model. The Control UI model picker now revalidates silently in the
background: a warm catalog stays rendered and interactive, and the
'Refreshing models…' state is deleted end to end (chat + New Session).

* test(ui): prove the model picker stays interactive during background refresh

* test(ui): give the shared chat-pane test host a model catalog

The background-revalidation change reads chatModelCatalog.length in
refreshPageChat; createTestChatPane omitted the field, breaking the
chat-pane lifecycle suites on CI.
2026-08-26 00:07:02 -07:00
RoboClaw 636e21b198 fix(ui): prevent horizontal model picker scrolling (#129928)
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-25 23:55:29 -07:00
RoboClaw 1f881019f4 fix(ui): align mobile toast actions (#129909)
* test(ui): cover responsive toast alignment

* fix(ui): align mobile toast actions

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-25 23:40:19 -07:00
Peter Steinberger 1473333f46 feat: bring Linux desktop onboarding to macOS parity (#129815)
* feat: bring Linux desktop onboarding to macOS parity

* fix: keep onboarding recovery within startup budget

* fix: satisfy onboarding startup and CI contracts

* fix: preserve Linux setup recovery and JSON5 configs

* refactor: keep onboarding recovery inside startup budget

* refactor: deduplicate first-run startup routing

* fix(linux): secure remote gateway pins and saved credentials
2026-08-25 23:34:34 -07:00
Hannes Rudolph 53e942ae8d fix(ui): keep hidden session files cold (#129620)
* fix(ui): defer hidden session file listing

* fix(ui): refresh deferred session files on reopen

* fix(ui): recover deferred workspace reloads

* test(ui): align sidebar hydration with lazy files

* fix(ui): keep hidden workspace actions deferred

Co-authored-by: Hannes Rudolph <49103247+hannesrudolph@users.noreply.github.com>
Co-authored-by: Josh Lehman <550978+jalehman@users.noreply.github.com>

---------

Co-authored-by: Josh Lehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-25 23:34:25 -07:00
Vyctor H. Brzezowski 7b3fb42617 improve(control-ui): redesign composer slash and skill invocations (#129707)
* improve(control-ui): align composer invocations with current main

* fix(ui): close composer invocation regressions

* test(ui): follow grouped slash hint rule

* fix(ui): preserve reasoning reset cleanup after rebase

* fix(ui): keep checkbox capability rows switch-free

The unified capability toggle row embedded a wa-switch even in checkbox
mode, reintroducing a hidden-but-real switch control inside the
new-session composer (breaking the rail-owns-switches contract asserted
by new-session-page.places e2e) and an aria-hidden focusable control.
Checkbox rows rely on the dropdown item's own checked state.

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-25 23:18:47 -07:00
Peter Steinberger 48e026161e fix(ui): preserve Settings navigation selection and workspace fragments (#129888)
* fix(ui): preserve Settings navigation selection and workspace fragments

* test(ui): scope mobile session menu proof to active chat pane
2026-08-25 23:06:27 -07:00
Peter Steinberger 4e864dff21 fix(ui): fill collapsed progress-card hover to the composer edge (#129869)
Collapsed, the composer progress card's hover highlight painted only the
<summary> band, leaving slivers of the resting background in the underlap
strip and the arcs beside the composer's rounded corners. Move the
collapsed hover fill to the card container (clipped to the full rounded
shape, extending under the composer) and keep the summary-scoped fill for
the expanded state.
2026-08-25 22:29:02 -07:00
Peter Steinberger ced723c2a8 fix(ui): preserve cloud worker provider-owned settings (#129846) 2026-08-25 22:25:55 -07:00
Peter Steinberger 3d37bb4cba fix(ui): explain empty chat exports (#129881) 2026-08-25 22:22:08 -07:00
Peter Steinberger 43fb810d89 fix(ui): surface agent config errors and hydrated identity avatars (#129868) 2026-08-25 21:48:28 -07:00
RoboClaw 44907105c6 fix(ui): keep mobile updates in Inbox (#129864)
Remove the duplicate update row from the compact session menu while preserving the stale-client refresh action. Keep mobile update confirmation covered through the canonical Inbox flow.

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-25 21:46:37 -07:00
RoboClaw 46fab66580 feat(ui): align session menus (#128995)
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-25 21:45:54 -07:00
Peter Steinberger 781be142fc fix(gateway): isolate session event ownership (#129313) 2026-08-25 21:45:39 -07:00
RoboClaw 37945f1fdf fix(ui): show Draft mode once in new-session composer (#129858)
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-25 21:43:49 -07:00
Vyctor H. Brzezowski 552f160389 fix(ui): preserve new composer state across reconnects (#128620)
Prevent delayed rewind, branch-switch, and fork completions from overwriting replacement chat state after reconnects while preserving canonical history reconciliation.

Refs #128617.
Reviewed-by: @shakkernerd
Co-authored-by: Shakker <165377636+shakkernerd@users.noreply.github.com>
2026-08-26 05:28:22 +01:00
ClawSweeper ce08e259e9 fix(codex): prevent steered answers from disappearing or crossing threads (#128257)
* fix(codex): isolate steered thread transcripts

* fix(ui): scope terminal queue cleanup

* refactor(codex): split steering snapshot assembly

* fix(codex): suppress silent steering boundaries

* chore(ui): document chat event ownership

* test(ui): await Lobsterdex bootstrap requests

* test(update): isolate managed service selectors

* test(ui): submit queue edits by keyboard

* refactor(ui): tighten chat event ownership

* refactor(ui): narrow terminal chat payloads

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-25 21:27:01 -07:00
Peter Steinberger 215de48bd5 perf(ui): streamline session switching and transcript rendering (#129834) 2026-08-25 21:23:29 -07:00