Commit Graph

4831 Commits

Author SHA1 Message Date
Josh Lehman 32cc1c5397 fix(ui): keep GitHub hovercards interactive 2026-08-12 07:24:37 -07:00
Peter Steinberger df707a9670 feat: view this machine in the Desktop panel (#122545)
* feat(gateway): add gateway-host desktop source behind desktop.host lab

Introduce the host as a first-class desktop source so operators can view
the machine OpenClaw runs on, not just cloud-worker environments:

- protocol: desktop.observe / desktop.launch with a discriminated
  DesktopSource union (host | environment) plus an additive auth hint;
  EnvironmentSummary gains a top-level desktop flag
- config: desktop.host { enabled, port?, passwordFile? }, Labs-gated
- rfb-probe: pure RFB version/security-type parser used to detect an
  already-running loopback VNC server and classify its auth
- host-source: attaches to 127.0.0.1:<port>, refuses unauthenticated
  (None) and unsupported (VeNCrypt) servers, and refuses ARD with the
  supported alternative until the macOS milestone
- host-guidance: per-OS enablement text so no path dead-ends
- doctor + status report host desktop availability and auth type only

worker.desktop.observe/launch stay as delegating aliases with identical
behavior. Also drops the now-unused WorkerDesktopTunnels type export.

Live-verified against macOS Screen Sharing: probe reads RFB 003.889,
returns security types [30,33,36,35], classifies ard-account.

* test(gateway): probe RFB handshakes through the socket boundary

The probe's banner and security-offer parsers were exported solely so
unit tests could call them, which the dead-export gate rejects and which
tests internals rather than behavior. Keep them module-local and drive
the probe through a scripted loopback server instead.

The boundary tests also cover what pure-function vectors could not:
handshakes split across packets, legacy RFB 3.3 single-word security,
server-rejected handshakes, early hangups, and connect timeouts.

* feat(ui): let the Desktop panel view this machine, not just cloud workers

The Desktop panel was gated on a cloud-worker session placement, so an
operator running OpenClaw locally had no way to see the machine hosting
their main session even with a VNC server running on it.

Availability now follows the advertised desktop.observe method plus
operator.admin instead of session placement, and the picker lists every
environment whose summary reports a desktop, with the gateway row shown
as "This machine". Sources are passed to the generic desktop.observe /
desktop.launch RPCs; the app launcher stays worker-only. When a host
attach needs a password the gateway did not supply, the panel prompts and
keeps the value in memory for that connection only.

Adds the hostDesktop Labs toggle for desktop.host.enabled.

* fix(scripts): keep the env-var ratchet usable in shallow checkouts

The env-var budget check resolved its base ref, then hard-failed when
`git merge-base` found no shared ancestor. Shallow clones and grafted
agent checkouts resolve origin/main but truncate the history behind it,
so an advisory growth ratchet took down the whole check:changed gate
with "Could not resolve env-var count merge base for: origin/main".

Only the growth comparison needs a baseline, and the script already has
a no-baseline path. Treat git's exit 1 with empty output (no shared
ancestor) as that case and say so on stderr; a genuine failure still
exits 128 and still throws, and the absolute count-vs-budget check runs
either way.

* test(ui): measure the inline-code chip against its line box

The inline-code spacing test compared the chip's height to a prose text
rect, so it silently measured the monospace font's default line spacing.
That is ~17px on macOS and several px shorter on Linux, so the assertion
passed on CI and failed locally at 4.5 against a 3.75 bound -- after the
bound had already been widened once to chase browser font metrics.

Compare the chip to the paragraph's CSS line box instead, which is what
"the chip must not disrupt the line" actually means and is platform
independent. The horizontal gap stays as-is: it is em-derived padding
plus border, and it is the assertion that catches detached punctuation.

Verified both directions on macOS: the file is fully green, and
restoring the pre-fix 0.15em/0.35em padding still fails the gap
assertion at 5.41.

* feat(gateway): view macOS Screen Sharing from the Desktop panel

