* feat(chat-ui): tool-diff follow-ups from PR review
Persist bounded applied-diff details in the transcript cache so cold
opens keep inline diffs; parse apply_patch envelopes into per-file diff
sections with stats; thread tool-result isError so failed calls suppress
proposal diffs and read as failures; replace per-line truncation with
horizontal scrolling for long diff lines.
* chore(i18n): sync native inventory for tool-diff follow-ups
* fix(chat-ui): render move-only patches and drop zero diffstats
* feat(chat-ui): inline diffs for edit and write tool rows
Port the web Control UI's kind-aware diff rendering to the shared SwiftUI
tool rows: persisted details.diff parses into numbered add/del/ctx lines,
edit/write/text-editor args fall back to a local capped line diff, the
collapsed row shows a +N/-M diffstat, and the expanded surface renders the
diff with the result summary or error diagnostic beneath it.
* fix(chat-ui): preserve tool-result details through message reconciliation
Canonical-adoption rebuilds enumerated message fields and silently dropped
details, so inline diffs vanished after cache-warm history reconciliation
(every app relaunch). Carry details through adoptingCanonicalMessage and
messageWithTimestampIfNeeded and lock it in with regression tests.
* chore(i18n): sync native inventory for tool diff rows
* fix(chat-ui): render diffstat and line numbers as verbatim text
* fix(chat-ui): use theme tokens for diff colors
* refactor(chat-ui): drop test-only parseDetailsDiff wrapper
* fix(chat-ui): address diff-row review findings
Interpret details.diff only for edit/write/patch tool names so plugin
detail schemas stay opaque; number write previews from line 1 with an
80-line cap and a subtractive char budget while keeping the exact stat;
bound per-line rendering to 2000 unicode scalars; announce add/del rows
and collapsed skip markers to assistive tech.
* chore(i18n): sync native artifacts for diff-row labels
* fix(chat-ui): announce diff line numbers to assistive tech
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(macos): add Quick Chat power features
* docs(macos): document Quick Chat power features
* chore(i18n): refresh native locale artifacts for Quick Chat power features
* fix(macos): update DEBUG test helper sendProvider to the reasoning-threaded arity
Add optional per-job pacing bounds across the cron API, CLI, tool schema, public output, and SQLite job envelope, requiring at least one bound. Allow only the currently running paced job to record a one-shot next_check proposal and carry it through isolated-run completion.
After successful runs, clamp the proposal to the job bounds and persist an exact one-shot slot marker so maintenance preserves only that timestamp. Clear the marker on runs, edits, and schedule normalization; preserve existing no-proposal, skip, timeout, and error scheduling behavior.
* feat(webchat): reply-to a message with hydrated reply context
Control UI replies now carry the target transcript id as replyToId on
chat.send. The Gateway resolves the replied-to message from session
history and hydrates the channel-agnostic ReplyToId/ReplyToBody/
ReplyToSender envelope fields, so agents receive reply_to_id,
has_reply_context, and the untrusted reply-target block exactly like
Discord replies (mirrors #90263). Reply targets without a persisted
transcript id keep the inline-quote fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(webchat): keep non-reply chat.send dispatch ordering and satisfy CI gates
* docs(webchat): match reply-context doc to webchat conversation-info policy
* fix(webchat): hydrate reply bodies from display-visible content only
---------
Co-authored-by: openclaw-clawsweeper[bot] <openclaw-clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
* fix(macos): harden Quick Chat capture — drop AX-role text, restore bar after area capture, guard overlays post-permission
* chore(i18n): refresh native inventory line numbers after capture hardening
* refactor(chat-ui): render tool calls as flat expandable rows
Replace the nested ToolCallCard/ToolResultCard/pending-tool cards inside
assistant bubbles with flat rows that mirror the web Control UI: chevron +
kind icon + title + one-line mono detail, result folded into the row and
revealed on tap (Show all for long outputs). Tool-only messages no longer
render bubble chrome.
* chore(i18n): sync native inventory + translations for tool-row strings
* feat(macos): add Quick Chat capture context
* docs(macos): describe Quick Chat capture context
* fix(macos): bound the AX text walk with deadline, cancellation, and per-message timeout; keep distinct repeated text
* fix(macos): ancestry-scoped echo suppression and adapter-wide AX timeouts
* fix(macos): hard outer timeout abandons a hung AX walk
* fix(macos): truly abandon hung AX walks; hide the bar synchronously before area selection
* chore(i18n): refresh native locale artifacts for Quick Chat capture strings
* feat(ui): custom session icons with agent-drawable SVG and nav-parity pinned rows
* wip: slice-2 interleaved sidebar zone (pre-review)
* fix(ui): trim icon exports, bypass protocol barrel in startup bundle, regen Swift protocol client
* fix(ui): preserve unknown-agent zone entries on writes, prune entries on any unpin
* fix(ui): reset keeps unloaded session slots; enforce SVG byte cap on canonical form
* fix(ui): archiving a pinned session retires its sidebar zone slot
* fix(ui): persist drag-pinned zone slot only after the pin patch lands
* fix(ui): recompute drop insertion against freshest zone order on pin ack
* fix(ui): consume self-drop events before the zone bailout
* docs(ui): note the non-sidebar unpin pruning contract on pruneSidebarSessionEntry
* refactor(ui): extract pure session-tree projection; drop unused zone type export
* 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(mac): show native notification permission in dashboard Notifications settings
The Mac app's embedded Control UI showed the web-push section as
Unsupported/Not subscribed even though the app delivers notifications
natively. The dashboard now installs an openclawNotifications WebKit
bridge (status / request-permission / send-test) and the Notifications
settings page renders native permission state with Enable, Open System
Settings, and Send test actions when hosted in the Mac app. Browsers
keep the existing web-push UI. The notifications section renderer moved
out of the oversized config view module.
* fix(ui): break config view import cycle and satisfy lint/deadcode/i18n gates
Move the notifications section props to a leaf contract (no view.ts
type import), unexport the status-event constant, use bracket access
for the injected dunder global, default-case the status switch, and
refresh the native i18n inventory line offset.