Commit Graph

4936 Commits

Author SHA1 Message Date
Peter Steinberger 563cea4e66 fix(ui): shared picker's 138px floor overflows narrow grid cells (#123175)
The phone-width ellipsis fix (#123085) promoted a chat-composer-scoped
138px minimum onto the shared select-picker inline style, which beats
every consumer stylesheet. Containers that legitimately shrink below
138px overflow instead: the cron 'Every N <unit>' and stagger-window
unit pickers spill out of their minmax(0,1fr) grid cells at 320px, and
workboard's min-width:0 became dead. Cap the floor at the host width
with min(138px,100%); the readable-label floor is preserved everywhere
the container is wide enough (the mobile e2e asserts it at 364px). Also
fix the stale '8ch' comment from the pre-138px iteration.
2026-08-13 20:07:14 -07:00
Peter Steinberger c66d5d84db fix(ui): clearing a cron delivery destination in the Control UI silently keeps the old chat (#123171)
Blanking the 'To' field in the Automations delivery editor omitted the
key from the cron.update patch. The Gateway merges delivery patches
by key presence, so the stored destination survived and every future
run kept announcing to the old chat with no error. Send an explicit
null clear on update, exactly like the accountId field one line above
(same bug class fixed there in #105762). Webhook mode is unaffected:
its blank-URL validation rejects the form before submit.
2026-08-13 20:03:06 -07:00
Peter Steinberger 31d54a61fb fix(ui): one failed full-message load pins the truncated preview for the whole session (#123324)
* fix(ui): retry failed full-assistant-message loads instead of pinning the truncated preview

Since ce466fccb0 removed the manual Show-more toggle, a failed
loadFullAssistantMessage left {status:error} that no renderer reads and the
render-time kick-off never retried (it only fired when no expansion state
existed). One transient RPC failure pinned the '...(truncated)...' preview
for the rest of the session with no visible outcome. Retry on later render
passes while attempts remain, bounded by revision so a dead loader cannot
hot-loop.

* fix(ui): render a retry affordance once automatic full-message loads exhaust

Addresses ClawSweeper P2 on the bounded-retry fix: after
FULL_MESSAGE_RETRY_REVISION_LIMIT the truncated preview previously stayed
silent. The exhausted error state now renders an inline error line with a
Retry button that re-enters the canonical loader via the same toggle path;
the retry bound is a named shared constant.
2026-08-13 19:51:15 -07:00
Jason (Json) 95240c0d57 fix(ui): pin notifications to viewport top (#123423) 2026-08-13 19:43:22 -07:00
Peter Steinberger eecf488c32 fix(ui): hidden retained chat panes silently clear unread for sessions you are not viewing (#123247)
Since retained panes (#121625) every pane — hidden or presented — keeps
its sessions subscription alive, and applySessionsState marked the
pane's selected session read unconditionally. New activity in a
recently-viewed session was cleared by its hidden pane instantly: the
sidebar unread dot never stuck, agentStatus attention was wiped by the
read patch, and "Mark as unread" self-reverted. Gate the read-marking
on this.presented, matching applyActiveSessionBindings.
2026-08-13 19:37:51 -07:00
Vyctor H. Brzezowski b0583e2643 fix(ui): contain long sharing member labels (#123017) 2026-08-13 23:19:26 -03:00
Peter Steinberger 6d716d9318 fix(ui): keep composer pickers visible near viewport edges (#123386)
* fix(ui): keep composer pickers visible near viewport edges

Model and effort pickers now share viewport-aware, preferred-above placement through the anchored overlay. This replaces the fixed CSS positioning policy so either picker can flip and shift within the viewport.

* test(ui): move picker regression to model flow suite

Keep the viewport-placement regression in the existing model and reasoning E2E owner so the composer redesign suite remains below the max-lines gate without losing coverage.

* test(ui): wait for adaptive pickers before interaction

Wait for the Web Awesome popup content to become visible before measuring menus or sending keyboard input. This aligns existing E2E interactions with the asynchronous details toggle lifecycle.
2026-08-13 18:53:34 -07:00
Peter Steinberger 90e039c30f fix(ui): do not call a portal unreachable when policy refused the probe (#123344)
* fix(ui): do not call a portal unreachable when policy refused the probe

The Portals page probes a portal with fetch before mounting its preview,
but a Content Security Policy refusal fails the same way a dead port
does, so a reachable portal was reported as unreachable. That fires
whenever the request Host differs from the hostname the browser used —
the Control UI reached through a portal, or a proxy that rewrites Host.

Frames obey frame-src, not connect-src, so a refused probe says nothing
about the portal: report it as blocked and mount the preview anyway.

Also stop an unrelated chat geometry assertion from flaking the merge
gate: boundingBox returns subpixel floats (16.000001907348633 on CI),
so compare with tolerance instead of exact equality.

Found by running a dev gateway on a cloud box and reaching its Control
UI through a portal; the browser console showed the connect-src refusal
for a portal that curl could reach.

* test(auth): isolate OAuth callback unit coverage

* test(ui): assert attachment failures through toast host
2026-08-13 18:00:20 -07:00
Peter Steinberger 79f41079d2 fix(ui): re-clicking the active nav item no longer stacks duplicate history entries (#123205)
Clicking the sidebar item for the page you are already on pushed an
identical history entry every time, so browser Back appeared dead until
every duplicate was popped. navigateAndWait now downgrades push to
replace when the computed target equals the current history location,
reusing the existing sameRouteLocation helper; context.replace collapses
into the same navigateWithMode owner instead of a parallel copy.
2026-08-13 17:57:22 -07:00
Peter Steinberger bbe97388d3 fix(ui): create automations for selected agent in all-agents view (#123381)
* fix(ui): preserve selected automation owner

Keep the concrete selected agent on model lookup and new automation creation while the Automations page is filtered to All agents, avoiding AgentSelectionRequiredError in multi-agent setups.

* fix(ci): remove duplicate Codex test shard assignment

Keep run-attempt-state.test.ts in the light Codex app-server shard only so the full-suite inventory remains one-to-one.
2026-08-13 17:50:15 -07:00
Peter Steinberger f0076b0ca6 feat(ui): thicken chat effort slider into capsule rail (#123380)
Restyle the effort popover slider from a 3px line to a full-height capsule rail with a pill thumb.
Retune the fill and dots for the tall track.
Verify the result in both dark and light themes.
2026-08-13 17:27:47 -07:00
Peter Steinberger 09940f718d fix(ui): stop app-card badges from squeezing titles into mid-word wraps (#123375)
On narrow cards the nowrap bundled-app badge left the title as the only
shrinkable item in the title row, so "Apple Watch" / "Wear OS" broke
across two lines. Let the row wrap: the badge drops to its own line
(left-aligned under the title) while wide cards keep it inline right.
2026-08-13 17:09:25 -07:00
Colin Johnson 9f263b2b19 fix(ui): keep remote input out of chat composer (#123293)
Co-authored-by: Solver <solver@localhost>
2026-08-13 19:58:48 -04:00
Peter Steinberger 1348387076 refactor(plugins): replace node-llama-cpp with managed llama-server (#123105)
Move llama.cpp chat and local embeddings onto a verified externally managed llama-server runtime. Remove the in-process native runtime, forked embedding workers, and node-llama-cpp dependency while preserving guided setup, local GGUF models, tool-capable agent runs, diagnostics, and operator docs.
2026-08-13 16:58:20 -07:00
joshavant 639e7718f3 fix(models): preserve refresh generation and fallback 2026-08-13 18:57:50 -05:00
joshavant bb65c12e0c test(ui): preserve deferred discovery proof 2026-08-13 18:57:50 -05:00
joshavant 21a3bfb4e4 fix(ui): keep model retry callback void 2026-08-13 18:57:50 -05:00
joshavant 6659007352 fix(models): preserve explicit discovery outcomes 2026-08-13 18:57:50 -05:00
joshavant a16b41488a test(ui): expect startup metadata to avoid model lookup 2026-08-13 18:57:50 -05:00
joshavant e34fd3016b fix(models): keep read-only refresh on prepared owner 2026-08-13 18:57:50 -05:00
joshavant 2cf9c99f8d fix(models): retry superseded catalog owners 2026-08-13 18:57:50 -05:00
joshavant d83f7b815d fix(models): defer live catalog discovery 2026-08-13 18:57:50 -05:00
Peter Steinberger 1287befae7 fix(release): preserve upgrade diagnostic probe (#123338)
* fix(release): preserve upgrade diagnostic probe

* test(vitest): reinstantiate contract projects for audits

* style(test): format upgrade diagnostic assertions

* test(ui): isolate route transition outlet fixture
2026-08-13 16:56:43 -07:00
Vyctor H. Brzezowski 9ee74069b0 feat(ui): add fullscreen mode to Desktop preview (#123278)
* feat(ui): add Desktop preview fullscreen

* test(ui): use real VNC for Desktop proof

* test(ui): satisfy Desktop proof checks

* test(ui): wait for VNC framebuffer pixels

* test(ui): format framebuffer readiness check
2026-08-13 20:50:13 -03:00
Peter Steinberger 6000ef2bbe fix(ui): localize JSON-collapse summaries, JSON badge, and Tool sender label (#123325)
jsonSummaryLabel returned hardcoded English ('Object (7 keys)', 'Array (2
items)', 'JSON'), the chat-json-badge span carried a raw 'JSON' literal, and
the tool sender label was a raw 'Tool' string — all rendered on localized
chat surfaces while sibling strings go through t(). #115730's localization
sweep missed these. Raw-copy baseline shrinks by the two badge entries.
2026-08-13 16:36:49 -07:00
Peter Steinberger 81a80a63e7 fix(ui): dropped attachments that fail to read vanish with no visible outcome (#123274)
* fix(ui): report attachment read failures instead of dropping files silently

Unreadable drops and pastes (folder drops, permission-denied files) resolved
null in readAttachmentFile and were filtered out with no visible outcome —
the attachment simply never appeared. Name the skipped files in a toast while
still attaching the successful siblings; aborted batches stay silent.

* test: assert the read-failure toast message is a plain string (oxlint no-base-to-string)
2026-08-13 16:33:30 -07:00
Vyctor H. Brzezowski f73b436f05 refactor(ui): make chat rails full-height resizable columns (#122475)
* refactor(ui): make chat rails resizable columns

* fix(ui): preserve board runtime across rail modes

* fix(ui): unify chat rail chrome

* fix(ui): unify rail header controls

* refactor(ui): normalize rail header chrome

* fix(ui): render background tasks rail

* test(ui): preserve workspace collapse semantics

* fix(ui): keep companion separator single

* test(ui): distinguish companion sheet and column borders

* test(ui): compare shared rail focus chrome

* test(ui): drive rail focus with keyboard

* fix(ui): align single-panel rail headers

* fix(ui): add custodian rail toggle label

* test(ui): narrow mock chat request params

* test(ui): follow shared rail controls

* test(ui): keep rich rail state visible

* fix(ui): center terminal rail actions

* test(ui): keep chat notices in conversation column

* test(ui): detect focus inside rail shadow roots

* test(ui): respect desktop overlay interactions

* test(ui): restore rails after route navigation

* test(ui): rebuild route-scoped rail matrix

* test(ui): preserve custodian rail during matrix

* chore(ui): keep chat pane within lint budget

* test(ui): tolerate subpixel icon geometry
2026-08-13 19:49:11 -03:00
Peter Steinberger cf885476a1 test: stabilize hosted UI and gateway races (#123357)
Await intercepted banner-module routes and isolate optional-module failure from stale-chunk reloads. Settle and atomically sample responsive geometry. Complete probe handshakes before teardown, and assert portal server ownership instead of reusable ephemeral ports.
2026-08-13 15:44:24 -07:00
Peter Steinberger 67a1cdda00 docs(ui): record stylesheet policy (tokens, breakpoint ladder, dead-CSS audit, no-layer decision) (#123185) 2026-08-13 15:00:03 -07:00
Peter Steinberger f8bca11d77 chore(ui): add dead-CSS audit script and delete unreferenced stylesheet rules (#123178)
* chore(ui): add dead-CSS audit script and delete unreferenced stylesheet rules

* fix(scripts): type-safe AST walking in the dead-CSS audit

* fix(scripts): detect ternary-headed class stems and restore live wizard rules
2026-08-13 14:53:17 -07:00
Peter Steinberger b7b33149b0 fix(ui): responsive layout test no longer flakes under load (#123300)
* test(ui): stabilize responsive layout geometry

* test(ui): fix lint errors in layout-settle helper

Rename the evaluate-scope selector binding and stop returning the rAF id
from the Promise executor (no-shadow, no-promise-executor-return).
2026-08-13 14:31:48 -07:00
Peter Steinberger 3cdbe3a511 fix(ollama): verify guided setup with runtime model (#123190) 2026-08-13 14:27:34 -07:00
Peter Steinberger dbea5a77f0 fix(ui): make Control UI terminal readable in light mode (#123184)
The light terminal theme reused the dark-background ANSI palette (1.4-2.6:1
contrast on #f7f8fa; brightWhite was 1.06), and the intro banner hardcoded
256-color indices 223/216 that bypass the client theme entirely.

Light mode now gets its own darkened ANSI-16 palette (>=4.5:1, bright
variants darker for emphasis, brightWhite maps to strongest ink), and the
banner emits themable ANSI-16 yellow/bright-red so each mode owns its
rendering. Dark mode is visually unchanged.
2026-08-13 14:14:35 -07:00
Josh Lehman f04fdd5594 fix(ui): align transcript search corner radius (#123299) 2026-08-13 14:12:30 -07:00
Josh Lehman 0b847dc92f fix(ui): keep question prompts from fading transcript (#123301) 2026-08-13 14:11:48 -07:00
Peter Steinberger be7f3225f7 fix: auth-cold gateways hide all models and dead-end the operator (#123130)
* fix(control-ui): recover auth-cold model pickers

Keep configured auth-cold models visible but disabled in chat and new-session, block submission, and link Model Setup. Fixes #123080.

* fix(control-ui): preserve rebased model picker behavior

* fix(ui): split composer model picker under the lint size cap

Repairs main's red check-lint caused by the #123090 restore.

* fix(ui): align auth-cold picker CI contracts
2026-08-13 14:09:35 -07:00
Peter Steinberger b3d5265f58 fix(docker): harden runtime images against CVE surface (#123282)
* fix(docker): harden runtime image dependencies

* chore(deps): update container security dependencies

* docs(docker): explain image security contents

* test(browser): align file-chooser and install tests with #114506 contract

* test(browser): restore extension install test isolation

* test(browser): add temporary CI diagnostics for pre-registration refusal

* test(browser): make install fixture interpreter hermetic

The suite passed process.execPath as the native-host interpreter; on
GitHub-hosted runners the hostedtoolcache node binary is group/world-
writable, which installChromeExtensionBootstrap correctly refuses, so
every registration test failed CI-only. The fixture now provides an
owned 0700 interpreter; only the launcher-exec test keeps the real
node it must spawn.

* fix(qa-lab): stop re-polling after a probe consumes the discovery deadline

The Matrix health-probe loop re-entered when the probe timeout fired
marginally before Date.now() crossed the deadline, starting a doomed
extra probe. Flaked on contended CI runners as 'expected 1 fetch, got
2'. A timed-out probe now ends discovery.

* test(ui): poll the callout inset invariant in device-scope E2E

One-shot boundingBox reads raced the nav-collapse transition and
intermittently measured a 20px stale offset on CI.
2026-08-13 14:02:39 -07:00
Peter Steinberger 2dcd47d4f4 feat(runners): remember offline devices (#123198)
* feat(runners): remember offline devices

Persist exact node connection end timestamps and keep execution-capable offline devices visible with quiet lifecycle history in the session picker.

* test(ui): keep offline devices disabled
2026-08-13 13:49:00 -07:00
Peter Steinberger 12ada43464 refactor(ui): resolve duplicate stylesheet selectors and restore no-duplicate-selectors (#123166)
* refactor(ui): resolve duplicate stylesheet selectors and restore no-duplicate-selectors

* refactor(ui): merge duplicate .kbd block in file-preview modal instead of config carve-out
2026-08-13 12:50:18 -07:00
Peter Steinberger 21d59e16e2 feat(gateway): monitor cloud session disk space (#123177)
* feat(gateway): monitor cloud session disk space

* fix(gateway): keep disk monitor contract acyclic
2026-08-13 12:47:53 -07:00
Peter Steinberger 9415181b75 fix(ui): make active visibility pill distinct from its hover state (#123173)
* fix(ui): make active visibility pill distinct from its hover state

* test(ui): advertise sessions.create in the control-ui mock harness

* ci: refresh pull_request run after zombie-queued Blacksmith jobs
2026-08-13 12:42:40 -07:00
Peter Steinberger f2f3ba2eaf refactor(ui): consolidate responsive breakpoints onto a canonical ladder (#123160)
Use the 400, 560, 640, 768, 900, 1100, and 1320px max-width ladder. Round noncanonical thresholds up to the next rung so compact layouts engage before desktop layouts become cramped.

Move the 1180px agent-tool summary breakpoint to 1320px because the persistent desktop rail leaves about a 1020px content column at that viewport; 1100px would coincide with the rail disappearing and the content expanding.

Stylelint now enforces the ladder and the existing compound-query exceptions.
2026-08-13 12:34:50 -07:00
Peter Steinberger d805562a84 fix(ui): verify referenced translation keys (#123279) 2026-08-13 11:32:56 -07:00
Peter Steinberger 249a38d29f fix: keep background work scoped to its selected agent (#123257)
* fix: preserve explicit agent ownership

Scoped route, session, and job owners now survive hooks, cron, process exits, global delivery, bindings, and media cleanup. Truly ownerless operations remain explicit errors.

* test: provide session memory hook owner

* test(ui): close failed-module page explicitly

* test(matrix): align ownership and replacement proof
2026-08-13 11:29:31 -07:00
Peter Steinberger 843017073c refactor(ui): route stylesheet colors through the token layer and gate hex literals (#123156)
Add semantic, presentation, brand, and shared highlight tokens in base.css.
Remove dead fallbacks and consolidate dark/light syntax colors behind tokens.
Enforce color-no-hex for CSS while exempting token definitions and sprite artwork.
2026-08-13 05:36:52 -07:00
Peter Steinberger 0926d56cbf fix(ui): stop stamping a dead blur class on chat links containing 'tail' (#123154)
The DOMPurify afterSanitizeAttributes hook added class
chat-link-tail-blur to every rendered chat link whose href merely
contains the substring "tail" (docs.openclaw.ai/tailscale, /details/,
retail, ...). No stylesheet or script anywhere consumes the class: it is
an orphaned producer that survived the Control UI import, polluting
sanitized HTML and the markdown LRU cache, and priming a foot-gun where
any future .chat-link-tail-blur CSS rule would visually blur docs links
in every transcript.

Link presentation classes are owned by markdown-parser.ts
(web-link-classes, hostname-based); delete the substring classifier.
2026-08-13 05:22:02 -07:00
Peter Steinberger 6c9916a48a feat(runners): publish atomic device runner inventory (#123094)
* feat(runners): publish atomic node inventory

# Conflicts:
#	docs/.generated/plugin-sdk-api-baseline/agent-harness-runtime.json
#	src/gateway/server-methods/environments.ts
#	src/gateway/worker-environments/device-provider.test.ts

* fix(runners): refresh topology on inventory removal

* fix(runners): resolve session host type overlap

* fix(protocol): document mobile runner inventory event

* fix(runners): project authoritative session host state

* docs(nodes): clarify prepared session host status
2026-08-13 04:54:26 -07:00
Peter Steinberger c3d843eb24 test: remove residual duplicate assertions (#123134)
* test: remove residual duplicate assertions

* test: repair stale routing and capability fixtures

* fix(cli): bound one-shot stream drain exit
2026-08-13 04:41:37 -07:00
Peter Steinberger 449ca204a6 fix(ui): keep limited-access banner clear of shell controls (#123128)
* fix(ui): keep limited-access banner clear of shell controls

* fix(ui): avoid native notice gutters
2026-08-13 04:40:26 -07:00
Peter Steinberger 2a8b322ebf feat: view your machine's screen from the iOS and Android apps (#123097)
* feat(ui): add mobile desktop document mode

Add a shell-free mobile desktop route that reuses the dock panel controller and lazy noVNC client, with source preselection, touch controls, keyboard input, and retryable inventory failures.

* feat(ios): add desktop viewer entry points

* feat(android): add desktop viewer

* fix(android): keep System Back inside the desktop viewer

The per-session viewer replaces SessionDashboardScreen in place instead of
pushing a shell tab, so System Back fell through to the shell-level handler
and popped the whole Dashboard tab, stranding the operator on Chat. Claim
Back while the viewer is showing.

Also carry over TerminalSettingsScreen's imePadding: the viewer's own touch
toolbar hosts the keyboard affordance, so the soft keyboard would cover it.

Proof (emulator, Medium_Phone_API_36.0, stub control UI on 18789):
pre-fix Back from the viewer lands on Chat; post-fix it returns to Dashboard.
No Robolectric regression test — no existing screen test constructs
MainViewModel, and BackHandler under Robolectric would need new scaffolding
for weaker evidence than the live repro.

* test(ui): stop the pairing views leaking dialogs into the shared document

`ui/vitest.config.ts` runs the unit project with `isolate: false`, so test files
share one jsdom document inside a worker. `view.pairing.test.ts` appends a
container to `document.body` for every case and never tears down, unlike its
sibling `channels-page.test.ts`, so whichever suite the worker scheduled next
inherited a mounted pairing dialog.

That surfaced on this PR's first CI run as ten failures in the untouched
`input-dialog.test.ts`, which found "Approve DM access" where it expected
"Rename session". A rerun went green, so the ordering is scheduler-dependent
rather than deterministic; this removes the contamination source rather than
leaving the next suite to lose the race.

Not a proven fix for that specific run — the leak reproduces only under CI's
file scheduling, and the full suite passes locally either way — but the missing
teardown is a real violation of the shared-environment contract.

* test(ui): stop the background-tasks rail asserting on a ticking clock

The rail e2e captured the main transcript's text before opening a task detail
and required it to be byte-identical afterwards. A running task renders a live
elapsed label, so the assertion failed whenever a second ticked over between
the two reads — twice while landing this PR, both times "12s" against "13s"
with no other difference.

Normalize elapsed labels on both sides instead of weakening the assertion. The
invariant it protects, that opening a detail leaves the main transcript alone,
still holds: a real content change is still caught, and only complete duration
tokens collapse, so diffstat figures like +14/-3 and phrases like "5 messages"
are untouched.
2026-08-13 04:10:48 -07:00