mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
codex/buzz-flat-replies
17 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0b0cff180b | fix(ci): stabilize Control UI startup gzip ratchet (#130688) | ||
|
|
9ccbbf83f2 |
perf(control-ui): load built-in theme palettes on demand (#130473)
* perf(control-ui): load built-in theme palettes on demand
Every built-in theme's tokens shipped in the startup stylesheet, so the default
path downloaded six palettes it never painted and each new theme taxed everyone.
That is what pushed the startup CSS ceiling from 45 to 47 KiB when Tide, Beacon,
and Phosphor landed.
Moves the twelve non-default palettes into public/themes/<family>.css, one file
per family covering both modes. Claw stays inline because its tokens are the
:root defaults, so the default path loses nothing and gains the bytes back.
The first-paint story this needed: index.html's boot script now links the active
family's palette during head parsing, which makes it render-blocking exactly
like the app stylesheet, so a persisted theme paints its own colours on the
first frame instead of flashing the default. The href is built from the mount
prefix the gateway already stamps on <html>, so it follows a configured Control
UI base path without the script having to know one. theme.ts keeps the link
correct when the theme changes at runtime, reusing the helper the webfont
stylesheets already use.
The nested resolve-theme ternary became a family table in the same script, since
it now picks an asset as well as a data-theme value.
startup CSS 45.8 -> 42.2 KiB gzip, below the 44.3 KiB it measured before the
three themes landed; ceiling restored 47 -> 45 KiB
base.css 64.0 -> 35.8 KiB raw
Adds a regression test that blocks every bundle script and asserts the palette
still applies, so moving this back into the app bundle fails instead of silently
reintroducing the flash. Verified it catches that: with the boot-script link
removed the assertion reports `expected null to be '/themes/tide.css'`.
* fix(control-ui): publish themes after their palettes load
* fix(control-ui): clean up palette completion listeners
* refactor(control-ui): consolidate theme name resolution
|
||
|
|
08a42a561c |
feat(control-ui): add the Tide, Beacon, and Phosphor themes (#130232)
* chore(control-ui): vendor JetBrains Mono and Atkinson Hyperlegible Next Adds the woff2 faces for the Beacon and Phosphor themes plus the stylesheets that declare them. Both families are SIL Open Font License 1.1 and ship unmodified with their upstream license text, as the license requires. Atkinson Hyperlegible Next was drawn by the Braille Institute for low vision: its letterforms disambiguate the pairs that blur first at low acuity (I/l/1, O/0, rn/m), which is why it belongs to the accessibility theme specifically. base.css has always named JetBrains Mono first in --mono, but nothing shipped it, so code blocks rendered in it only for people who happened to have it installed locally. Phosphor makes that deterministic. Latin and latin-ext subsets only, 195 KB. Served from the gateway rather than a font CDN so font-src 'self' holds, no third-party request happens on load, and an offline or LAN-only gateway still renders correctly. The url() references are relative to their stylesheet so they follow a configured Control UI mount. * feat(control-ui): add the Tide, Beacon, and Phosphor themes Three built-in themes, each filling a gap the existing four share rather than adding a fifth variation on them. Every shipped accent today is warm — coral, crimson, chocolate, clay — and the only cool token anywhere is Claw's teal secondary. Tide is the cool one: steel cyan on deep slate, deep teal-blue on cool paper. It deliberately ships no webfonts, so it costs nothing beyond its tokens. Beacon is the accessibility one. It targets WCAG AAA (7:1) rather than the AA 4.5:1 floor the other themes hold, for low vision, direct sunlight, projectors, and poor panels, and sets everything in Atkinson Hyperlegible Next. Reaching AAA forced one deliberate break from the status-token convention: elsewhere each --x-subtle is --x at 8%, but a label on a tint of its own hue caps achievable contrast because the tint pulls the background toward the text. Beacon tints neutrally and lets the label carry the contrast. Its focus ring is opaque and 3px for the same reason — a translucent ring is the first thing to vanish on a bad panel. The contrast guardrail now enforces 7:1 for Beacon specifically, so a later palette edit cannot quietly demote it to an ordinary dark theme. Phosphor is the terminal one: phosphor green on green-cast black with the whole surface, chat prose included, in JetBrains Mono. Monospace prose is a character choice for an operator console, not a readability claim, which is why it is opt-in. Every palette was solved against the real guardrail before any CSS was written. Worst text pairing per theme: Tide 6.12:1 dark / 4.63:1 light, Beacon 10.29:1 / 9.72:1, Phosphor 6.69:1 / 4.76:1. Worst status label on its own tint: Beacon 7.05:1 / 7.02:1, the others above 4.6:1. Budgets: the startup CSS ceiling moves 45 -> 47 KiB and the JS ratchet baseline moves 523 B. Every built-in theme's tokens ship in the startup sheet, so each new theme costs ~0.5 KiB gzip whether or not anyone selects it. Moving per-theme palettes to lazily linked stylesheets (as theme webfonts already are) would take this back under the old ceiling, but it needs a first-paint story first: a late palette flashes default colors where a late font only swaps. * fix(control-ui): use Beacon's destructive foreground in chat confirmations The chat confirmation button paints from --danger under --media-foreground, a theme-invariant white, and each dark palette opts out of that pairing through a selector list in chat/grouped.css. Beacon defines a deliberately light --danger (#ffabab, chosen so status labels clear AAA) but was missing from that list, so its confirm button rendered white on pale pink at 1.80:1 — on the theme whose entire promise is contrast. Adds Beacon to the list, which routes it to --destructive over --destructive-foreground at 11.67:1. Also adds a guardrail for the bug class, since the selector list has to be extended by hand for every new dark theme and I have now missed it once. The test reads membership back out of grouped.css rather than restating it, resolves each theme's effective (background, color) pair, and holds it to that theme's floor. It reproduces this defect at 1.80:1 against the pre-fix stylesheet. Reported by ClawSweeper review on #130232. |
||
|
|
0f55efc443 |
fix(ci): restore Control UI startup CSS budget (#130046)
Amp-Thread-ID: https://ampcode.com/threads/T-01a03d42-8db7-733e-bbff-f3d2a08071d0 Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
ff73ba5f56 |
fix(ci): restore Control UI startup budget gate (#130015)
Amp-Thread-ID: https://ampcode.com/threads/T-01a037b5-3918-749f-90bd-5c9ac1dced16 Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
64853b1a9d |
improve(control-ui): restructure the composer as a multiline surface (#124301)
* improve(control-ui): align composer stack with current main
* refactor(ui): isolate new-session permission state
* fix(ui): close composer CI regressions
* test(ui): align composer presentation contracts
* test(ui): fix composer browser contracts
* test(ui): stabilize composer browser ownership
* test(ui): target presented mobile composer
* test(ui): select accessible mobile composer
* test(ui): open mobile rail session directly
* fix(ui): keep mobile composer content laid out
* fix(ui): address composer review findings
* fix(ui): close composer interaction gaps
* fix(ui): bound swarm task popover
* fix(ui): restore progress-card docking beside the wide composer
The multiline restructure dropped the .session-progress-card--dock
placement shipped in #129141 and left the completed marker inside the
collapsed-only summary span, so it vanished when the disclosure opened.
Restore the dock positioning block and hoist the marker into the
always-visible summary grid.
* fix(ui): bound swarm task popover by its composer anchor
The popover capped its height against the raw viewport, so on short
mobile viewports its top rendered above the screen. Register the swarm
group with the existing composer popover anchor observer and consume the
measured --chat-composer-popover-max-height, keeping the viewport calc
only as fallback. The new actual-tree mobile regression failed with
-71px top clipping before the fix.
* fix(ui): restore Draft capability controls and split the new-session composer
Restores the Draft option and selected-state indicators the capability
menu lost in the restructure, and moves the capability/visibility
controls into composer-capability-controls.ts so composer.ts drops back
under the 700-line lint gate (677 lines) without a suppression.
* test(ui): prove permission-mode final effect before session creation
Adds mocked-gateway proof that an admin's Full selection reaches
sessions.create as permissionMode:"full" exactly once, and that a
write-scoped operator's retained Full selection is rejected before
creation with a visible status notice and zero create requests. Also
aligns the mobile footer reading-order assertion with the shipped
attachments-draft-model order.
* test(ui): register swarm popover layout test as node-driven
chat-swarm-progress.browser.test.ts drives its own Playwright chromium
from node, but
|
||
|
|
8fe2c1b83c |
perf(ui): consolidate Control UI boot chunk graph for HTTP/1.1 gateways (#128514)
* perf(ui): consolidate Control UI boot chunk graph for HTTP/1.1 gateways The Control UI boot flow (app shell + sidebar + chat route) lazily loaded ~124 automatic chunks in one burst after the gateway handshake, which the gateway's HTTP/1.1 transport serializes into ~24 six-connection round-trips on high-latency links (Tailscale, remote gateways). Add a measured boot-module manifest (ui/config/control-ui-boot-modules.json, regenerated via pnpm ui:boot-manifest:gen) and a control-ui-boot codeSplitting group that merges exactly that module set into a handful of chunks with recursive dependency inclusion. Lazy islands (locales, ghostty-web, novnc, non-default routes) keep their own chunks; stale manifest entries degrade gracefully back to automatic chunking. Measured on the built dist with the mocked gateway (chat route, 3 runs): unique boot JS requests 140 -> 45, raw boot JS 3751 -> 3717 KiB, chat composer interactive at simulated 50 ms RTT ~1600 ms -> ~575 ms. Largest-CSS budget rises 45 -> 47 KiB for the merged boot CSS; startup JS gzip baseline ratchets down (345049 -> 339214 B) as consolidation shrinks the startup graph. * chore(ui): refresh boot module manifest after rebase onto current main * fix(ui): stop the pending lazy shell action replay loop starving boot When a pending lazy shell action (command palette open, panel toggle) replayed while the shell was still splash-gated, the dispatched event had no rendered element to consume it and re-entered requestLazyElement in a microtask cycle: request -> load -> replay -> dispatch -> request. The cycle starved tasks (Gateway WebSocket messages included), so the boot never finished and the recovery e2e froze on the splash screen. Gate replay on the element actually being rendered: the controller skips the action after load until the host's render root contains the tag, and restorePendingLazyAction skips dispatch while a defined element is still render-gated. The host retries after every completed update, so the replay fires on the update that first renders the element. Regression test fails on the pre-fix controller. * fix(ui): re-anchor the scope-upgrade details popover before opening wa-popover resolves its `for` target once per property change and never re-resolves a missing or replaced anchor. The trigger with the shared id can render after the popover's first update (the header trigger ships with the lazy chat chunk), leaving the opened popover permanently invisible: active popup with a native [popover] part stuck at UA display:none because showPopover() never ran without an anchor. Re-arm the watcher when opening while the anchor is missing or disconnected. * test(ui): compare settled layouts in device-scope stability assertions The 0.5px no-move assertions sampled geometry that later reflowed when the details surface's first render fetched glyph subsets, reporting sub-pixel drift the open never caused. Burn in the one-time open per context and sample the baseline adjacent to the click. * fix(ui): map the keyboard shortcuts dialog in lazy replay gating Current main added the keyboard-shortcuts lazy shell event; the replay gate's exhaustive event-to-element record needs its entry. * chore(ui): refresh startup budget baseline after rebase onto current main |
||
|
|
ee7146a282 |
fix(ui): make startup gzip runtime-independent (#126795)
Host zlib versions compress identical startup assets into different bytes, causing false Linux budget failures. Emit canonical shipped pako gzip sidecars, restore the 512 B ratchet tolerance, and lower the startup baseline from 348351 B to 344531 B. |
||
|
|
8668aeb969 |
fix(discord): bind transcript capture to source account [AI] (#118579)
* fix(discord): bind transcript capture to source account * style(agents): keep transcript tool wiring compact * fix(transcripts): declare account binding channels * fix(transcripts): report effective capture account * fix(transcripts): enforce account lifecycle ownership * fix(transcripts): preserve cross-surface control * fix(copilot): preserve transcript channel context * fix(transcripts): fail closed for legacy channel owners * fix(transcripts): add trusted legacy recovery * fix(transcripts): preserve auto-start cleanup ownership * fix(transcripts): reject untrusted account starts * fix(transcripts): keep persisted ownership authoritative * fix(transcripts): harden legacy recovery * fix(transcripts): preserve agent ownership boundary * fix(transcripts): scope account binding to source channel * fix(transcripts): preserve unattributed owner isolation * fix(transcripts): own configured captures by account * docs(plugins): clarify transcript auto-start ownership * test(transcripts): cover account-less recovery * docs(transcripts): scope legacy recovery by provider * fix(discord): reuse eligible account ordering for transcripts * test(discord): use neutral transcript account fixtures * fix(transcripts): keep accountless recovery local * fix(discord): resolve transcript accounts by voice capability * fix(transcripts): bound account resolution failures * fix(transcripts): bound account tool output * fix(transcripts): honor unresolved provider accounts * fix(transcripts): preserve binding when providers are missing * fix(transcripts): fail closed on unknown binding provenance * fix(transcripts): qualify account lifecycle capability * fix(transcripts): normalize provable legacy owners * fix(transcripts): bind scheduled capture to caller authority * fix(transcripts): preserve scheduled caller identity tuple * fix(transcripts): preserve channel-less scheduled authority * fix(plugin-sdk): publish transcript provider types * fix(transcripts): use exact lifecycle ownership tokens * fix(transcripts): preserve local ownerless lifecycle access * fix(transcripts): allow local configured capture control * fix(transcripts): preserve scheduled caller channel * fix(transcripts): retain named-agent legacy recovery * fix(transcripts): deny unrelated remote channels * fix(doctor): validate transcript owner inference * fix(transcripts): restrict legacy remote recovery * fix(ci): align transcript Doctor checks * fix(transcripts): require Doctor-owned legacy metadata * fix(transcripts): reject unowned remote capture starts * fix(transcripts): reject unbound Discord lifecycle calls * fix(transcripts): distinguish legacy owner rows * test(discord): keep unavailable account fixture typed * fix(transcripts): mark current imports for Doctor * fix(transcripts): complete account ownership validation * fix(discord): restore transcript package boundary * fix(discord): preserve bundled transcript entry boundary * docs(transcripts): clarify Discord auto-start account * fix(transcripts): bind account-owned imports * fix: preserve transcript and cron policy state * fix(cron): preserve scheduled transcript authority * fix(discord): keep legacy transcript rows local * fix(transcripts): narrow account ownership boundary * fix(transcripts): preserve trusted caller ownership * fix(discord): enforce transcript source authorization * fix(ci): bound Control UI gzip build variance * test(qa): align transcript scenario contracts * fix(agents): repair rebased caller context * fix(discord): restore rebased account ownership * test(discord): restore voice account fixtures --------- Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com> |
||
|
|
e7ba2e1f6b |
fix(ui): give the Gateway update a visible, truthful outcome (#121686)
* fix(ui): give the Gateway update a visible, truthful outcome Confirming an update in the Control UI could end in nothing the operator could see. The sidebar card only knew `updateRunning`, which clears as soon as `update.run` answers, so a dev-channel handoff — git pull, install, full build, restart — spent minutes back on the stale "N commits behind" call to action. A verified install then published a null banner, so success was never announced at all, and the reload that a verified install triggers would have destroyed any in-memory notice anyway. The lifecycle was already recorded; nothing rendered it. Feed the sidebar the reconciliation fact the settings page already uses, narrate the wait in the shell callout that survives the restart, and record the verified result so it outlives the reload it causes. Failures now name their cause. The updater already stores the failing step and its captured output in the restart sentinel and `update.status` returns it whole, so a disk-full install reads "The update failed at install: ENOSPC: no space left on device" instead of a reason slug plus "see the gateway logs". Missing handoff-reason guidance is filled in, and the duplicate post-restart banner resolver is folded into the shared one. Also fixes a callout clipped by the fixed shell chrome controls, and the "Available 246 commits behind" double framing in the confirmation. * fix(ui): report the Gateway update in the dialog that starts it Follow-up to the first pass: the shell-top callout was the wrong surface for this. The dialog the operator clicked is now the one that reports the whole update, and the ambient surfaces are the fallback for anyone who leaves it. Confirming no longer closes the dialog. It becomes stateful — the danger button turns into a disabled "Updating…" with a spinner, and the body narrates the phases in place: installing, then "The Gateway is restarting. This page disconnects and reconnects on its own." The dialog is mounted on document.body, outside the shell, so losing the connection cannot unmount the only surface still reporting. A request the Gateway never accepts stops spinning after a bounded grace and says so. A failure lands in the dialog with its recorded cause and next step. Success still arrives after the reload a verified install triggers, so it stays a toast — no dialog exists by then. The shell-top status callout is gone. The same outcome now renders beside the sidebar update card that started the update, and stays hidden while the dialog owns the report so the two surfaces never duplicate each other. Splits the wire-payload readers out of update-overlay-helpers into update-schedule-dto and moves update.run answer classification next to them, keeping both owners under the file-size budget. * fix(ui): keep the update dialog open through the install in E2E and use a defined warn token * refactor(ui): wire the update progress stream into the floating card and drop unused video capture * fix(ui): report the current update, not the previous failure The dialog subscribed to progress before starting the update. Producers replay their current snapshot synchronously on subscribe, so a retry still carrying the previous attempt's failure banner painted that old error as this update's outcome. Start the request first, and treat a failure present on the subscribe emit as the previous attempt's: an accepted run clears the banner before its first await, so a failure that survives means the request was refused, which the accept timer already reports truthfully. * fix(ui): keep update helpers internal * chore(perf): raise startup-JS budget to 350 KiB Approved by maintainer Vyctor on 2026-08-11. Trigger: after #121734, main measured 326650 B—6 B below the prior 319 KiB hard ceiling. Current main subsequently moved the ceiling to 320 KiB; this records the authorized final 350 KiB decision for #121686. * fix(ui): announce update success without session storage Show the verified install identity directly when the current document does not reload, so denied session storage cannot suppress the operator-visible outcome. * test(ui): isolate update toast in overlay tests The Node-only overlay suite now reaches the direct non-reload success toast. Mock the DOM-owned toast boundary so reconciliation assertions can complete without a browser document. |
||
|
|
8d3f3377da | fix(ci): make Control UI artifact validation consistent (#122086) | ||
|
|
db73b59c04 |
refactor: burn wrapper shadowing baseline entries (#122040)
* refactor: burn wrapper shadowing entries * chore: refresh wrapper shadowing baselines * test: update secrets runtime state mocks * fix(ci): absorb Control UI build identity variance |
||
|
|
8dace97c2e |
perf(test): cut workflow guard overhead (#122038)
Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
fa03d9b913 |
refactor: consolidate coercion helpers (#121366)
* refactor: consolidate coercion helpers * fix: remove duplicate coercion imports * fix: preserve serialized coercion guard * chore: ratchet coercion helper carve-outs * fix(test): keep gauntlet subprocess startup lean * fix: preserve imported session timestamp semantics * fix: preserve catalog timestamp string semantics * chore: align plugin SDK surface ratchet * fix: preserve trajectory and SDK string contracts * fix(test): preserve QA record assertion semantics * fix: complete standalone record guard rename * refactor(cron): use canonical string coercion * fix(acpx): preserve Pi timestamp parsing * test(channels): adapt custody test harnesses * test(telegram): classify media harness as test support * test(acpx): split timestamp contract coverage * test(channels): support generated custody contracts * chore: ban the full coercion helper name set Extends the declaration guard to all eleven consolidated helper names and renames the cron schedule-identity readNumber wrapper to readScheduleInteger so the banned generic name cannot regrow. * fix(scripts): repair release-validation guard drift and lint cause Restores the renamed isJsonRecord guard in assertTrustedWorkflowHarness after main added isRecord call sites in parallel, and attaches the caught YAML error as the thrown error cause (preserve-caught-error was red on main). * fix: preserve Claude timestamp string semantics * fix: preserve persisted timestamp string semantics * fix: preserve date-first timestamp contracts * fix(openai): harden delegation failure formatting * chore: close coercion helper guard gaps * test(openai): model non-error delegation rejection * chore: refresh plugin SDK API contract * fix(tasks): use canonical string field reader * fix(ai): use canonical provider error field coercion * fix(browser): migrate native bootstrap coercion * docs(plugin-sdk): clarify text record export compatibility * fix(gateway): normalize approval execution identity * test(outbound): isolate message action poll harness |
||
|
|
a6a464f4c0 |
feat(terminal): fullscreen /terminal route and dot-matrix claw intro (#121451)
* feat(ui): add fullscreen /terminal route and open button * feat(terminal): dot-matrix claw intro banner and CLI mascot * fix(terminal): satisfy lint and wizard-header assertions for the new mascot * ci: account for fullscreen terminal startup bytes * test(terminal): split session manager coverage |
||
|
|
5207c4765d |
feat(ui): float task-suggestion cards top-right with copy-prompt action (#121259)
* feat(ui): float task-suggestion cards top-right with copy-prompt action - Float a compact tray in .chat-main__conversation with absolute positioning, min(400px, 100% - margins) sizing, elevation, and internal scrolling. - Remove the icon column, keep dismiss in the header row, and move a compact split button to the bottom-right. - Add Copy prompt to the menu via shared copyToClipboard, copying the raw prompt while display-only sanitization remains documented inline. Release note: Suggested-task cards now float as compact cards at the chat top-right and offer Copy prompt in the action menu. * style(ui): neutral grey task-suggestion card and actions Drop the accent-tinted card border/background, mute the eyebrow, and use default grey button treatment for the split control per operator direction. * fix(ui): ungate copy-prompt and surface clipboard outcomes - Menu now always renders with the client-local Copy prompt while acceptance-mode items stay capability+scope gated (addresses ClawSweeper finding 1). - Copy success shows a transient Copied label and failures surface through the pane error path (finding 2). - Task-suggestion pane logic extracted to chat-pane-task-suggestions.ts and the tray adapter moved into the component to keep files under the 700-line cap. * fix(ui): test task suggestions through tray adapter * test(ui): cover task-suggestion clipboard failure * test(ui): split task-suggestion pane coverage * chore(ui): raise startup JS budget for task-suggestion card feature QA-profile measurement was 324617-324620 B versus the 324608 B effective budget; this owner-directed PR #121259 bump follows the baseline file’s documented feature-growth precedent. * chore(ui): raise startup JS ceiling to 318 KiB for task-suggestion cards The fixed 317 KiB ceiling (324608 B) was within ~20 B of CI-measured startup bytes after accumulated main drift; the task-suggestion card feature crossed it. Ceiling raised with in-file maintainer-approval comment per its own convention; ratchet baseline set to CI-measured 324626 B. * fix(ui): keep task-suggestion copy available read-only Remove the stale accept-or-dismiss tray guard now that Copy prompt is a client-local action. Keep Gateway-backed accept and dismiss controls independently gated, with a regression for operators who can only list suggestions. * test(ui): cover list-only task-suggestion copy Update the mocked-Gateway E2E to match the repaired read-only behavior: the suggestion remains visible, its mutating Start action is disabled, and Copy prompt stays available. |
||
|
|
c70aee247e |
refactor(scripts): migrate JavaScript tools to TypeScript (#121005)
* refactor(scripts): migrate JavaScript tools to TypeScript * fix(ci): keep changed-scope preflight zero-install * fix(ci): preserve zero-install script owners * fix(ci): complete script migration follow-through * fix(release): keep stable closeout zero-install * fix(scripts): preserve standalone execution boundaries * fix(scripts): repair standalone loader boundaries * fix(scripts): normalize gateway observation ids * fix(scripts): keep Docker packager standalone * test(scripts): preserve rebase cleanup helpers * test(sessions): use tracked temp directory |