* fix(exec-approvals): escape control characters in display sanitizers
* docs(changelog): add exec approval control-char display sanitizer entry
* fix(exec-approvals): redact before escape, cover U+2028/U+2029 in display sanitizers
* fix(exec-approvals): strip invisibles before redaction and align forwarder test
* fix(exec-approvals): cover Zs bypass and preserve multi-line context on obfuscated secrets
* fix(exec-approvals): compare redaction outputs by content, not length
* fix(exec-approvals): suppress raw command on bypass; cover non-ASCII Zs in macOS sanitizer
* fix(exec-approvals): use position-bitmap bypass detection and bound input size
* style(exec-approvals): satisfy oxlint no-new-array-single-argument and SwiftFormat
* fix(exec-approvals): iterate by code point and redact before truncating
* feat(gateway): make chat history max chars configurable
* fix(gateway): address review feedback
* docs(changelog): note configurable chat history limits
* feat(macos): add "Trigger Talk Mode" option to Voice Wake settings
When enabled, detecting a wake phrase activates Talk Mode (full voice
conversation: STT -> LLM -> TTS playback) instead of sending a text
message to the chat. Enables hands-free voice assistant UX.
Implementation:
- Constants.swift: new `openclaw.voiceWakeTriggersTalkMode` defaults key
- AppState.swift: new property with UserDefaults persistence + runtime
refresh on change so the toggle takes effect immediately
- VoiceWakeSettings.swift: "Trigger Talk Mode" toggle under Voice Wake,
disabled when Voice Wake is off
- VoiceWakeRuntime.swift: `beginCapture` checks `triggersTalkMode` and
activates Talk Mode directly, skipping the capture/overlay/forward
flow. Pauses the wake listener via `pauseForPushToTalk()` to prevent
two audio pipelines competing on the mic.
- TalkModeController.swift: resumes voice wake listener when Talk Mode
exits by calling `VoiceWakeRuntime.refresh`, mirroring PTT teardown.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review feedback
- TalkModeController: move VoiceWakeRuntime.refresh() after
TalkModeRuntime.setEnabled(false) completes, preventing mic
contention race where wake listener restarts before Talk Mode
finishes tearing down its audio engine (P1)
- VoiceWakeRuntime: remove redundant haltRecognitionPipeline()
before pauseForPushToTalk() which already calls it via stop() (P2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: resume wake listener based on enabled state, not toggle value
Check swabbleEnabled instead of voiceWakeTriggersTalkMode when deciding
whether to resume the wake listener after Talk Mode exits. This ensures
the paused listener resumes even if the user toggled "Trigger Talk Mode"
off during an active session. (P2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: play trigger chime on Talk Mode activation + send chime before TTS
- VoiceWakeRuntime: play the configured trigger chime in the
triggersTalkMode branch before pausing the wake listener. The early
return was skipping the chime that plays in the normal capture flow.
- TalkModeRuntime: play the Voice Wake "send" chime before TTS playback
starts, giving audible feedback that the AI is about to respond. Talk
Mode never used this chime despite it being configurable in settings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: move send chime to transcript finalization (on send, not on reply)
The send chime now plays when the user's speech is finalized and about
to be sent to the AI, not when the TTS response starts. This matches
the semantics of "send sound" -- it confirms your input was captured.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>