Modern macOS only offers ARD account authentication for Screen Sharing,
so the host desktop source refused every Mac. The Gateway now performs
the ARD handshake itself against the loopback server and hands the
browser a plain RFB 003.008 no-auth handshake, so the operator's macOS
account password authenticates the desktop without ever reaching the
browser, the observe result, a URL, or a log.

- rfb-preauth: ARD (type 30) Diffie-Hellman with MD5-derived AES-128-ECB
  credentials, and VncAuth (type 2) bit-reversed DES, both under a single
  10s negotiation deadline; Apple's RFB 003.889 maps to 3.8
- observe-bridge: runs pre-auth before splicing and starts the view-only
  filter at clientInit, since the browser handshake is consumed here;
  worker tokens keep the original version start phase
- host-source: attaches ARD, requiring per-observation credentials that
  live only in the one-shot observer token and are dropped after use
- doctor: offers an explicitly confirmed sudo launchctl repair when
  Screen Sharing is off, and prints the System Settings path otherwise

Live-verified against this Mac's Screen Sharing: the DH exchange and
credential framing are accepted and the server returns SecurityResult.
The VncAuth DES vector is confirmed against OpenSSL independently.
2026-08-12 06:58:30 -07:00
Peter Steinberger 40cb6f0e3b feat(ui): keep Where picker destinations live (#122531)
* feat(gateway): enrich environment summaries

* chore(sdk): refresh plugin API surface

surface drift approved: additive EnvironmentSummary enrichment (runners plan rev 2, milestone 4)

* feat(ui): keep where picker destinations live

* docs: link picker liveness milestone PR

* fix(ui): deduplicate picker locale keys

* test(ui): update picker section expectations

* fix(ssrf): narrow lookup resolver contract

* fix(gateway): publish authoritative environment facts
2026-08-12 05:11:46 -07:00
Ziyang Guo 220b2dcec7 fix(skills): reconcile manually created proposal targets (#118676)
* fix(skills): reconcile manually created proposal targets (#90388)

* fix(skills): preserve stale apply lifecycle errors

* fix(skills): stale existing create targets

* fix(skills): dispatch reconciliation hooks outside target lease

* test(skills): prove manual target reconciliation

* fix(skills): serialize proposal reconciliation with collection writes

* fix(skills): surface proposal reconciliation failures

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-12 20:06:56 +08:00
Peter Steinberger cb58073a90 fix(ui): paginate all active tasks (#122565)
Preserve task-list cursors and drain every active page so supported running work remains visible and cancellable.
2026-08-12 02:46:55 -07:00
Peter Steinberger d44f70eb4b feat(pairing): one-paste device pairing via oc-pair setup links (#120768)
* feat(pairing): one-paste device pairing via oc-pair setup links

Implements milestone 3 from docs/plan/runners.md.

* fix(pairing): sign bootstrap handshake, keep URL candidates, wire pairing countdown

* test(gateway): update client callsite guard

* fix(pairing): preserve setup URL context paths

* fix(ui): keep pairing help aligned with setup mode

* fix(pairing): isolate bootstrap credentials

* perf(ui): keep one-paste pairing within bundle budget

* refactor(pairing): isolate native pair URL prefix parsing

* fix(pairing): preserve candidate lifecycle state

* fix(pairing): retire shared credentials after bootstrap

* fix(pairing): apply rotated manifest through client owner

* test(pairing): prove bootstrap retirement across reconnect

* fix(pairing): preserve native gateway context paths

* fix(pairing): carry native context paths through reconnect

* fix(ios): preserve encoded gateway context path

* chore(plugin-sdk): refresh pairing API baselines
2026-08-12 02:01:33 -07:00
Peter Steinberger 85a176b98a feat(ui): commit-scoped session diff viewer with file, sync, and view menus (#122470)
* feat(ui): commit-scoped session diff viewer with file, sync, and view menus

The Control UI session diff panel becomes a dense Amp-style viewer: per-file
menus (copy path, open file, reveal in file tree, open in editor), a Sync
Locally popover with a copyable git fetch command, view options (collapse all,
wrapping, split/unified layout), and a sticky footer that reports how far the
branch is ahead of its merge base and switches between all changes,
uncommitted work, and individual commits.

sessions.diff gains an additive scope param (all | uncommitted | commit) plus
commits, aheadCount, and mergeBase metadata. Commit-scope diffs read only the
object database, skip untracked collection, and bypass session-start baseline
filtering; unknown commits surface a typed unavailableReason. Offscreen file
bodies use content-visibility so large diffs stay responsive.

* fix(ui): satisfy session diff CI gates

* fix(gateway): fence commit-scoped session diffs to the advertised branch history

ClawSweeper found that commit-scoped sessions.diff accepted any commit resolvable in the checkout. Fence operator.read commit reads to the advertised merge-base..HEAD history and cover sibling-branch and base-history commits.
2026-08-12 01:08:33 -07:00
Peter Steinberger 5a643e3543 fix(plugins): keep packed entries consistent through package lifecycle (#121174)
* fix(plugins): own packed entry lifecycles by package

Persist package ownership per runtime child, route lifecycle actions through one closed resolver, reconcile removed child policy during updates, and retain rollback generations until durable config/index commit.

* fix(plugins): break uninstall policy import cycle

* test(plugins): model package ownership in lifecycle fixtures
2026-08-12 00:49:29 -07:00
Peter Steinberger bfe1f33ea0 improve(ui): make Control UI feel native on mobile (#122492)
* improve(ui): make Control UI feel native on mobile

* fix(ui): keep coarse-pointer input floor text-scale aware

* fix(ui): let self-sized controls opt out of the touch input floor

* fix(ui): fold per-control coarse-pointer font floors into the shared touch floor
2026-08-12 00:46:35 -07:00
Peter Steinberger af3550df73 fix(ui): confirm automation removal (#122505)
Bind irreversible Cron removal to the current Gateway, admin scope, state object, and job revision across the confirmation modal.
2026-08-12 00:41:17 -07:00
Peter Steinberger 0de17482ae feat(gateway,ui): quiet Where picker, placement chip, and projects read model (#120804)
* feat(gateway): projects.list groups known checkouts by repo identity

Implements docs/plan/runners.md milestone 4 derived projects read model.

* feat(ui): regroup the Where picker by gateway, devices, and cloud

* feat(ui): placement chip shows where a session runs with reclaim

Implements docs/plan/runners.md milestone 4 placement display and reclaim.
2026-08-12 00:21:23 -07:00
Peter Steinberger 6f39f953ec fix(ui): show each chat panel button once, in the pane header (#122507)
* test(ui): advertise terminal and browser panels in the mock harness

The mocked Control UI never advertised browser.request or terminal.open and
left terminalEnabled false, so the chat header's panel toggles were invisible
in the harness and could not be visually verified.

* fix(ui): give the chat pane header the only panel toggle row

The session workspace rail header rendered Terminal, Browser, Ask OpenClaw and
Changes alongside its own dock/refresh/collapse controls. Terminal and Changes
already lived in the chat pane header, so both rendered twice at once, while
Browser was reachable only from inside a files rail and Ask OpenClaw sat in a
per-session rail despite being a global surface that already owns a sidebar
entry (settings route 'custodian').

The rail header now owns workspace-file actions only. Browser moves up to the
pane header beside Terminal and into the narrow-header overflow menu; the
duplicated Terminal and Changes buttons and the Ask OpenClaw toggle are gone.

Production LOC: +21 -84.

* test(ui): stop the vite stub from shadowing the mock bootstrap config

ui/vite.config.ts registers a placeholder /control-ui-config.json middleware
and config-file plugins load before inline ones, so the mock gateway plugin's
bootstrap body never reached the app and every scenario bootstrap field was
silently dropped. Marking the mock plugin 'pre' lets it answer first.
2026-08-12 00:13:13 -07:00
Peter Steinberger 07d9f9c92c refactor(gateway): extract source-agnostic desktop relay core (#122455)
* refactor(gateway): extract source-agnostic desktop relay core

Split the cloud-worker desktop observer into a reusable core under
src/gateway/desktop/ so upcoming desktop sources (gateway host, nodes)
plug into one relay pipeline:

- attachment.ts: RfbAttachment union (unix socket | loopback TCP)
- session-registry.ts: generic observer lifecycle (8-observer cap,
  controller eviction, 60s linger, owner-epoch fencing) with typed
  stale-owner/stopped errors
- observe-bridge.ts: single-use observe tokens + WS relay, path moves
  to /desktop/observe (clients consume wsPath verbatim)
- rfb-view-only-filter.ts: pure move

desktop-tunnel.ts keeps only worker SSH acquisition and app launch,
plugged into the shared registry; external API unchanged. Behavior
is identical; worker desktop tests pass unmodified apart from import
and path renames. Drops the never-used now() option.

* fix(gateway): fence only superseded desktop sessions

The desktop core extraction replaced the owner fence's "stop strictly
older owners" check with an unconditional session stop. A launcher that
claims an owner epoch first, then reaches its async fencing pass after a
same-epoch observe has already created the session, tore that session
down and failed the observer with "stopped before connecting".

Restore the original invariant in the registry that owns it:
stopSuperseded() retires an entry only when its epoch is strictly lower
than the claimant's, so peers sharing a generation keep the session.

The regression test drives launch-then-observe at one epoch and fails on
the pre-fix code inside fenceReplacedOwners.

* refactor(gateway): drop unused WorkerDesktopTunnels type export
2026-08-11 23:58:06 -07:00
Peter Steinberger 87ecc4085d feat(webui): auto-request notification permission on first chat send (#122446)
* feat(webui): auto-request notification permission on first chat send

The Control UI now asks for notification permission automatically on the first chat message you send (once per browser/origin; web push in browsers, native prompt in the macOS app; denied permission is never re-asked automatically — Settings -> Notifications stays the manual path).

* fix(webui): preserve notification prompt gesture boundary

Restrict the one-shot notification prompt to direct non-command composer sends, invoke browser permission synchronously, and keep Settings documented as the management and recovery path.

* fix(webui): exclude deferred notification sends

Require an actively dispatching browser input event before consuming the notification one-shot, so retained catalog handoffs and other deferred sends cannot suppress the first real composer prompt.
2026-08-11 23:52:05 -07:00
Peter Steinberger b080dd1e76 refactor: consolidate coercion contracts (#122458)
* refactor: consolidate coercion contracts

Centralize exact string, record, numeric, date, Boolean, argument, and structured-error coercions while preserving call-site semantics.

Migrate canonical-name collisions and deprecated internal SDK bypasses, deleting 55 net production/tooling lines. Expand declaration ownership enforcement to 101 allowed helpers and add a narrow export-completeness audit.

* fix: preserve standalone script coercions

Keep copied Control UI tooling self-contained and retain the trusted release harness module-relative source seam when the harness runs against an old target cwd.
2026-08-11 23:26:37 -07:00
Peter Steinberger f166d4ee98 fix(ui): repair shared test isolation (#122451) 2026-08-11 22:26:52 -07:00
Vyctor H. Brzezowski dd57cfb6c1 fix(ui): show catalog labels in hidden section settings (#122320)
* test(ui): reproduce hidden catalog id labels

* fix(ui): label hidden session catalogs

* test(ui): capture hidden catalog label evidence

* test(ui): harden hidden catalog label proof
2026-08-12 02:18:05 -03:00
Vyctor H. Brzezowski 0295b7ab54 fix(ui): gate model shortcuts by search focus (#122316)
* improve(ui): unify model picker shortcut hints

* fix(ui): gate model shortcuts by search focus

* fix(ui): focus model trigger on open
2026-08-12 02:14:47 -03:00
Peter Steinberger 1c46d517c4 fix(ui): confirm WhatsApp logout (#122437)
Require an account-bound confirmation before deleting WhatsApp credentials, and revalidate Gateway ownership plus account state after the modal resolves.
2026-08-11 22:07:35 -07:00
Peter Steinberger f6459a3255 refactor(ui): split chat thread into transcript owners (#122420)
* refactor(ui): split chat thread into transcript owners

* refactor(ui): delete orphaned pinned-messages surface

* chore(lint): ratchet max-lines baseline after chat-thread split

* fix(ui): tighten transcript owner type boundaries

* refactor(ui): prune pinned-message orphans
2026-08-11 21:49:47 -07:00
Peter Steinberger 6b0be0215f refactor(ui): split new-session page into draft owners (#122413)
* refactor(ui): split new-session page into draft owners

* refactor(ui): delete duplicated draft plumbing

* chore(lint): ratchet max-lines baseline after new-session split
2026-08-11 21:15:32 -07:00
Peter Steinberger 0e6f38178a refactor(ui): simplify model effort profile flow (#122412)
* refactor(ui): simplify model effort profile flow

consolidate the Gateway profile shape, remove New Session's synthetic row, and retain boundary regression coverage.

* fix(gateway): preserve thinking projection order

Keep deterministic response bytes while consuming the canonical thinking profile.
2026-08-11 21:08:26 -07:00
Vyctor H. Brzezowski 2a1eba7713 fix(ui): show local project icons in the chat header (#122406)
* fix(ui): resolve project icons from deterministic paths

Prepare project icon bytes during chat startup and serve only the process-stable snapshot. Keep all filesystem work asynchronous and lift the breadcrumb trail onto the topbar's optical axis.

* fix(ui): keep project icon paths web-focused

Drop the IDE-specific icon convention so every candidate remains an explicit web project path.

* fix(ui): preserve project icon compatibility

Restore existing web icon paths and refresh bounded session snapshots when they are served. Keep the IDE-specific path excluded per the final product decision.
2026-08-12 00:52:44 -03:00
Vyctor H. Brzezowski 5583459475 improve(ui): use diff icon for session changes (#122399) 2026-08-12 00:26:22 -03:00
Vyctor H. Brzezowski fe1dafbada fix(ui): show nested session parent in chat header (#122335)
* fix(ui): show nested session parent in chat header

* fix(ui): cap nested chat breadcrumb width

* test(ui): avoid breadcrumb geometry shadowing

* chore(ui): keep breadcrumb comment issue-agnostic
2026-08-12 00:18:11 -03:00
Vyctor H. Brzezowski 56e6221a81 fix(ui): align chat disclosure gutters (#122286) 2026-08-11 23:40:36 -03:00
Shakker 05d0d980d8 fix: fence companion context snapshots 2026-08-12 03:26:33 +01:00
Shakker 0e78a64df5 fix: simplify session companion grounding 2026-08-12 03:26:33 +01:00
Vyctor H. Brzezowski 7630d060f8 improve(ui): quiet slash command menu hierarchy (#122237)
* improve(ui): quiet slash command menu hierarchy

* improve(ui): compact slash command menu

* improve(ui): polish slash command rows

* improve(ui): streamline slash command sheet

* chore(ui): refresh command copy baseline

* fix(ui): keep skill selection in view

* test(ui): type skill menu viewport stub
2026-08-11 23:00:58 -03:00
Peter Steinberger 8876528f7c feat: clone GitHub projects from session picker (#121818)
* feat(projects): add managed GitHub clones

* feat(ui): clone GitHub projects from session picker

* fix(projects): integrate current gateway owners

* fix(protocol): derive Swift error detail accessors

* fix(ui): gate project cloning by scope

* test(gateway): isolate session prewarm probes

* revert: drop duplicate session prewarm repair
2026-08-11 18:49:16 -07:00
Vyctor H. Brzezowski 7c83fc1729 fix(ui): show a placeholder for remote markdown images (#122312)
* fix(ui): show remote image placeholders in chat

Keep remote Markdown images fetch-blocked while giving users a visible outcome and an explicit HTTPS open action.

* fix(ui): preserve links around image placeholders
2026-08-11 22:48:20 -03:00
Vyctor H. Brzezowski 89bae11799 fix(ui): fit short Canvas previews (#122294) 2026-08-11 22:43:07 -03:00
Vyctor H. Brzezowski bbb5164ec9 fix(ui): keep chat sharing menu compact (#122322)
* fix(ui): keep chat sharing menu compact

* fix(ui): refine sharing menu sections

* fix(ui): use one sharing menu scroll
2026-08-11 22:34:46 -03:00
Vyctor H. Brzezowski 9dfc26ba63 fix(ui): fit chat table cards to their columns (#122297)
* test(ui): cover chat table card width

* fix(ui): fit chat table cards to columns
2026-08-11 22:10:23 -03:00
Vyctor H. Brzezowski ff73a14f5a fix(ui): match chat transcript text rendering to prototype (#122328)
* fix(ui): match chat transcript letter spacing

* test(ui): match computed zero letter spacing

* fix(ui): scope transcript letter spacing

* test(ui): compare inherited transcript tracking

* fix(ui): scope transcript letter spacing
2026-08-11 21:45:19 -03:00
Peter Steinberger 964c8c84c1 refactor: consolidate coercion ownership (#122299)
* refactor: consolidate coercion ownership

Centralize four canonical coercion helpers, migrate exact core and plugin duplicates through narrow Plugin SDK facades, and enforce declaration and plugin-normalization ownership boundaries.

The sweep adds eight focused SDK exports while deleting more production and tooling code than it adds. User-visible behavior is unchanged except for safer equivalent object and UI parsing at existing boundaries.

* fix: guard integer option ownership

Register resolveIntegerOption with the canonical function owner and extend the declaration-guard fixture so future local duplicates fail validation.

* fix: keep integer helpers on numeric facade

Remove the unshipped duplicate string-coerce exports and route every affected plugin consumer through the existing number-runtime contract.

* fix: point numeric coercion to number runtime

Make boundary and declaration diagnostics recommend the canonical numeric facade, with failing-before coverage for both guidance paths.
2026-08-11 17:14:53 -07:00
Peter Steinberger cd0a1235a3 feat: sync new-session preferences and recents by identity (#121816)
* feat(gateway): add identity preferences and project recents

* feat(ui): sync new-session identity state

* docs: explain identity-scoped session state

* test: track preference temp directories

* fix(gateway): preserve identity preference boundaries

* chore(protocol): refresh identity preference bindings

* test: refresh historical schema hashes

* style(gateway): format method order assertion

* fix(protocol): emit project recent Swift models

* test(gateway): track preference RPC release train

* fix(gateway): harden identity preference state

* fix(state): keep preference errors internal

* chore: refresh split plugin SDK baseline

* fix(gateway): use core session store loader

* refactor(state): fold additive migration checks

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* test(ui): relocate identity recents e2e

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline

* chore: regenerate plugin SDK baseline
2026-08-11 16:52:59 -07:00
joshavant 3776770a29 fix(ui): keep run inspector evidence reachable 2026-08-11 18:44:38 -05:00
joshavant 77f9bcd484 test(ui): satisfy Activity layout lint 2026-08-11 18:44:38 -05:00
joshavant d32a290b7c fix(ui): preserve live Activity viewport 2026-08-11 18:44:38 -05:00
joshavant 5751562bfe fix(ui): retain shared Activity status copy 2026-08-11 18:44:38 -05:00
joshavant 9008f9b0e2 fix(ui): paginate ambiguous run candidates 2026-08-11 18:44:38 -05:00
joshavant 5f7ea6b548 fix(ui): lazy-load run inspector copy 2026-08-11 18:44:38 -05:00
joshavant acb7f7192a fix(ui): support exact run execution selection 2026-08-11 18:44:38 -05:00
joshavant 0f1a549a51 fix(ui): label Activity tab panel 2026-08-11 18:44:38 -05:00
joshavant a2ca2ac8e7 fix(ui): use shared Activity tabs 2026-08-11 18:44:38 -05:00
joshavant 7ab5d99a6c feat(ui): add durable run inspector 2026-08-11 18:44:38 -05:00
Peter Steinberger db9bdb4a11 fix(gateway): preserve session identity through archive transitions (#121169)
* fix(sessions): fence lifecycle mutations by identity

* fix(sessions): finalize lifecycle identity fences
2026-08-11 16:43:10 -07:00
Shakker 24ad2e5d43 fix: bound companion settlement context 2026-08-12 01:17:17 +02:00
Shakker 8dc5016460 fix: make companion context fail closed 2026-08-12 01:17:17 +02:00