Commit Graph

4937 Commits

Author SHA1 Message Date
Jason (Json) c1002c3ec4 fix(ui): dismiss landed steer notices (#123908)
* fix(ui): retire landed steer notices

* fix(ui): preserve steer transcript order
2026-08-14 21:19:30 -06:00
Jesse Merhi 47442197a1 feat(ui): review install policy warnings (#120900)
* feat(ui): review install policy warnings

* fix(ui): keep install feedback on runtime plugin
2026-08-15 13:07:02 +10:00
Jason (Json) cae6030575 fix(ui): clear proven uncertain sends (#123927) 2026-08-14 20:49:45 -06:00
Peter Steinberger 6abfedcca5 fix: stale Control UI tabs stop retrying after updates (#123932)
* fix(ui): preserve stale client refresh compatibility

* fix(ui): cover build-aware frozen clients
2026-08-14 19:29:59 -07:00
Peter Steinberger 7160c4de0b refactor(ui): remove disconnected build mismatch projection (#123916) 2026-08-14 18:52:22 -07:00
Peter Steinberger 0982ee5729 perf(ui): reduce startup connect error overhead (#123910) 2026-08-14 18:07:20 -07:00
Peter Steinberger 7f891f4a78 fix(ui): stale tabs keep dispatching after gateway updates (#123882)
* fix(ui): reject stale bundled clients at connect

* chore: keep release notes out of runtime fix

* fix(ui): preserve stale-build admission invariants
2026-08-14 17:05:10 -07:00
Peter Steinberger 0a6b95a3df feat: cloud workers for the codex runtime (remote-exec placements) (#123743)
* feat(gateway): add remote-exec cloud placements

* feat(codex): run cloud turns through remote exec

* fix(sandbox): quote ssh_config path directives containing whitespace

Crabbox lease keys default to ~/Library/Application Support/... on macOS;
unquoted IdentityFile/UserKnownHostsFile/CertificateFile arguments tokenize
on the space and fail as 'extra arguments'. Found via live remote-exec
cloud-worker proof.

* test: consolidate gateway maintenance schedule coverage

* fix(ci): invalidate plugin sdk declarations on state changes
2026-08-14 16:46:56 -07:00
Peter Steinberger 1cf932295c refactor(ui): trim pairing uncertainty state (#123856)
* refactor(ui): trim pairing uncertainty state

* test(ui): stabilize navigation and worker readiness

* test(ui): drop popover flip geometry replay
2026-08-14 16:16:57 -07:00
Patrick Erichsen f726393812 fix(ui): offer permanent lobster dismissal (#123789)
* fix(ui): add persistent lobster dismissal

* fix(ui): keep the lobster dismiss menu from scrolling its own items

The dismiss menu anchors a synthetic trigger at the raw pointer position,
and the pet always sits on the sidebar footer ledge at the bottom of the
viewport. Web Awesome caps its popup to `--auto-size-available-height`,
and its `size` middleware runs after `flip`, so the menu was shrunk in
place to 60px against 64px of content instead of moving up. The two
dismissal items then scrolled inside a 6px overflow, which shows as a
scrollbar for anyone running the system setting that always renders them.

Clamp the anchor to the viewport the way every other pointer-anchored
menu already does (session-menu.ts:264, catalog-session-menu.ts:55,
native-link-menu.ts, sidebar-menus-controller.ts:270), so the popup keeps
the room it needs and renders both items in full.

* docs(web): describe the lobster dismiss menu options

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-08-14 20:00:50 -03:00
Jason (Json) 89a424bcb9 fix(ui): page activity indicator matches session rows (#123813)
* fix(ui): align page working indicator with sessions

* test(ui): align lifecycle checks with home spinner
2026-08-14 16:58:44 -06:00
Peter Steinberger 3ef7f590a7 fix(ci): invalidate plugin SDK declarations on auth changes (#123861)
* fix(ci): invalidate plugin sdk declarations on auth changes

* test(ui): await device migration overlay updates
2026-08-14 15:21:29 -07:00
Jason (Json) 386b12ba9c fix(ui): sort created sessions by creation time (#123828) 2026-08-14 15:44:38 -06:00
ClawSweeper 4fd825bc0a fix(ui): contain mobile lightbox images (#123840)
Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
2026-08-14 14:32:36 -07:00
ClawSweeper c12745429a fix(ui): fork active sessions from stable history (#123718)
* fix(ui): fork active sessions from stable history

* fix(protocol): align active fork types

* test(ui): match main-session fork routing

* fix(sessions): unify stable fork admission

* fix(native): fork active sessions from stable history

* fix(android): fork active sessions from stable history

* style(android): simplify active fork result handling

* fix(native): preserve legacy fork transport API

* test(native): complete legacy fork transport stub

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-14 14:13:25 -07:00
Peter Steinberger 3dff1727b1 fix(control-ui): give chat links a contrast-safe color token (#123824)
chat links used --accent while user bubbles are filled from --accent-subtle (and peer bubbles from a per-sender hue), so links sat on their own hue at 3.95:1 worst case. New --link/--link-hover tokens derive from each palette's audited --accent-hover and clear WCAG AA on every bubble surface and sender hue; the hover opacity fade, which lowered contrast, is replaced by a color shift.
2026-08-14 14:03:28 -07:00
Vyctor H. Brzezowski dc8facb060 improve(ui): explain the Utility model setting (#123277)
* improve(ui): explain utility model setting

* fix(ui): anchor utility model help above trigger
2026-08-14 17:43:52 -03:00
Vyctor H. Brzezowski b90001934a fix(ui): keep the chat header blend off the transcript scrollbar (#123308)
The header-to-transcript blend spanned the full pane width, so it painted over the strip where the transcript scrollbar lives and the thumb vanished under the fade while scrolling. Stop the blend one transcript gutter short of both pane edges and make that gutter a token shared with the transcript column, which keeps every pixel of content faded while the scrollbar stays visible (symmetric so RTL keeps the same clearance).
2026-08-14 17:30:05 -03:00
Peter Steinberger 58ab589da5 fix(ui): stale tabs restore terminals without owners (#123660)
* fix(ui): fence stale same-version clients by build

* test(ui): satisfy build identity lint guards

* fix(ui): complete build identity fencing

* chore: leave release notes to release prep

* test(ui): align build identity fixtures

* fix(ui): fence reconnects on pending worker

* test(ui): stabilize cross-platform update proof

* test(ui): satisfy service worker lint

* test(ui): preserve offline socket cohorts
2026-08-14 13:19:02 -07:00
Peter Steinberger 986be4558f fix(ui): prevent duplicate Skills preload requests (#123705)
* fix(ui): wait for Skills route preload

* fix(ui): defer Skills fallback until first update

* fix(ui): keep Skills unmounted during preload

* fix(ui): retain route data through gateway metadata updates

* test(ui): make Skills preload race deterministic

* fix(ci): round boundary artifact refresh mtimes

* fix(ci): repair merged main checks

* chore: keep Skills repair diff focused
2026-08-14 12:41:24 -07:00
Peter Steinberger 1805e8eec3 fix(ui): surface auto-disabled automations instead of hiding them as paused (#123769)
An auto-disabled job (10 consecutive run failures / 3 schedule errors)
rendered as plain 'Paused' and simultaneously vanished from the failed
chip, the failing count, and the error dot — the escalation removed
visibility exactly when the problem became permanent. The recorded fact
(state.autoDisabled, already public and used by CLI/doctor) now renders as
a distinct danger note with the reason and count, and auto-disabled jobs
stay in the active-failure predicate.
2026-08-14 12:39:03 -07:00
Peter Steinberger 877f2ec083 fix(ui): report connection loss instead of silently dropping session actions (#123771)
* fix(ui): report connection loss instead of silently dropping session actions

patchSession returned 'stale' with no message when the gateway dropped
before a queued dialog submit (rename and similar) — the dialog closed and
the edit vanished with no visible outcome. loadCheckpoint had the sibling
gap: expanding a row while disconnected left the drawer claiming 'No
checkpoints' beside a nonzero checkpoint badge. Both now surface the
existing actionRequiresConnection message at their owning error surface.

* test: widen patchSession test-support patch type for label patches
2026-08-14 11:33:47 -07:00
Peter Steinberger 0bdf11c9dc fix(ui): surface companion clear failures (#123724)
* fix(ui): surface companion clear failures

* fix(ui): scope companion reset errors

* fix(ui): preserve companion clear async contract
2026-08-14 11:30:26 -07:00
Peter Steinberger f22e4990c1 fix(ui): surface Talk input backpressure (#123783) 2026-08-14 11:27:13 -07:00
Peter Steinberger da7a2b4651 fix(ui): show session attention on Home (#123759)
* fix(ui): show main-session attention on Home

* refactor(ui): keep Home attention boundary private
2026-08-14 11:27:04 -07:00
Peter Steinberger c4b92d9a7a fix(ui): stop turn recap freezing the previous run's token count (#123712)
The session row's terminal stamp (status/endedAt/runtimeMs) and its usage
persist (outputTokens) are separate gateway writes. resolveTurnRecap settled
on the first fresh terminal row and froze whatever outputTokens it carried —
frequently the PREVIOUS turn's count. Treat an unchanged-from-baseline value
as that lag and fall back to the watched run's live usage-stream counter
(captured while watching, since the usage map entry dies at lifecycle end).
2026-08-14 11:18:18 -07:00
Vyctor H. Brzezowski 3f006ba0fc fix(ui): complete mobile pairing after code redemption (#120933)
* fix(ui): complete pairing setup lifecycle

Redeemed and expired mobile setup codes stayed on screen as usable QR
codes, so a successful pairing had no visible outcome and expired bearer
material still looked live.

The Gateway now mints an opaque, non-authorizing setupId beside each
setup credential, returns its authoritative expiresAtMs, records the
terminal outcome of the exact redemption, and broadcasts
device.pair.setup.completed. Because that broadcast is dropped for
buffered operator sockets, the completion is persisted first and can be
reconciled through the new device.pair.setupStatus method: the Control UI
asks for the recorded outcome before it may present a credential as
expired, so a pairing that succeeds is never shown as a failure.

The Control UI models one closed lifecycle (selection, loading, waiting,
success, expired, error) correlated only by the active setupId, and
Pairing help now carries the external-link affordance.

* fix(ui): preserve unknown pairing outcomes

* test(ui): target pairing terminal headings

* test(ui): align pairing completion fixture

* fix(gateway-protocol): decode setup-code results from older gateways

Older protocol-v4 gateways omit the new setupId and expiresAtMs
lifecycle fields, so requiring them in the generated native model broke
decoding an existing device.pair.setupCode response. Keep both optional
at the wire boundary, require lifecycle metadata before the Control UI
enters its waiting state so a missing outcome stays visible, and cover
the legacy payload with Swift and schema regressions.

* fix(ui): surface rejected pairing dialog loads

The lazy pairing chunk could reject while its overlay was already open,
leaving the shell rendering nothing at all. Record the rejection on the
shell and render a recoverable modal with a reason and a retry so the
open action always ends in a visible outcome.

* fix(state): preserve pairing setup schema compatibility

* test(gateway): cover pairing setup release train

* fix(gateway): commit pairing setup completion atomically

* refactor(state): distinguish setup transaction helpers

* refactor(state): remove obsolete bootstrap restore path

* fix(gateway): preserve setup handoff type safety

* fix(gateway): keep pairing completion terminal after consume

* fix(gateway): validate Watch binding during setup commit

* fix(gateway): revalidate setup credential expiry at commit

* chore: refresh Plugin SDK API contracts after rebase

* fix(pairing): prune expired setup completions

* chore: retrigger CI

* fix(protocol): deduplicate setup expiry field

* fix(protocol): refresh pairing setup clients

* fix(gateway): make pairing setup completion durable

* fix(ui): retire expired pairing credentials immediately

* fix(ui): keep pairing dialog visible while loading

* fix(macos): align setup result initializer order

* fix(gateway): restore generic bootstrap retries

* chore(ui): record pairing startup budget

* chore(ui): refresh pairing startup budget

* style(gateway): format maintenance imports

* test(gateway): cover session-sharing mock

* fix(state): defer setup correlation schema

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-08-14 11:10:30 -07:00
Peter Steinberger 88babc4643 test(ui): stabilize update confirmation focus proof (#123757)
* test(ui): wait for update dialog focus

* test(ui): poll update confirmation focus
2026-08-14 11:09:03 -07:00
Peter Steinberger 3292ff146c fix(ui): keep the archive Undo working after leaving the Sessions page (#123749)
The archive toast outlives the Sessions page (toast host is app-shell
level), but its Undo action was gated on the page's request scope, which
dies the moment the operator navigates away — clicking Undo within the 6s
window then silently did nothing. Run the un-archive through the shared
sessions mutations store directly (it already fails closed on connection
replacement and publishes errors), mirroring the always-mounted sidebar
undo sibling.
2026-08-14 11:09:02 -07:00
Peter Steinberger f9d9b9da2b fix(ui): stop flagging running automations as overdue and show their state (#123745)
* fix(ui): stop flagging running automations as overdue and show their state

The gateway intentionally leaves nextRunAtMs past-due while a run executes
(it advances only on the outcome), and agentTurn runs may take up to an
hour — 12x the sidebar's 5-minute overdue grace. The overdue chip and the
cron table therefore reported healthy in-flight jobs as 'overdue' / 'next
run: X minutes ago'. runningAtMs is the recorded fact for an in-flight run
(already public and used by the CLI's status), but no UI surface read it.
Add the shared isCronJobRunning helper, exclude running jobs from the
overdue filter, and render 'Running' in the next-run cell.

* test(cron): move running-state row test beside the capped view suite
2026-08-14 11:07:08 -07:00
Josh Lehman 84c2d64206 fix(ui): preserve new session drafts across navigation (#123697) 2026-08-14 11:01:28 -07:00
ClawSweeper eab9cf5ea4 fix(ui): preserve pinned widget identity (#123721)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-14 10:44:55 -07:00
ClawSweeper e03d1a42f8 improve(ui): collapse limited access notice (#123735)
* improve(ui): collapse limited access notice

* test(ui): follow limited access banner host

* test(ui): await limited access guidance host

* test(compaction): split context budget coverage

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
2026-08-14 10:25:28 -07:00
ClawSweeper 0803505259 fix(ui): retire persisted steer chips immediately (#123744)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-14 10:22:40 -07:00
Josh Lehman ab363234f7 fix(ui): open image attachments in a lightbox (#123693)
* fix(ui): open attachment images in lightbox

* fix(ui): verify blob image types before navigation
2026-08-14 10:08:59 -07:00
ClawSweeper a875ead7ef fix(ui): prevent dashboard chat messages from overlapping (#123713)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-14 10:00:01 -07:00
Peter Steinberger fc5265d685 improve: tighten newest regression ownership (#123606)
* test: tighten newest regression ownership

* test(ui): stabilize request-driven e2e waits

* fix(ci): stabilize lifecycle-bound test observations

* test(ci): pin current Telegram job cap

* test(ui): wait for terminal selection owner

* test(mac): use shared unread wait policy
2026-08-14 08:08:45 -07:00
Josh Lehman c23c188902 fix: explicit agent ownership no longer breaks system surfaces (#123339)
* fix: honor explicit agent ownership in system surfaces

* oc-000: fix explicit-owner runtime preparation
2026-08-14 06:52:21 -07:00
Peter Steinberger 5523f8a334 fix(ui): surface the autosave reconnect latch instead of silently never saving (#123663)
When a gateway connection is replaced while a config form draft is
dirty, autosave intentionally latches off (autoSaveRequiresExplicitSubmit)
until an explicit Save — but the latch was invisible: the indicator
rendered nothing on idle, form mode has no Save button, and every
subsequent edit (plus the dispose-time teardown flush) silently never
saved. A recorded fact existed in the coordinator with no read where
the operator needed it.

The latch now projects a visible "paused" autosave status owned by the
same arm/clear transitions (conflict outranks it; local edits cannot
clear it), and the save indicator renders "Autosave paused after
reconnect" with a Save action reusing the existing retry plumbing.
2026-08-14 06:36:00 -07:00
Peter Steinberger 946e49cb80 fix(ui): renaming a config map key that holds a stored secret dead-ends the draft or binds the wrong credential (#123571)
* fix(ui): block renaming config map keys that still hold redacted secrets

Renaming a map entry whose value is the server redaction sentinel moves
the sentinel to a new key. The gateway's exact-key restore then fails
closed forever (dead-end draft with a Retry that can never succeed), and
when the rename is folded with a delete of the target key inside one
autosave window, the restore silently binds the deleted entry's old
credential under the new name and drops the intended one — a green
Saved with the wrong secret.

The rename handler is the only owner that knows a sentinel was moved
rather than typed, so it now rejects the rename like the existing
duplicate-key guard and explains the retype path via input validity.
Plain values rename exactly as before.

* chore(ui): record startup JS growth for the redacted-rename guard string

The renameRedactedBlocked validity message in the startup en locale adds
65 gzip bytes over the recorded baseline; record the CI-measured size
per the established baseline pattern.
2026-08-14 06:34:24 -07:00
Peter Steinberger 45d5b5ef51 fix(ui): empty-string i18n params rendered the raw {placeholder} (#123474)
t() interpolation used || so a provided empty-string param fell through to
the visible {placeholder} fallback meant for missing params — e.g. the
devices page rendered 'Bound to {node}' whenever an agent binding was the
empty string. ?? keeps the missing-param debugging aid and renders provided
empties as empty.
2026-08-14 06:26:21 -07:00
Peter Steinberger c3d16060d4 fix(ui): oversized attachment kills the whole gateway connection instead of being rejected client-side (#123654)
* fix(ui): validate attachment size against hello policy before encoding

The gateway advertises decoded-size attachment ceilings in hello-ok
policy.attachments precisely so clients can validate before sending,
but the Control UI never read them. A file over ~18.7MB decoded
expands past the gateway's 25MiB WS frame cap: ws hard-terminates the
connection (1009) for every pane and session in the tab, the message
parks as waiting-reconnect with wrong copy, and manual retry kills the
connection again.

appendAttachmentFiles — the single funnel for file input, paste, drop,
and plus-menu — now rejects oversized files up front with a named
toast, mirroring the terminal-upload sibling guard. Limits thread from
hello policy at both composer assembly sites (chat pane, new-session);
absent policy means no client-side cap, matching older gateways.

* refactor: keep chat-pane-render under the line cap

The attachmentLimits prop tipped chat-pane-render.ts to 701 counted
lines (cap 700, main sits exactly at cap). Compact the checkpoint
deep-link callback and read limits from the pane's own hello snapshot.
2026-08-14 06:24:19 -07:00
Peter Steinberger 5972b36b14 test(ui): make model auth scoping proof route-owned (#123661) 2026-08-14 06:24:04 -07:00
Peter Steinberger 1f61c9e295 test(ui): stabilize activity and workboard e2e waits (#123664) 2026-08-14 06:23:18 -07:00
Peter Steinberger e342e02b6f test(ui): capture mock gateway timeout diagnostics (#123650) 2026-08-14 05:50:05 -07:00
Peter Steinberger f39f48f7b9 fix(ui): hiding a catalog makes its adopted sessions vanish from the entire sidebar (#123481)
* fix(ui): hiding a catalog no longer makes its adopted sessions vanish everywhere

The adopted-key exclusion set was computed from the unfiltered catalog list
while the renderer shows only visible catalogs (hidden ids filtered; none
under the Archived filter). Hiding a catalog therefore removed its adopted
sessions — possibly running, unread, or selected — from the entire sidebar
with no row anywhere. One visibleSessionCatalogs() projection now feeds both
the renderer and the exclusion; the subclass's duplicate filter is deleted.

* refactor: fold sidebar catalog projection into its owner module

The visibleSessionCatalogs projection and adopted-PR-row merge pushed
app-sidebar-session-navigation.ts over the 700-line lint cap. Move the
projection predicate to app-sidebar-session-catalogs.ts (the catalog
helper owner) and the PR-row merge to the navigation-logic module,
inlining the single-caller passthrough into the indicators controller.
2026-08-14 05:48:47 -07:00
Peter Steinberger 44dd983c0c feat(mac): add canonical elevation host workflow (#123569)
* feat(mac): add canonical elevation host workflow

* fix(mac): clarify elevation host deployment

* test(ui): stabilize cloud startup effort flow

* fix(mac): fail closed on unknown TCC status

* test: synchronize async CI boundaries

* test: remove remaining CI timing races
2026-08-14 05:39:24 -07:00
Peter Steinberger db6e238aff fix(ui): gateway client rotation silently discards plain staged attachments (#123628)
* fix(ui): gateway client rotation silently discarded plain staged attachments

replacePaneStagedAttachmentGatewayOwner discarded every staged
attachment when the gateway client object rotated — which happens on
event-gap recovery (laptop sleep/wake reconnect) and after plugin
install/enable, not just on real credential changes. Draft text
survived; attachments vanished with no error, so a user who staged
screenshots before sleeping delivered a text-only message.

Only browser-annotation attachments actually depend on the old client
(their Undo context dies with it). Plain file/image payloads are
client-local data URLs; rotation now drops annotation-backed
attachments and keeps the rest, in both live state and memory
fallbacks.

* test: align annotation-lifecycle rotation tests with selective discard

Three tests pinned the old wholesale-discard contract; rotation now
drops only annotation-backed attachments while plain payloads survive
(live state, fallbacks, and post-undo-toast state).
2026-08-14 05:33:25 -07:00
Peter Steinberger c9152fef4e test(ui-e2e): scale Control UI wait budget for loaded CI runners (#123623)
ui-e2e intermittently failed with `page.waitForFunction: Timeout 10000ms
exceeded` on loaded CI runners (model-agent-scoping, terminal-embedded),
both green on rerun. The 10s deadlines were repo-imposed at the mock
Gateway helper and readiness helper, plus twelve per-test
setDefaultTimeout sprinkles.

Give the harness one owner for the wait budget: 30s under CI, 10s
locally, mirroring the ui-e2e vitest config's existing expect.poll
reasoning. Suite-created contexts inherit it, so two per-test overrides
are deleted outright.
2026-08-14 05:20:45 -07:00
Peter Steinberger 08d41e55a5 refactor(ui): one token formatter — the same count no longer renders two ways (#123472)
The UI carried three token formatters with proven divergence: 15,600
rendered as 16k on the Sessions page (formatTokens) but 15.6k in the chat
composer and /usage (formatCompactTokenCount); 999,600 as 1.0M vs 999.6k.
The message-meta cost line also bypassed formatCost with a hardcoded
toFixed(4), showing $1.2346 next to a popover showing $1.23.

formatCompactTokenCount is now the one owner: it absorbs formatTokens'
nullish fallback and model-providers' local billion-suffix wrapper (both
deleted), and the message cost line goes through formatCost. Production
net −19.
2026-08-14 05:09:05 -07:00