* fix(state): name the refusing install in schema and exec-approval refusals
The newer-schema refusal told operators not to downgrade and to upgrade
OpenClaw, neither of which is actionable when two builds share one release
version string. It now names the install root that refused, both schema
versions, and warns that a linked source checkout reports its git HEAD even
when its built dist is older.
The exec-approvals gate told operators to run `openclaw doctor --fix` without
naming the state directory, so a bare invocation repaired the default root
while the scoped install stayed blocked. Both the TypeScript gate and its
Swift sibling now scope the command to the blocked state directory.
Refs #115008
* fix(gateway): name the refusing install in the startup schema refusal
* fix(mac): keep the exec-approvals gate message buildable on iOS
* fix(exec-approvals): shell-quote the state directory in the repair command
* fix(exec-approvals): state the repair directory in prose so every shell can follow it
* fix(ios): forgetting a paired gateway does nothing
Confirming "Forget Gateway" in Settings never removed the gateway. The
confirmation dialog derived its isPresented binding from the payload it
later needed and cleared that payload on dismissal, so by the time the
destructive button's action ran, forgetPendingGateway() found nil at its
opening guard and returned without calling forgetGateway(stableID:).
Nothing was logged either, because the failure branch was never reached.
Hand the entry to the action through the dialog's presenting: parameter
instead of reading it back from view state, and take it as a parameter
rather than looking it up. Everything downstream is unchanged; it simply
runs now.
* Shorten the dialog comment to one line
The three-line version repeated in prose what the next line already shows.
What is worth keeping is the trap: someone simplifying this back to reading
the entry from view state reintroduces the bug.
* fix(ios): harden forget gateway payload handoff
Co-authored-by: Cihat Gündüz <anthropic@fline.dev>
* test(ios): run gateway settings source guards
* test(ios): allow forget dialog state cleanup
* test(ci): provide rg in PR review fixture
---------
Co-authored-by: Cihat Gündüz <anthropic@fline.dev>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(android): honor reduced motion reactively in mascot and talk waveform
OpenClawMascot and TalkWaveform read Settings.Global.ANIMATOR_DURATION_SCALE
with a one-shot remember(context){} that never reacts when the user toggles
"Remove animations" while the screen stays mounted. Extract the reactive
ContentObserver read that ChatWorkingIndicator already used into a shared
ui/SystemAnimations.kt helper and use it in all three surfaces. Follow-up to
#112245 (Wear avatar).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(android): resync the native i18n baseline after the animation refactor
The native-i18n check failed with "native app i18n inventory drift detected".
apps/.i18n/native-source.json pins a line number for each of its 5290 entries,
and 23 of them point into ChatWorkingIndicator.kt. Extracting the shared
rememberSystemAnimationsEnabled helper shortened that file by 26 lines, so every
recorded string below the edit moved and the inventory no longer matched.
Regenerated with `pnpm native:i18n:baseline`, as the failure message instructs.
The diff is 23 line-number updates (384 -> 358 and so on); no `source` value
changes, because this refactor adds and removes no user-facing strings.
`node --import tsx scripts/native-app-i18n.ts verify` now passes.
* fix(android): reuse Compose motion scale
Co-authored-by: Masato Hoshino <g515hoshino@gmail.com>
* fix(android): keep system motion scale authoritative
Co-authored-by: Masato Hoshino <g515hoshino@gmail.com>
* test(cli): raise root help deadlock guard
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
CPP was added to blockedEverywhereKeys in #112560, but its Autoconf twin
CXXCPP was not. Autoconf defines the preprocessor selectors as a pair —
AC_PROG_CPP sets CPP, AC_PROG_CXXCPP sets CXXCPP — and this list already
carries the compiler pair CC/CXX for the same reason.
Measured on current main: CC, CXX and CPP all report dangerous, while
CXXCPP lands in no category at all (blockedPrefixes is only DYLD_, LD_
and BASH_FUNC_, so no prefix rule covers it either).
* perf(gateway): warm handler families and cache identity avatars
* chore: drop changelog edit (release-generation-owned)
* fix(ci): regenerate protocol models, satisfy knip, absorb sidecar drift
- commit regenerated Swift GatewayModels for the additive cron.list param
- split the avatar data-url cache into assistant-avatar-cache.ts so its
factory has a production consumer (knip production scan)
- de-export prewarm family names/type; test now drives fake families only
- postReadySidecarCount 2 -> 3: main added a post-ready sidecar in parallel
* feat(ui): offer normally-absent core files instead of flagging them missing
The Agents core-files editor rendered every unwritten workspace file as a
permanently-badged MISSING tab, so an ordinary workspace looked broken. Absence
is only a fault for required files; SOUL.md, USER.md, and MEMORY.md are normal
to be absent until written.
agents.files.list now marks those entries expectedAbsent, and the editor keeps
them out of the tab strip behind an add picker that creates the file on save.
A missing AGENTS.md still shows as a fault.
* chore(protocol): regenerate Swift gateway models for expectedAbsent
* fix(gateway): carry expectedAbsent through agents.files.get
Clients merge the get response over the listed entry, so a missing-file get
that dropped the flag made a file picked from the add picker re-render as a
MISSING fault. Caught by live Control UI proof, not by the unit tests.
Add agent ownership to observer digests, scope global observer delivery and reconciliation, and centralize application session selection across navigation and reconnects.
Co-authored-by: xbrxr03 <abrarhabib03@gmail.com>
* fix(ui): keep stable chat rows in insertion order and only sort live tool/stream items by timestamp
* fix(ui): keep live rows within current turn
* fix(ui): keep current work above queued turns
* fix(ui): keep streamed replies above queued turns
* fix(ui): preserve reconnecting run order
* fix(ui): preserve causal terminal ordering
* fix(ui): bound replay rows to owning turns
* fix(ui): keep question summaries in owning turns
* fix(ui): scope question run ownership to session
* fix(ui): restore reconnecting chat run identity
* fix(ui): remove unused chatItemTimestamp import in chat-thread-build.ts
* fix(ui): correlate question summaries with agent runs
Co-authored-by: Peter Lee <li.xialong@xydigit.com>
* chore(i18n): refresh native source baseline
Co-authored-by: Peter Lee <li.xialong@xydigit.com>
* fix(ui): remove unused chatItemTimestamp export and split tool-stream test file
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ui): durable session board face and dashboards index
Board face lived only in client-side boardSessionViews, capped at 50 entries,
so the preference never followed the user to another device, evicted as
sessions accumulated, and could not be seen as a set.
Persist it as SessionEntry.boardFace, which rides the existing entry_json blob
and so needs no SQLite schema change or version bump. Expose it on the session
list row and add it to the sessions.patch write-scope allowlist alongside label,
pinned, and archived: setting your own view preference is user-level chat
organization, not policy. Unknown patch fields still fail closed to
operator.admin.
Generic navigation now reads the stored face, so the sidebar and session list
open a thread on the face you left it on. boardSessionViews keeps only
activeTabId and reopenDockByTab, which are genuinely per-device.
Add /dashboards listing threads whose preferred face is dashboard. Filtering
runs server-side in filterSessionEntries before pagination, because the client
holds only a capped page and a client-side filter would silently omit
dashboards.
* test(protocol): assert the pre-rename face param is rejected
The gateway-protocol validator test still passed the pre-rename 'face' key,
which the closed schema rejects. Use boardFace, and pin the old name as a
negative case so it cannot silently return.
* chore(protocol): regenerate Swift bindings and docs map for boardFace
Adding boardFace to the sessions schema changes two committed generated
artifacts: the Swift gateway models (pnpm protocol:gen:swift) and the docs map
(pnpm docs:map:gen), which now lists the dashboards index section.
Two geometry defects found while stress-testing the companion.
Positioning mixed coordinate spaces. `work_area()` is physical pixels of the
monitor Quick Chat is moving to, but `inner_size()` is physical pixels at the
scale of the monitor it is on now. A 640pt window on a 2x display reports
1280px, so invoking it on a 1x 1920px display centred using 1280 and landed it
320px left of centre. Re-express the window size in the target monitor's scale
first; equal scales give a ratio of 1, so single-monitor setups are untouched
to the pixel.
Widget growth resized without re-anchoring. `quickchat_set_expanded` resizes
and then repositions, but the widget path only resized, keeping the old top
edge. Growing from 360pt to 440pt in a 440pt work area left the bottom 80pt
off-screen and unreachable. `resize_window_if_needed` now reports whether it
actually changed the height so the caller re-anchors only on a real resize,
rather than moving the window when nothing changed.