Commit Graph

1338 Commits

Author SHA1 Message Date
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
Peter Steinberger 6e458d84bf feat(agents): configure per-agent GitHub identities (#125199)
* feat(agents): add managed GitHub identities

* fix(agents): use opaque GitHub setup handles

* style(codex): format managed shell environment call

* refactor(agents): own managed GitHub process identity

* chore(config): refresh baseline after rebase

* fix(ci): satisfy managed GitHub identity gates

* fix(ci): repair managed GitHub identity checks

* test(agents): align GitHub identity CI coverage

* fix(codex): scope login shell isolation

* fix(agents): let managed gh profiles authenticate

* fix(agents): harden GitHub identity setup

* test(gateway): align method suffix counts

* fix(ui): serialize GitHub identity mutations

* fix(protocol): generate GitHub configure requests

* test(ui): restore timers after identity tests

* fix(ui): preserve GitHub identity mutation ownership

* fix(agents): preserve native GitHub CLI auth
2026-08-17 15:54:03 -07:00
openclaw-mantis[bot] 5fe5d26007 chore(i18n): refresh native locales (#125353)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-17 11:00:25 -07:00
Peter Steinberger 7170a6231a feat(agents): unify agent status into a durable progress_card (#125125)
* feat(agents): unify agent status into a durable progress_card

Replace the write-only update_plan to-do tool and the fragmented plan
rendering with one durable status artifact per session: progress_card
({plan?, markdown?}, replace-on-write, 8 KiB markdown / 50-step caps).
Cards persist in a lazy-additive session_progress_cards table in the
per-agent DB (no schema-version bump), broadcast progressCard.changed,
and render from the store with exactly one live placement per view
(session rail when visible, else the composer-adjacent bar); transcripts
collapse to one-line receipts, and the sidebar hovercard shows other
sessions' cards inline (markdown + <progress>, DOMPurify allowlist, no
iframes). The three stream-derived plan renderers and their dedup
heuristics are deleted.

Codex runs disable the native plan tool per thread
(tools.update_plan.enabled=false) and receive progress_card via the
dynamic-tool bridge; compaction restore now reinjects the card (steps +
bounded markdown). Card writes still emit the legacy plan stream event so
native apps and channels keep working until their per-platform
migrations. Policy names map update_plan -> progress_card; the shipped
tools.updatePlan=false kill switch is honored.

Net -277 production LOC; -480 test LOC.

* test(agents): regenerate Codex prompt snapshots for update_plan thread-config disable

* chore(protocol): allowlist progressCard.changed for native apps pending card migration

* fix(ci): repair progress card integration checks

* fix(codex): canonicalize native progress cards

* test(gateway): reconcile progress card method order

* test(codex): stabilize native approval fixture
2026-08-17 09:44:04 -07:00
Peter Steinberger 27b77a901e feat(ui): make session references first-class links with titled chips and hovercards (#125014)
* feat(sessions): teach session tools the Control UI link rule

Gate guidance on publicOrigin plus enabled Control UI, with exact literal-URL fallback after short-link misses.

* feat(ui): linkify session keys in chat markdown

Match agent session keys structurally in plain text and inline code, then delegate canonical chat navigation.

Allowlist data-session-key through markdown sanitization.

Pathfinder: keep internal Control UI route anchors in-app by removing target="_blank" and external-link rel attributes while preserving external link behavior.

* feat(ui): session hovercard + titled session chips backed by controlUi.sessionPreview

* fix(gateway): scope controlUi.sessionPreview to caller-visible sessions

Hover previews now apply the same createSessionListEntryFilter predicate as
sessions.list, so identity-bearing non-admin callers cannot preview-by-key
incognito rows or non-owner drafts the sidebar hides. Regression test proves
the viewer/admin split; pre-fix run leaked ok-status metadata.

* feat(sessions): carry the session-link rule in tool result envelopes

Deferred-description mode hides prose tool descriptions at decision time. Carry the shared Control UI session-link sentence in successful session lookup result envelopes so every tool mode sees the rule.

* fix(ui): upgrade session chips on appearance, not first pointer event

* fix(android): regenerate gateway protocol methods

* test(gateway): track session preview release train

* perf(ui): lazy-load session hovercard registration

* fix(ui): keep session hovercards off sidebar navigation

* fix(ui): cancel routed session-link navigation

* fix(sessions): advertise forced-literal ~key URLs so short-ID collisions cannot misroute

* test(sessions): update forced-literal guidance expectation

* fix(ui): collision-proof raw-key navigation and SPA-route internal session URLs

* perf(ui): preserve session route lazy boundary

* fix(ui): defer unseeded session-preview fetches to hover intent

* fix(sessions): hard-cap the model-visible session-link base
2026-08-17 04:05:02 -07: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
Peter Steinberger 55ce95fac8 feat(sessions): three-layer session ownership — agent attribution, assignable owner, participants (#125057)
* feat(sessions): stamp agent identity on spawned sessions and return spawn receipts

Agent-spawned sessions recorded the requesting session key as createdActor.id,
so the Control UI creator chip rendered an opaque key. Spawn producers now stamp
the canonical requester agent id; parent-authority validation moves to a new
trusted requesterSessionKey field. projectSessionActor enriches agent actors
with configured identity name/avatar at read time, and visible sessions_spawn
returns a sessionUrl + owner receipt with URL-first acknowledgement guidance.

* feat(sessions): assignable session ownership with owner facet and menus

GitHub-assignee-style ownership: sessions get a mutable owner (defaulting to
the immutable createdActor) stored in additive bare-nullable SQLite columns
with first-use lazy ensure. New operator.write sessions.assignOwner validates
targets, requires an identified caller, authorizes by session visibility, and
records assignedBy/assignedAt inside the write transaction. The sessions agent
tool gains assign_owner; the Control UI adds Assign-to-me/Assign-to menus in
sidebar rows and chat headers, renders the effective owner chip, and the
creator facet/filter now keys on effective owner. Sharing authority stays
anchored on createdActor.

* feat(sessions): record session participants and stack them in the owner chip

Records every distinct external prompter (human profile/channel sender, or a
requesting agent) per session in an additive session_participants table at the
turn-admission boundary — best-effort, deferred, never blocking the turn; the
session's own agent and viewers are never recorded, capped at 32 per session.
The session row projects a bounded participants list (owner excluded) plus a
total count with the same actor enrichment as owner/createdActor. The sidebar
chip becomes a pair-stack when others have prompted (owner front, one peeking
participant or +N behind), the chat header shows the full facepile, and an
authenticated involvingMe list filter adds an Involving-me sidebar predicate.
Participant projection is excluded from logical-session CAS equality so display
history never invalidates session writes.

* fix(sessions): identify built-in agent tool callers for owner assignment

The sessions tool's assign_owner dispatched through the in-process synthetic
client, which carries neither a signed agent-runtime identity nor a human
profile, so agent-initiated reassignment always failed with FORBIDDEN. The
tool now captures its trusted requester agent identity and carries it across
in-process dispatch as internal client state (never wire params); the handler
derives assignedBy as signed runtime identity, then trusted agent-tool caller,
then authenticated human. Live-verified end-to-end on a dev gateway.

* fix(ci): split oversized session modules and refresh prompt snapshots

Split the max-lines offenders at concept boundaries for session equality, tool overrides, and protocol owner schemas. Remove the redundant Number conversion from the node:sqlite participant count. Refresh prompt snapshots after drift from the sessions and sessions_spawn tool description updates.

* fix(ci): restore solo-mode chip suppression and conform new method descriptors

Solo-mode root cause: owner-assignment submenu options reused the permanent owner-chip custom element, so hidden menu avatars were counted as attribution chrome. Menus now use viewer avatars while gateway-gated owner chips remain exclusive to collaborative sessions.

Conform sessions.assignOwner to the 2026.8 descriptor and append-only advertised-method inventories, and regenerate the Swift and Kotlin protocol surfaces.

Keep historical v15/v14 fixtures frozen by stripping the new owner columns; the existing range already excludes the participant table. Replace the new raw SQLite schema probes with synchronous Kysely queries.

Clear max-lines by splitting the organizer host contract, pure agent-navigation projections, and ownership/filtering sidebar cases at their concept boundaries.

* fix(ci): integrate ownership series with latest main surfaces

Wire the sessions-page assign-owner action, merge capability imports, narrow the navigation export scope, and apply sessions-create formatting.

The owner-presence regression came from hidden assign-owner menu avatars emitting data-viewer-id, so owner and menu chrome now opt out of presence markers while real facepiles retain them.

* fix(sessions): scope the involving-me filter to profile-backed participants

Session participant history mixed channel-native sender ids with authenticated Gateway profile ids, so involving-me missed real sessions and could accept numeric collisions.

Record the actor_source namespace at each producer, carry it through the internal SQLite projection, and match authenticated viewers only against profile-backed human participants. Legacy NULL sources fail closed for filtering, while channel ids remain available for display.

* build(ui): raise startup budget baseline for session ownership surfaces

Ownership chips, assignment menus, and the participant stack add ~0.7 KiB
gzip to the startup path; CI compression landed just over the previous
baseline+tolerance. Hard cap (350 KiB) unchanged.

* refactor(sessions): drop raw NULL projection for the lazy actor_source column

The Kysely guardrail rejects typed raw sql snippets outside allowlisted
boundaries; select the lazily-ensured column only when present and let the
row projection treat its absence as unknown/legacy.

* build(ui): refresh combined startup baseline
2026-08-17 02:35:20 -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 3010318ba3 fix(android): isolate debug app installs (#125099) 2026-08-16 23:36:57 -07:00
Peter Steinberger 4b2aa935f3 fix(android): restore 11sp caption density (#125053) 2026-08-16 22:12:23 -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
Peter Steinberger 3d152540e3 fix(android): remove completed plan strikethrough (#124916) 2026-08-16 17:18:12 -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
openclaw-mantis[bot] 2d322d1ae7 chore(i18n): refresh native locales (#124089)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-15 01:54:57 -07:00
Jesse Merhi edb5adfbf5 Start new sessions with folder group defaults (#123276)
* feat(ui): add folder group session defaults

* fix(ci): align folder group contracts

* fix(protocol): refresh Android gateway methods

* fix(ui): reuse folder picker for group defaults

* fix(ui): harden session group defaults

* test(ui): align group defaults with current main

* test(ui): keep group catalog defaults path-free

* fix(ui): close folder group CI gaps

* fix(ui): satisfy folder group CI contracts

* fix(session-groups): enforce defaults safety

* test(gateway): keep group defaults in workspace

* fix(session-groups): enforce defaults participation

* fix(session-groups): close review authorization races

* fix(session-groups): canonicalize defaults authorization

---------

Co-authored-by: Jesse Merhi <jesse-merhi@users.noreply.github.com>
Co-authored-by: Jesse Merhi <openclaw@users.noreply.github.com>
2026-08-15 15:24:13 +10: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
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
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 ad6bc6d3ae fix(delivery): keep failed queue rows payload-free (#123642)
* refactor(delivery): collapse failed-row lifecycle

Replace the unshipped failure-operations platform with payload-free terminal receipts owned by existing queue boundaries. Keep bounded/permanent idempotency only for reusable or crash-ambiguous producers, move physical expiry to queue maintenance, and preserve migration and media-cleanup safety.\n\nTogether with #123410, production code is net negative by 11 lines; tests, docs, and generated protocol mirrors are accounted separately.

* fix(delivery): break state DB import cycle

* fix(delivery): classify SQLite boundary uses

* test(gateway): mark retained health fixture
2026-08-14 06:22:51 -07: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 7026cf2f21 fix(delivery): bound failed-row retention lifecycle (#123410) 2026-08-13 22:03:33 -07:00
Peter Steinberger edf1777ddb refactor(i18n): re-key native i18n artifacts to content-hash identity (v2) (#123347)
* refactor(i18n): re-key native i18n artifacts to content-hash identity (v2)

The native inventory stored a write-only 'line' field per entry, so any
unrelated edit above a string rewrote apps/.i18n/native-source.json
(~half of all commits touching it were pure line-number churn). Identity
was (surface, path, source), duplicating the same string per file
(5385 entries for 4187 unique pairs) and churning IDs on file moves.
Locale artifacts were positional arrays repeating full English source
text, so one inserted string rewrote diff spans in all 21 files.

v2 artifacts: inventory entries keyed by (surface, source) with merged
per-site {path, kind} lists and pure sha256 content-hash IDs; locale
files become id-keyed sorted translation maps. Existing translations
carry over by source match with a deterministic duplicate pick; the
sticky-ID reuse machinery and positional validation are deleted.
Everything under apps/.i18n plus generated platform locale artifacts is
marked linguist-generated. ci-changed-scope gains a one-time
owner-complete migration escape mirroring the control-ui precedent.

CLI surface (baseline/check/sync/verify) and the locale-refresh
workflow are unchanged.

* ci: register run-attempt-state test in its Vitest lane

Commit e04dfd26e2 added extensions/codex/src/app-server/run-attempt-state.test.ts
without a lane owner, so the full-suite ownership audit
(test/vitest-projects-config.test.ts) fails on main. Register it in the
attempt-light shard alongside its run-attempt siblings.
2026-08-13 19:39:32 -07:00
openclaw-mantis[bot] 9550d6f962 chore(i18n): refresh native locales (#123101)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-14 01:00:58 +08:00
Peter Steinberger 6c9916a48a feat(runners): publish atomic device runner inventory (#123094)
* feat(runners): publish atomic node inventory

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

* fix(runners): refresh topology on inventory removal

* fix(runners): resolve session host type overlap

* fix(protocol): document mobile runner inventory event

* fix(runners): project authoritative session host state

* docs(nodes): clarify prepared session host status
2026-08-13 04:54:26 -07:00
Peter Steinberger 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
Peter Steinberger cc2fc55f9b feat: portals — expose agent-run dev servers to the operator (#122536)
* feat(protocol): add portal methods and event

Bump the reviewed protocol owner-module count from 55 to 56.

* feat(gateway): add portal service and reverse proxy

* feat(agents): add portal tool

* fix(gateway): refine portal URL and proxy auth

* refactor(gateway): keep portal helper types private

* fix(gateway): declare portal transport service

* test(gateway): satisfy portal proxy lint

* test(gateway): narrow websocket payload types

* refactor(protocol): compact portal schema exports

* fix(gateway): export portal protocol types

* feat(ui): add portals page

* docs(gateway): add portals guide

* fix(gateway): dial portal targets via localhost dual-stack

Vite and other Node >=17 dev servers bind ::1 only for localhost, so a
fixed 127.0.0.1 dial 502s on the default path. Use hostname localhost
with family autoselection and rewrite Host to match.

* fix(gateway): type portal dual-stack connection

* fix: satisfy portal integration gates

* fix(gateway): isolate portal cookie jars per target

Cookies are hostname-scoped, not port-scoped, so the per-port origin
split alone let Gateway plugin-auth cookies reach agent-run targets.
Forward only cookies carrying this portal's own name prefix (stripped),
rewrite target Set-Cookie names to the prefixed form incl. the WS 101
handshake, and drop Domain attributes.

* fix(ui): detect unreachable portals behind proxied gateways

Probe the portal origin from the browser (no-cors, 4s timeout) and show
a recovery notice with the gateway-host URL instead of a dead iframe
when only the gateway port is exposed (Serve/Funnel/reverse proxy).
Docs: cookie isolation + reachability; zh-CN glossary entry.

* test(ui): satisfy portal reachability lint

* test(gateway): provide control UI request hosts

* chore(protocol): regenerate after rebase

* fix(gateway): namespace portal auth cookies by listener

* fix(gateway): scope portal token URLs to write-capable clients

The portal bearer token rides in the summary url/tokenQuery; portal.list
is operator.read and portal.changed fans out to read subscribers, so a
read-only client could harvest an openable URL. Make those fields
optional, redact them from read-scope list responses, and drop them from
every portal.changed broadcast; write/admin clients still receive them
and the UI refetches the list on change.

* docs(web): list the portals route

* fix(gateway): type portal open credentials

* docs(gateway): clarify portals PORT/PUBLIC_URL are agent-set

Opening a portal creates only the proxy listener; the agent sets PORT
and PUBLIC_URL in its own exec command, matching the portal tool
contract. Removes the implication of an automatic env handoff.

* chore(protocol): regenerate portal models

* style(gateway): format portal method-order assertions

Rebase union-merge left the portal.list assertion wrapped; oxfmt fits it
on one line.

* chore(plugin-sdk): refresh API baseline after rebase

* chore(plugin-sdk): refresh API baseline after rebase

* chore(protocol): refresh portal event order after rebase

* chore(plugin-sdk): refresh API baseline after rebase

* fix(gateway): pin portal referrer policy to no-referrer

The portal URL carries its bearer token in the query, and upstream
response headers are copied verbatim, so a target answering with
Referrer-Policy: unsafe-url could leak that URL to every third-party
origin it references. Force no-referrer after the copy and drop any
inbound Referer that still carries the token before forwarding.
2026-08-13 00:46:11 -07:00
Peter Steinberger 5e67beb02e feat(node-host): add negotiated private worker controls (#122939)
* feat(node-host): add private worker supervisor controls

* refactor(node-host): harden private worker controls

* refactor(node-host): simplify private worker controls

* docs(plan): track private worker controls

* fix(node-host): satisfy private control CI guards

* test(gateway): follow worker service construction boundary

* test(gateway): register private control release train

* fix(gateway): retain worker dialect across reapproval
2026-08-12 21:27:01 -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 cef071582e feat: let limited browsers request admin access (#121459)
* feat(gateway): add live device scope upgrades

* feat(ui): add limited-access upgrade flow

* fix(protocol): refresh Swift scope upgrade models

* perf(ui): lazy-load device scope upgrades

* fix(ci): complete scope upgrade generated surfaces

* perf(ui): lazy-load GitHub link hovercards

* fix(ui): keep admin repair guidance focusable

* fix(ui): gate and refresh scope upgrade banner

* refactor(ui): keep gateway client within line budget

* fix(ci): align rebased scope upgrade checks

* fix(ui): resolve scope upgrade in browser tests

* fix(gateway): honor refreshed scope upgrade deadline

* fix(gateway): honor refreshed scope upgrade deadline

* fix(gateway): coalesce scope upgrade waiters

* fix(ui): gate scope upgrade actions

* chore(plugin-sdk): refresh rebased API baseline

* fix(scope-upgrade): return canonical request ids

* fix(ui): preserve gateway event type binding

* fix(protocol): generate scope upgrade result models

* fix(ui): preserve scope upgrade recovery guidance

* chore(plugin-sdk): refresh rebased API baseline

* test(ui): avoid scope upgrade navigation race

* docs(control-ui): clarify scope upgrade approver

* test(gateway): align appended method counts

* chore(plugin-sdk): refresh rebased API baseline

* refactor(ui): keep place picker within line budget

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* fix(gateway): preserve scope-upgrade browser origin
2026-08-12 18:32:00 -07:00
Josh Lehman bb5ef9872a fix: resume sessions interrupted by gateway restarts (#122644)
* fix: resume sessions interrupted by gateway restarts

* fix: make session recovery retry-safe

* refactor: isolate restart session recovery

* test: track recovery temp directories

* fix: inherit recovery state under lifecycle lock

* fix: surface rejected recovery continuation

* fix: fence recovery continuation authority

* chore: regenerate plugin sdk api baselines

* test(gateway): track sessions.recover release train
2026-08-12 16:18:58 -07:00
openclaw-mantis[bot] d17264f5fd chore(i18n): refresh native locales (#122688)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-13 02:45:50 +08: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 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
openclaw-mantis[bot] 4bd56a316b chore(i18n): refresh native locales (#122355)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-11 20:00:14 -07: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
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
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 73ae583263 refactor(tasks): simplify subagent activity tracking (#122198)
* refactor(tasks): simplify subagent activity tracking

* fix(android): single-source subagent activity expiry clock

* chore(i18n): refresh native source baseline
2026-08-11 12:30:49 -07:00
openclaw-mantis[bot] 6e9e569243 chore(i18n): refresh native locales (#121939)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-11 09:10:15 -07:00
Peter Steinberger ea06d72e85 feat(secrets): manage team secrets in Control UI (#121724)
* feat(secrets): add gateway store settings

* perf(control-ui): trim secrets startup copy

* perf(control-ui): reduce secrets startup payload

* fix(secrets): harden store mutation refresh

* perf(control-ui): meet secrets startup budget

* test(control-ui): update secrets navigation copy

* fix(ui): pluralize secret-detection count and drop duplicated dialog hint

* chore(protocol): regenerate gateway clients and SDK baseline after rebase

* fix(gateway): merge secrets store methods after project RPCs in advertised order

* chore: leave changelog to release generation

* test(gateway): retain desktop launch train coverage
2026-08-11 07:20:38 +00:00