Commit Graph

542 Commits

Author SHA1 Message Date
WhatsSkiLL 407930cb6a feat(wear): add direct agent, session, and model pickers (#126863)
* feat(wear): add searchable context pickers

* fix(wear): simplify context picker rows

* fix(wear): prioritize chat actions over context

* fix(wear): keep chat follow above context controls

* fix(wear): keep context labels localized

* fix(wear): refine context picker layout and close

* fix(wear): sync picker locale resources

* fix(wear): negotiate picker search support

---------

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
2026-08-21 11:54:47 -04:00
Vincent Koc 2405b98b05 fix(android): explicitly target notification intents (#127055) 2026-08-20 23:48:16 -07:00
Peter Steinberger f0881cfaa9 test: trim native test mirrors (#126896) 2026-08-20 17:41:18 -07:00
Peter Steinberger 1bf3e4e9b8 fix(android): gate gateway RPC polling on the hello method catalog (#126540)
* fix(android): gate gateway RPC polling on the hello method catalog

Released 2026.7.x gateways authorize before dispatch and reject unknown
methods with "missing scope: operator.admin", so the app's
"unknown method: X" detectors never fired: outbox sends parked forever
behind an ~800ms sessions.branches.list retry loop and question.list
retried on every health event. Generalize the progress-card negotiation
(3377a21c4e) into a tri-state gatewayAdvertisesMethod seam fed by
hello features.methods and skip sessions.branches.list, question.list,
and progressCard.get when the gateway does not advertise them; branch
scopes reconcile immediately and queued sends flush.

* fix(android): keep the hello method catalog unknown when hello omits features.methods

A successful connect without a usable features.methods list must not read
as a known-empty catalog: parse it as null so gatewayAdvertisesMethod stays
tri-state and the catalog gates no-op instead of skipping documented RPCs.
Pairing capabilities keep positive-advertisement semantics via orEmpty().
Addresses the ClawSweeper P1 on #126540.
2026-08-19 22:56:15 -07:00
Peter Steinberger 0a86702241 feat(github): authorize agent identities from Settings (#126474)
* feat(github): add device authorization lifecycle

* fix(github): harden device authorization lifecycle

* fix(github): refresh native tool display snapshot

* fix(github): refresh config and UI baselines

* refactor(github): break OAuth identity import cycle

* test: make gateway retry deadline assertion scheduler-safe
2026-08-19 17:41:58 -07:00
Peter Steinberger 0606e31d0e feat(gateway): broker GitHub publication (#126306)
* feat(gateway): broker GitHub publication

* refactor(gateway): split publication owners

* fix(gateway): enforce publication branch authority

* fix(gateway): bind publication to remote identity

* fix(gateway): bind publication recovery to remote state

* fix(gateway): preserve publication git state

* fix(gateway): retain publication recovery authority

* fix(gateway): commit publication index atomically

* fix(gateway): narrow publication index errors

* refactor(gateway): keep publication CAS errors private

* fix(gateway): recover publication index transactions

* fix(agents): describe GitHub publication tool

* fix(gateway): harden publication base fetch

* fix(gateway): reject publication filter semantics

* fix(gateway): verify publication creation base

* refactor(agents): align publication tool options

* fix(gateway): isolate publication object lineage

* refactor(gateway): use shared table probe

* test(gateway): keep publication helpers in routed suite

* perf(ui): lazy-load GitHub publication request

* fix(gateway): preserve publication support contracts

* fix(gateway): recover publication before authority checks

* fix(gateway): fence local publication snapshots

* fix(android): format generated protocol models

* fix(gateway): harden publication recovery

* fix(gateway): fence publication recovery

* fix(ui): reset completed publication cycles
2026-08-19 11:05:12 -07:00
Peter Steinberger ae55a4090c refactor(canvas): make the panel a widget presenter (#126030)
* refactor(canvas): retire legacy host and commands

* refactor(apple): narrow shared Canvas contracts

* refactor(macos): keep Canvas as widget presenter

* refactor(ios): remove Canvas client

* refactor(android): remove Canvas client

* refactor(linux): remove Canvas client

* fix(ci): isolate native locale artifacts

* fix(linux): regenerate companion lockfile

* fix(canvas): refresh native tool display metadata

* test(canvas): align coverage with presenter surface

* test(canvas): remove obsolete asset root seam

* test(canvas): stabilize retirement CI coverage

* refactor(swift): remove orphaned resource wrapper

* test(ios): remove retired canvas layout assertion

* fix(macos): reserve retired canvas command namespace

* refactor(macos): isolate canvas command policy

* fix(canvas): select only eligible macOS panels

* fix(canvas): keep panel selection plugin-owned
2026-08-19 08:21:07 -07:00
Peter Steinberger 4af09d4961 feat(ui): unify focused presentation routes (#126143)
* feat(ui): unify focused presentation routes

/focus/<target> replaces unshipped standalone query links across dashboard, terminal, desktop, and native apps.

Gateway-served index assets are anchored so nested documents resolve their bundles from the Control UI base path.

* test(gateway): narrow emitted asset URLs

Fixes check:test-types TS18048/TS2322 by dropping unmatched optional captures before comparing emitted asset URLs.

* test(docs): follow centralized cloud secret guidance

Fixes the stale current-main docs test after #126132 centralized GCP and Hetzner setup in docker-vm-runtime.

* test(ui): retry missing locator reads

The 500ms locator text read can time out while the menu label is still rendering, causing expect.poll to reject instead of using its owning 10s retry window. Treat only Playwright TimeoutError as a missing value so the outer poll retries while page-closure and arbitrary failures still surface.

* test(android): capture TLS probe coroutine

The TLS probe test inferred its coroutine from mutable scope children, racing unrelated child startup and teardown in CI. Capture the exact Job from inside the probe coroutine and join that owner before asserting the stale-attempt guard.

* fix(gateway): preserve plugin focus routes

Keep approval handling ahead of plugin dispatch, but treat focus documents as an unclaimed Control UI fallback after plugin authentication and routing. Exact and prefix plugin routes therefore retain ownership, while unclaimed reads serve the focus document and other methods return 404.

* fix(ui): migrate released terminal links

Preserve stable v2026.7.1 terminal query compatibility by rewriting the root/base ?view=terminal URL once to the canonical /focus/terminal path with history.replace. Keep URL parsing path-only, and leave the removed desktop and dashboard query forms as a hard cut.

* test(codex): assign run-attempt tools shard

Cached filtered configs caused duplicate ownership, and the test lacked a canonical full-suite owner.

* test(ui): keep cloud recovery proof state-owned

The recovery test should assert owner state and reload identity, while dedicated tests own transient alert visibility.

* test(qa): wait for outbound bus state

* fix(qa): reserve gateway ports through staging

* refactor(qa): keep socket creation in gateway owner
2026-08-19 03:41:29 -07:00
Peter Steinberger e71fc902ee fix(gateway): make activeRunIds presence mean a complete exact run set (#126106)
* fix(gateway): make activeRunIds presence mean a complete exact run set

Session rows no longer emit activeRunIds: [] while hasActiveRun is
true. Presence now means the complete exact set of direct run ids;
omission means identities are unavailable (projected/embedded owners);
[] only ever represents proven idle. Consumers stop guessing:
soleActiveSessionRunId() replaces the arbitrary [0] fallbacks in the
observer digest, transcript cache key, activity inspector, and
stale-terminal reconciliation, each falling back to its owner fact.

Follows the maintainer direction from #125983: the field stays as
Gateway-owned exact facts; producer-side liveness/observer projections
are a named follow-up.

* fix(gateway): clear unavailable active run ids in events

* fix(gateway): preserve idle active run sets

* fix(clients): close active run id cache gaps

* test(android): isolate history run snapshot
2026-08-19 02:08:57 -07:00
Peter Steinberger b8ff4fe3d7 fix: show sessions waiting for concurrency slots (#125654)
* fix: show sessions waiting for concurrency slots

* test: align queued session integration fixtures

* test: distinguish queued and reactivated followups

* fix: preserve queued state in workboard and android

* fix: project queued status through chat history

* test(ui): keep queued sidebar case under line cap
2026-08-18 02:31:39 -07:00
Peter Steinberger af599f2cf7 feat(android): render durable progress card in chat (#125444)
* feat(android): render durable progress card in chat

Remove Android consumption of the legacy stream:"plan" agent events and in-flight plan snapshots.\n\nHandle progressCard.changed through the durable progressCard.get store and remove the Android protocol coverage allowlist entry.

* fix(android): refetch progress card on unattributable poke

The changed event carries the server-derived observer scope key, which the
client only learns from a get response carrying a card. Before that, a
canonical-keyed poke (e.g. global session scope) failed both match checks and
was silently dropped until reconnect. Unknown attribution now triggers an
authoritative refetch instead.

* chore(i18n): refresh native source baseline for progress-card strings

* fix(android): render legacy plan events when the gateway lacks the progress-card store

Released gateways through v2026.7.x emit stream:"plan" events and do not advertise progressCard.get, so retain a negotiated Android fallback.

Remove this branch with the gateway legacy dual-emit after the minimum supported gateway ships the progress-card store.
2026-08-17 18:42:19 -07:00
BYTE 46e44baf46 fix(android): retain pinned sessions in compact picker (#125264)
* fix(android): retain pinned sessions in compact picker

* fix(android): prioritize pinned sessions in compact picker

* fix(android): satisfy ktlint in session picker
2026-08-17 21:39:37 -04:00
Víctor Gurbani Carballo daf7cae8c0 fix(android): new chats receive generated titles (#123670)
* fix(android): allow generated session titles

* fix(android): keep unnamed chat titles local

Co-authored-by: victor-gurbani <89997914+victor-gurbani@users.noreply.github.com>

* chore(android): refresh native i18n inventory

Co-authored-by: victor-gurbani <89997914+victor-gurbani@users.noreply.github.com>

* fix(ci): keep fork lint within hosted limits

Co-authored-by: victor-gurbani <89997914+victor-gurbani@users.noreply.github.com>

* test(ci): align prerelease manifest fixture

Co-authored-by: victor-gurbani <89997914+victor-gurbani@users.noreply.github.com>

* fix(android): align chat switcher session titles

Co-authored-by: victor-gurbani <89997914+victor-gurbani@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-17 03:35:34 -07:00
WhatsSkiLL 657b6e8f49 feat(android): pin gateway-backed session search in the sidebar (#124338)
* fix-android-chat-session-picker

* fix-search-all-android-sessions

* fix-native-i18n-inventory

* fix-android-share-session-browser-policy

* fix(android): show loading during session search

* refactor(android): move gateway-backed session search into the pinned sidebar

The sidebar header and search field no longer scroll away: they sit above
the scrolling sections, and the search field is always visible instead of
hidden behind a toggle. Queries now run through the shared session-browser
search state (debounced gateway search with offline fallback) instead of a
local filter over cached rows, and matching threads replace the section
list while a query is active.

The in-chat bottom-sheet picker is removed: the compact switcher's All
button navigates straight to the Sessions screen again, and the sidebar
owns in-context session search. The shared rememberSessionBrowserSearchState
extraction from the Sessions screen is kept and gains the sidebar as its
second consumer.

* style(android): fix sidebar import ordering for ktlint

---------

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-17 01:22:16 -07:00
Peter Steinberger f59e945013 feat(gateway): move sessions between Gateway and runners (#125036)
* chore: preserve session placement move work

* feat(gateway): move sessions between runners

* test(gateway): align recovery fixture with move barriers

* fix(protocol): format literal-only Swift initializers

* test(ui): prove session move terminal state

* refactor(gateway): narrow session move target fence

* fix(gateway): preserve pending result recovery ownership

* test(workers): migrate reclaim lifecycle fixtures

* refactor(gateway): keep move internals private

* test(gateway): align split bootstrap fixture with moves

* fix(gateway): recover session moves through exact intent

* refactor(gateway): derive move placement subtypes locally
2026-08-17 00:23:13 -07:00
Peter Steinberger d095113698 refactor(android): collapse the chat's second color palette into ClawTheme (#125020)
* refactor(android): consolidate chat palette

* refactor(android): fold the font family into ClawTheme
2026-08-16 21:31:27 -07:00
Peter Steinberger 2c4651538c improve(android): drop the role title on every chat message (#124985)
* fix(android): let chat bubbles carry message roles

* test(android): prove chat speaker accessibility

* chore(android): refresh native i18n inventory

* ci: shrink assertion safety baseline

* fix(android): satisfy Compose modifier lint
2026-08-16 20:21:59 -07:00
Peter Steinberger eeece038cd fix(android): show plan status and explanation (#124958) 2026-08-16 19:11:10 -07:00
Vyctor H. Brzezowski f61ec66249 Preserve ClawHub external source identity and expose only supported actions (#124250)
* fix(skills): keep ClawHub search results on the source the operator picked

ClawHub search returns each result's origin under `install.reference`, but the
response model expected a flat `installRef`. That field is never present, so
every row fell through to a synthesized `@owner/slug` reference. External
skills.sh results were rewritten onto a ClawHub-native identity, dropping both
the commit-pinned source and the "not scanned by ClawHub" trust record.

Map the search wire shape explicitly and make the search contract
action-specific: `installRef` always names the result's own source, `detailRef`
appears only while ClawHub can serve a detail card for that identity, and
`trustState` travels with unscanned sources. Clients render install directly
when detail is absent instead of offering a review the Gateway must refuse.

Covers the Control UI, macOS, iOS Settings, iOS AgentPro, and Android, which
previously routed every row through review and could not install an external
skill at all.

* fix(skills): make install-only sources explicit and keep legacy review intact

Address review findings on the search identity contract:

- Replace the detail-reference capability with an explicit `installOnly` flag.
  A Gateway released before this field omits it, and reading omission as
  install-only made ordinary registry results skip the reviewed-version flow on
  every client. Absence now means the existing review-then-install path.
- Parse closed source variants in the producer. A row whose source is unknown,
  whose external reference is missing, or whose registry publisher is absent is
  dropped instead of falling through to `@owner/slug`, which was the original
  source swap in a different disguise.
- Carry the exact install reference alongside the canonical slug. The Gateway
  already records `requestedReference`; the clients dropped it and matched
  installs by slug, so a completed external install read back as unknown.
- Gate the direct-install action on admin rights. The row previously stayed
  enabled for read-only operators and reached a guard that silently returned.
- Route the unscanned-source warning through the native and Control UI string
  catalogs instead of a hardcoded literal.

* chore(i18n): leave generated native locale artifacts to the refresh workflow

Preflight isolates generated locale output from source changes: only the native
sources and apps/.i18n/native-source.json belong in a feature commit.

* fix(skills): satisfy Android ktlint wrapping and Swift test link construction

Extract the ClawHub result action guard into a named value so the multiline
condition follows ktlint wrapping, and pass the new requestedReference field in
the OpenClawKit installed-link fixtures.

* fix(skills): preserve external install identity across clients

* test(skills): add exact refs to recommendation fixtures

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-08-15 21:38:46 -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
WhatsSkiLL e37e63ceb6 feat(android): add Wear Agent Pulse (#122123)
* feat(android): add Wear agent pulse

* fix-android-wear-pulse-selected-session

* fix(android): count direct Wear Pulse subagents

* fix(android): gate Wear Pulse page by capability

* fix:bound-wear-pulse-gateway-reads

* fix: keep native locale artifacts generated

---------

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: Colin Johnson <colin@solvely.net>
Co-authored-by: Solvely-Colin <159171061+Solvely-Colin@users.noreply.github.com>
Co-authored-by: Colin Johnson <211764741+Solvely-Colin@users.noreply.github.com>
2026-08-14 12:22:26 -04:00
Peter Steinberger 703c681aac test: remove latest duplicate coverage (#123551) 2026-08-14 01:39:19 -07:00
Peter Steinberger e5fca42952 fix(apps): per-session desktop button opens that session's machine (#123412)
* fix(apps): open session desktop on its machine

* fix(ui): scope the desktop session lookup to the key's own agent

`sessions.list` has no exact-key filter, so the viewer resolves a `session=`
parameter by searching for the key and matching it exactly in the response. A
key that prefixes longer ones — `agent:main:main` alongside user-named sessions
that start the same way — could push the exact row outside a five-row page and
silently fall back to the picker.

Session keys encode their agent and the list API accepts `agentId`, so scope the
search to that agent and widen the page.

* refactor(ui): stop parking a session key in the desktop environment id

Document-mode inventory failures stashed the requested session key in
`environmentId` purely so the Retry button's non-null guard would pass, even
though document-mode retry refreshes the inventory and never reads the value.
A session key only names a machine once the inventory loads, so it now stays
out of `environmentId`, and the retry branch that ignores it runs before the
guard. Adds E2E coverage for recovering a session-preselected desktop.

* fix(ui): resolve the desktop session with an exact-key lookup

The session-preselect path searched `sessions.list` and scoped the search to
the key's own agent, but a bounded search cannot rule a key out: 25 newer
same-agent sessions sharing the requested key's prefix would push the exact row
off the page, and the viewer would report the source as unavailable for a
session that exists. `sessions.describe` is the exact-key operation and already
projects placement, so the panel calls it directly and the app-root resolver
plumbing goes away with it.

* refactor(ui): move the desktop document keyboard bridge into its own controller

`desktop-panel.ts` crossed the 700-line cap. The mobile keyboard bridge — the
padded sentinel, the value diffing that turns composed input into backspaces
and text, and the field focus/reset helpers — is a self-contained concern, so
it moves into a `DesktopMobileKeyboard` controller alongside the existing
fullscreen controller instead of taking a `max-lines` suppression.

* test(ios): drain text fields instead of assuming a delete burst lands

`testReleaseChatScreenshot` typed a 5-character probe, sent 5 deletes in one
`typeText`, then asserted the field was empty. CI dropped one synthetic
keystroke under simulator load and the assertion failed with a leftover "f".
XCUITest makes no lossless-burst guarantee, so clearing now re-sends against
whatever the field actually still holds, bounded. The two gateway-setup fields
that overtyped through the same burst use the helper for the same reason.

* fix(ui): keep the chat placement owner out of the startup chunk

`desktop-document-mode.ts` is imported by bootstrap, so importing the chat
placement owner from it pulled the chat page's dependency tree into the startup
bundle and pushed startup JS past its gzip budget (331075 B against a 330507 B
allowance). The route module now only parses the URL; resolving a session to its
machine moves next to the lazily loaded desktop panel, which is the only caller.
Startup JS is back to 329710 B.
2026-08-13 23:33:19 -07:00
Peter Steinberger 2b61dc5429 fix(mobile): embedded Control UI follows the app's dark or light appearance (#123408)
* fix(mobile): sync Control UI webview appearance

* docs(android): explain why an appearance flip rebuilds the Control UI webview
2026-08-13 22:05:55 -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
Peter Steinberger af377128b4 feat(android): unify chat composer with iOS and web design (#123077)
* feat(android): unify chat composer with iOS and web design

Collapse the three attachment buttons into one + menu (Photos/Videos/Files),
move model and thinking pickers into a slim footer inside the composer card
with a freshness-gated context ring, and morph the trailing button between
talk, send, and run-abort stop. Idle talk drops the alarm-red fill; the
separate Stop pill and the Context -- placeholder row are deleted. Run abort
stays reachable while live talk is active.

* chore(android): refresh native i18n baseline for composer strings
2026-08-13 02:04:03 -07:00
Vyctor H. Brzezowski aba94bbe0b fix: install or review the publisher you picked when ClawHub skills share a slug (#121697)
* fix(skills): keep ClawHub publisher identity from search through install

ClawHub search returns one entry per publisher, so several results can share a
slug. Every client collapsed the selection to that bare slug before calling
skills.detail and skills.install, and ClawHub answered 409 AMBIGUOUS_SKILL_SLUG
with no in-product way forward.

searchClawHubSkills now records the publisher-qualified reference once, on the
result that carries it, and the Gateway protocol documents it. skills.detail
parses the same reference grammar skills.install already accepted, so review and
install cannot resolve to different publishers. Control UI carries that one
reference through row actions, detail, busy state, and acknowledgement retries,
and shows it so otherwise identical rows are distinguishable.

Fixes #117633

* fix(apps): send the ClawHub publisher reference from native skill browsers

macOS, iOS, and Android read the qualified reference from search results and use
it for skills.detail, install, busy state, installed matching, and list identity,
so two publishers sharing a slug stay distinct instead of collapsing into one
ambiguous request.

* fix(skills): refuse external-source skill detail instead of reading a same-slug skill

ClawHub has no source-qualified read endpoint, so a skills-sh reference parsed
down to its bare slug would have returned a registry skill's card while install
resolved the external artifact. Review and install could name different skills.

skills.detail now fails closed on any reference that carries a source, and the
macOS and AgentPro rows show the publisher reference next to the summary instead
of only when a summary is missing, so same-slug rows stay distinguishable.

* chore(apps): refresh native i18n source baseline for the skill row references

* refactor(skills): drop the unread search-result ownerHandle field

installRef is the one reference clients send back, and no client reads the
publisher handle separately, so the protocol and Control UI carry one field
instead of two.

* fix(skills): name the next step when external skill detail is refused

Clients that gate install behind a successful review would otherwise see only a
refusal, so the error names the direct install path and the CLI equivalent.

* fix(macos): use a doc comment on the ClawHub row subtitle

swift-format's docComments rule requires doc comments on declarations; the
subtitle property carried a regular comment and failed macos-swift.

* fix(skills): carry ClawHub trust state to clients that can install

Forwarding installRef let clients install the exact publisher the operator
picked, including external skills-sh sources. It did not forward the trust
state that says ClawHub never scanned that source, so iOS AgentPro — the one
surface that installs in a single tap with no review step — could install an
unscanned artifact with nothing on screen saying so. The CLI already labels
these (docs/clawhub/cli.md, docs/cli/skills.md); native clients could not,
because trustState was never on the wire.

trustState becomes an optional field on SkillsSearchResultSchema. It is purely
additive: older clients ignore an unknown key and the field is absent for
registry results, so downgraded readers are unaffected and no protocol version
moves.

Every client that renders a search row now shows "Not scanned by ClawHub",
matching the CLI wording exactly: iOS AgentPro in the row above the install
button, macOS and Android beside the review action, and Control UI on the row
that explains why review is refused for these sources.

Covered by a wire assertion that the state reaches clients for an external
source and stays absent for registry rows, plus decode-and-label tests on the
shared Swift kit and the Android parser, and a Control UI render assertion.

* fix(ui): size the ClawHub detail dialog to a refusal message

Refusing detail for an external source made an error-only dialog reachable.
The shared preview panel reserves a tall reader height for skill documents, so
a two-line refusal rendered in a mostly empty dialog and read as broken rather
than deliberate. Found by inspecting the review captures.

* revert(ui,apps): drop the ClawHub trust label layer

Maintainer product decision: skills.sh runs its own scanners, so OpenClaw does
not add a second alert layer in the apps. Removes the label from Control UI,
iOS, macOS and Android, and drops the trustState wire field that nothing would
render. The CLI keeps its existing label; changing that is a separate call.

Publisher identity, the fail-closed detail refusal, and the message-only dialog
are unchanged. Splits the oversized skills view test file to satisfy max-lines
without a suppression.

* test(ui): fix ClawHub skill fixture checks

* chore(plugin-sdk): refresh API baseline

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-08-12 20:13:15 -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 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 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
Peter Steinberger 896e3fb0ac fix(android): keep completed subagent activity visible for sixty seconds (#122089)
* fix(android): retain terminal subagent activity locally

* chore(android): preserve native i18n inventory
2026-08-11 15:19:57 -07:00
Peter Steinberger 01804a7531 feat(apps): Android chat adopts the system-notice family (#122268)
* feat(apps): Android chat adopts the system-notice family

Decode internal_system provenance and __openclaw markers in parseHistory,
classify into SystemNotice/SystemDivider timeline variants with web-parity
kinds, render minimal Compose rows, round-trip metadata through the
transcript cache textPartsJson envelope (no Room change), and extend the
deterministic screenshot fixture.

* fix(apps): keep generated locale artifacts out of the source PR

The native locale refresh workflow owns NativeStringResources.kt and
values-*/strings.xml; ship source strings plus the regenerated
apps/.i18n/native-source.json inventory only.
2026-08-11 15:03:15 -07:00
Peter Steinberger 8c3c8a2ab0 feat(android): show live subagent activity in chat (#121813)
* feat(android): show live subagent activity

* chore(android): mark task event handled

* fix(android): localize activity diff chips

* fix(android): reconcile dropped task activity
2026-08-10 21:08:44 -07:00
Peter Steinberger d4fd9cc96a fix(ui): restore settings search, media previews, clipboard, and native chat (#116654)
* fix(ui): restore search media and native text

* fix(native): unify visible chat text projection

* fix(ui): search composed array schemas

* fix(ui): keep config search off startup constraints

* fix(ui): honor schema and media path boundaries
2026-08-09 22:07:28 -07:00
Peter Steinberger c3a0d3e58a test(android): enforce shared talk config contract (#121333) 2026-08-09 18:55:04 -07:00
Peter Steinberger 0ef798d28d fix(gateway): keep hello authorization aligned with RPC access (#120888)
* fix(gateway): separate socket and device token scopes

* chore(i18n): refresh native source baseline

* style(ios): keep gateway channel within lint limit

* refactor(gateway): simplify scope metadata decoding

* ci(ui): isolate real-gateway e2e suites

* docs(ci): align runner table formatting

* chore(plugin-sdk): refresh api baseline

* ci(ui): route real-gateway retries to hosted runners

* chore(plugin-sdk): repair generated api baseline

* test(ui): select Labs toggles by title

* fix(gateway): preserve stored scopes without wire metadata
2026-08-09 14:50:07 -07:00
WhatsSkiLL d506f604d6 feat(android): add conversation reply notifications (#120389)
* feat(android): add conversation reply notifications

* fix(android): refresh native i18n inventory

* fix(android): harden conversation notification replies

* fix:android-conversation-notification-intent-hardening

* chore:i18n-refresh-native-source-inventory

* fix(android): notify completed background sessions

* fix/android-notification-reply-gateway-readiness

---------

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
2026-08-09 11:37:16 -04:00
Peter Steinberger a74738c16e feat(chat): add four rare working-claw stances and raise surprise odds (#120826)
* feat(chat): add four rare working-claw stances and raise surprise odds

* test(ui): assert claw surprises against the exported allowlist

* fix(ci): refresh native i18n inventory and drop test-only surprise export
2026-08-08 19:56:31 -07:00
joshavant 026f4045b7 Revert "fix(protocol): preserve gateway session attribution across node runs"
This reverts commit 735f176b01.
2026-08-07 18:40:17 -05:00
WhatsSkiLL a23444fc74 fix(android): gate Wear model controls by operator scope (#120016)
* fix(android): gate Wear model controls by operator scope

* chore(android): refresh native i18n inventory

---------

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: Colin Johnson <colin@solvely.net>
2026-08-07 11:00:41 -04:00
Dallin Romney 2a0bbd23f5 feat: expose session classification facts (#106832)
* feat: expose session presentation metadata

* fix: restrict session titles to safe display metadata

* fix: preserve saved session display names

* fix(protocol): tolerate future session presentation values

* fix(gateway): keep direct peer names out of presentation

* fix(protocol): connect session presentation to native rows

* fix(gateway): canonicalize session presentation metadata

* fix(gateway): satisfy session presentation CI checks

* feat(gateway): flatten session classification facts

* fix: repair session classification generated models

* fix(gateway): canonicalize session classification keys

* fix(gateway): classify spawned delivery sessions

* fix(android): retain session classification facts

* test(android): make scripted gateway calls concurrency-safe

* test(live-updater): isolate fixture git commands
2026-08-07 14:56:56 +08:00
Vincent Koc 735f176b01 fix(protocol): preserve gateway session attribution across node runs 2026-08-07 08:06:01 +08:00
WhatsSkiLL 238c8bfc23 fix(android): show active session status in sidebar (#118624) 2026-08-05 11:55:10 -04:00
Peter Steinberger 4511c9b71f refactor(android): remove obsolete chat rendering paths (#119615) 2026-08-05 08:17:04 -07:00
Peter Steinberger c49b68328e test(android): consolidate Wear bridge fixtures (#118559) 2026-08-03 00:42:43 -07:00
Peter Steinberger 4b3d0b37be test(android): consolidate connection TLS fixtures (#118532) 2026-08-02 23:29:37 -07:00
Peter Steinberger d2ff210679 test(android): compact chat persistence fixtures (#118456) 2026-08-02 21:51:33 -07:00
Peter Steinberger 39c506bbf2 test(android): compact onboarding gateway fixtures (#118383) 2026-08-02 20:09:31 -07:00
Peter Steinberger 1d2fc98bf4 test(android): reduce voice fixture duplication (#118270) 2026-08-02 19:27:55 -07:00
WhatsSkiLL 4c136a7dea fix-android-sidebar-search-results (#117981)
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
2026-08-02 17:13:16 -07:00