* feat(clients): adopt model controls, session management, and keyboard history recall on iOS
* chore(i18n): translate iOS adoption strings and realign locale artifacts
* refactor(clients): drop test-only group response inits flagged by the shared dead-code intersect
* chore: retrigger ci
* chore(i18n): resync inventory after voice-unification main rebase
* chore(i18n): resync inventory after main rebase
* chore(i18n): resync inventory after main rebase
* chore(i18n): realign locale artifacts after main rebase
* fix(clients): preserve native chat API and locale copy
* fix(ios): retain public session DTO initializers
* refactor(clients): delete test-only session DTO inits flagged dead instead of suppressing
Live-append voice transcripts into the agent session and persist a per-agent SQLite call record across relay and client transcript paths.
Add run-scoped spoken confirmation for high-impact actions, mutation digests, bootstrap-context injection, talk.client.transcript and talk.client.close protocol methods, and Control UI adoption. This adds zero new configuration.
Co-authored-by: Clifton King <clifton@users.noreply.github.com>
The Nodes & Devices screen showed pending pairing counts but pointed users
at a CLI command on the gateway host. An already-trusted phone can now act:
approve/reject pending requests (gated on hello methods plus the
operator.pairing or admin scope, mirroring the gateway's second
authorization gate for requested roles/scopes) and remove paired devices
(admin-gated, matching the gateway's removal contract). The confirm dialog
is the consent surface: it renders the complete requested roles/scopes
unbounded and scrollable with control/bidi sanitization, keyed to the
gateway identity so a pending confirmation cannot survive a gateway switch.
Mutations capture the gateway scope at claim time, require the exact write
acknowledgement plus a canonical device.pair.list readback before claiming
success, surface definitive gateway denials verbatim, and take over the
refresh generation with the verified snapshot so stale refreshes cannot
republish pre-mutation state. Bootstrap-requested scopes stay on the
canonical limited profile; extending it with operator.pairing is a
deliberate gateway-side product decision left open.
Publicly-CA-valid gateway hosts (e.g. Tailscale Funnel with Let's Encrypt)
no longer force fingerprint pinning: a real platform-trust handshake with
HTTPS hostname verification and SNI connects without prompting, ending the
changed-pin re-prompt on every cert renewal. Candidate gating excludes
.local, IP literals, and single-label hosts so LAN endpoints keep pinning.
Existing pins stay authoritative: a valid public chain never silently clears
a stored pin - the changed-pin prompt gains an explicit one-time 'Use system
trust' switch instead. Discovered endpoints still require a stored pin for
reconnect eligibility (discovery data is attacker-controllable). When the
TLS probe cannot capture a certificate, the trust dialog now accepts an
out-of-band SHA-256 fingerprint paste (sha256:/colon tolerant, 64-hex
validated) which is then enforced on connect. Malformed stored pins now
fail closed instead of silently degrading. Both probe attempts share one
connect+handshake budget so stalled hosts cannot double the probe latency.
* feat(android): accept audio and document attachments via share sheet and composer
Extends Android share-sheet intake beyond text/images: audio plus a curated
document set (PDF, OOXML, CSV, Markdown) with code-side mime enforcement,
provider-type revalidation while the URI grant is live, bounded streaming
reads, per-kind size budgets aligned with the outbox command limit, and a
document/audio picker in the composer. Share parsing moves off the main
thread behind a bounded slot queue; draft-not-autosend and drop/failure
reporting behavior unchanged.
* test(android): cover audio and document share intake
* test(android): align composer draft test with attachment kinds
* chore(android): refresh native i18n inventory
* chore(android): rebuild native locale artifacts for inventory order
* fix(android): stop push-to-talk from dropping speech after mid-hold pauses
The SpeechRecognizer PTT path ran one session with a 2.5s silence window,
kept only the last final, and never restarted during a hold - speech after
a mid-hold pause was silently dropped. Capture now uses an ordered rung
ladder: API 33+ app-owned AudioRecord piped via EXTRA_AUDIO_SOURCE with
EXTRA_SEGMENTED_SESSION, then silence-keyed segmented sessions, then a
restart-during-hold single-session fallback for older devices; failed holds
only descend. Final segments accumulate and merge locale-aware (no ASCII
separators after CJK/Arabic punctuation), with a trailing live partial
deduped against the last final. Release waits one bounded grace for the
terminal recognizer callback so late finals are not truncated; rapid
re-press drains the prior release instead of cancelling it; cancellation
mid-release still tears capture down, resumes realtime talk, and resolves
the stop payload. Raw-rung release also stops the recognizer since
EXTRA_AUDIO_SOURCE is optional and a service may run its own microphone.
Input level meters from our PCM stream in the raw rung where onRmsChanged
never fires.
* fix(android): API-33 recognizer extras behind RequiresApi and inventory refresh
Min-SDK lint (InlinedApi) flagged the API 33 RecognizerIntent extras; they
now live in RequiresApi(TIRAMISU) helpers behind explicit SDK gates, and
the native i18n inventory is refreshed for the moved lines.
* chore(android): rebuild native locale artifacts for inventory order
* feat(apple): copy or save rendered widgets as images from the chat transcript
* chore(i18n): regenerate native locale artifacts for widget export strings
* chore(i18n): resync native locale artifacts after main i18n advance
* fix(android): move device identity key into encrypted storage
Migrates the plaintext openclaw/identity/device.json Ed25519 signing key
into EncryptedSharedPreferences with a verified one-time import (synchronous
write, readback check, then plaintext delete) and no fallback reader.
Persistence failures now fail closed instead of silently continuing.
Documents why the cleartext NSC base-config must stay: NSC domain rules
cannot express user-selected RFC1918 gateway endpoints and OkHttp enforces
the policy, so GatewayHostSecurity remains the cleartext gate.
* fix(android): suppress UseKtx on synchronous identity commit
KTX edit(commit = true) discards commit's Boolean; the identity migration
fails closed on that result.
* feat(android): copy or save rendered widgets as images from the chat transcript
* chore(i18n): sync native app i18n inventory for widget export strings
* chore(i18n): regenerate native locale artifacts for widget export strings
* feat(chat): rewind and fork a session from a message bubble
* docs(web): document chat rewind and fork bubble actions
* fix(chat): lint cleanups, protocol regen, and test splits for rewind/fork CI
* fix(ui): fit chat rewind disabled styles inside the startup CSS budget
* feat(clients): gateway session groups, batch operations, session inspector, and new-session options
* chore(i18n): translate session management strings and regenerate catalogs
* chore(i18n): complete translation artifacts for session management strings
* fix(clients): fail closed when a transport cannot honor agent or base-ref session options
* fix(clients): authoritative agent identity for new-session parents and revision-keyed sidebar group refresh
* fix(clients): refetch session groups on remote catalog events and correct Swedish repository-root translation
* fix(clients): resilient new-session popover, revision-keyed group manager, and popover string translations
* fix(clients): scope reset fallback to plain new-session requests and revision-key the inspector groups
* fix(clients): reject empty agent catalogs and refresh sessions on remote group events
* refactor(clients): drop unused agents fetch wrapper and runtime memberwise init flagged by periphery
* chore(i18n): realign locale artifacts after rebase onto model-controls main
* chore(clients): split session creation into the session-actions extension and realign i18n after ask-user main rebase
* chore(i18n): resync derived artifacts after question-card main rebase
* feat(clients): provider-grouped model picker with fast, verbosity, and inherited-thinking controls
* chore(i18n): translate model-control strings and regenerate platform catalogs
* feat(clients): inherited-default restore for fast mode and verbosity controls
* fix(clients): preserve control state in thinking snapshots and persist inherited verbosity
* chore(i18n): resync native inventory after rebase onto message-reader main
* chore(i18n): realign locale artifacts after rebase onto message-reader main
* refactor(clients): drop superseded fast-mode accessors and picker convenience flagged by periphery
* chore(i18n): resync inventory line refs after dead-code removal
* feat(chat): replay the active plan snapshot to reconnecting clients
Plan checklists were live-event-only: a client that connected or
refreshed mid-run saw nothing until the next update_plan call, and iOS
adoptRun actively wiped retained plan state on reconnect. The gateway
now records the latest normalized plan snapshot per run beside the
existing chat text buffers and replays it through the established
chat.history/chat.startup inFlightRun recovery contract (runId > plan >
text under the payload budget; a budget-dropped plan is sent as an
explicit empty snapshot so clients never preserve a stale checklist).
Web, iOS, and Android seed and reconcile the snapshot through one shared
contract riding each client's existing inFlightRun adoption gate: present
plan replaces, absent plan preserves (legacy gateway), a different or
terminated run clears, and stale history responses cannot clobber a newer
live plan. Verified live gap: openclaw/openclaw#108675 comment 5001475066.
* fix(ios): extract run-snapshot adoption below the file-length lint ceiling
ChatViewModel.swift crossed SwiftLint's 1500-line limit; the in-flight
snapshot adoption and plan reconciliation move to a focused extension.
Also refresh the native i18n inventory for shifted line anchors.
* fixup: regenerate i18n anchors and test formatting after rebase
* ci: retrigger after wedged queued runs
* fix(ui): use Object.hasOwn for the plan field presence probe
* fix(ios): annotate the plan-snapshot fixture init for periphery
* feat(clients): session detail parity — row actions, unread auto-clear, and subagent tree in the native chat sidebar
* fix(clients): overlay pending explicit unread values over stale session snapshots
* chore(i18n): translate new session action strings and regenerate Apple/Android catalogs
* chore(clients): delete dead pin helper and worktree memberwise init
* feat(mac): add Quick Chat floating composer with global shortcut
- Spotlight-style non-activating key panel on Option+Space (KeyboardShortcuts
3.0.1 exact-pinned, recorder row in Settings -> General) plus a menu bar item
- shows the main-session agent identity via agent.identity.get with a
'main session' chip; Return sends via chat.send reusing the idempotency key
on ambiguous retries; Cmd+Return also opens full chat; Shift+Return newline
- targeted permission strip (notifications/accessibility/screen recording)
with a scoped 1s status poll while visible; grant flow keeps the bar alive
and avoids PermissionMonitor's AppleScript probe
- extract shared ChatSendStatus acceptance mapping; TalkModeRuntime adopts it
- rename anchored-panel 'quick chat' wording to 'compact chat panel'; docs
* chore(mac): regenerate docs map and native i18n inventory for Quick Chat
* ci: retrigger checks after stalled push event
* chore(mac): refresh native locale artifacts for Quick Chat strings
* chore(android): regenerate locale strings for refreshed shared translations