SQLite WAL mode requires shared-memory (mmap/shm) which is unavailable
on many network and virtual shared filesystems (NFS, SMB/CIFS,
UTM/VirtioFS, VirtualBox shared folders), causing xShmMap I/O errors
at startup.
Add `sqlite_journal_mode` config option under `[memory]` that accepts
"wal" (default) or "delete". When set to "delete", SQLite uses the
legacy DELETE journal mode and disables mmap, allowing ZeroClaw to run
with workspaces on shared/network filesystems.
Usage:
[memory]
sqlite_journal_mode = "delete"
Changes:
- config/schema.rs: Add sqlite_journal_mode field to MemoryConfig
- memory/sqlite.rs: Add with_options() supporting journal mode selection
- memory/mod.rs: Pass journal_mode from config to SqliteMemory
- onboard/wizard.rs: Include new field in default MemoryConfig
- Register 4 new tools (ManageAuthProfileTool, CheckProviderQuotaTool,
SwitchProviderTool, EstimateQuotaCostTool) in all_tools_with_runtime
- SwitchProviderTool now loads config from disk and calls save() to
persist default_provider/default_model to config.toml
- Inject Provider & Budget Context section into system prompt when
Config is available
- Remove emoji from tool output for cleaner parsing
- Replace format! push_str with std::fmt::Write for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire QuotaMetadata into ChatResponse for all provider implementations,
enabling quota tracking data to flow from API responses through the
agent loop to quota monitoring tools.
Depends on: circuit breaker (#1842) + quota monitoring (#1904)
Made-with: Cursor
WebDriver's execute() wraps the script as a function body. The snapshot
script used an IIFE without a top-level return, so the IIFE's return
value was discarded and the WebDriver function returned undefined (null).
All other execute() calls in the file (scroll, scrollIntoView, click)
correctly use explicit return statements.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MIME strings like 'audio/webm; codecs=opus' were incorrectly matched by
the 'opus' branch (contains-check) before reaching the 'webm' branch,
returning 'voice.opus' instead of 'voice.webm'. This could cause the
Groq Whisper API to reject or misidentify the file format.
Fix: split on ';' to extract only the base MIME type, then match
exhaustively. Also add 'audio/x-wav' as a wav alias.
Adds a regression test: audio_mime_to_filename('audio/webm; codecs=opus')
must return 'voice.webm'.
Reported by CodeRabbit in PR review.
(cherry picked from commit 84861c727a)
Audio/voice messages on the WhatsApp Web channel were silently dropped
because `text_content()` returns an empty string for non-text messages
and no transcription path existed (unlike the Telegram channel which
already uses `transcription::transcribe_audio()`).
Changes:
- **Cargo.toml**: Move `qrcode` and all `wa-rs-*` crates out of the
`[target.'cfg(any(linux|macos|windows))'.dependencies]` section into
the unconditional `[dependencies]` section. All affected crates are
`optional = true`, so they add no compile cost unless
`--features whatsapp-web` is active. The previous placement caused
Cargo to exclude them when targeting `android` (target_os = "android"
does not match the cfg predicate), producing E0433 unresolved-crate
errors for every wa-rs import in `whatsapp_web.rs` and
`whatsapp_storage.rs` on Android cross-compilation.
- **whatsapp_web.rs**:
- Add `transcription: Option<TranscriptionConfig>` field.
- Add `with_transcription()` builder (mirrors `TelegramChannel`).
- Add `audio_mime_to_filename()` helper mapping WhatsApp MIME types
(e.g. `audio/ogg; codecs=opus`) to filenames the Groq Whisper API
accepts.
- Extend `Event::Message` handler: when text is empty, check
`msg.audio_message`; download and decrypt audio via
`client.download(audio_msg.as_ref())` (`.as_ref()` required because
prost boxes nested proto fields as `Box<AudioMessage>`, which does
not itself implement `Downloadable`); forward decrypted bytes to
`transcription::transcribe_audio()`.
- Add three unit tests: builder enable/disable guard and MIME mapping.
- **mod.rs**: Chain `.with_transcription(config.transcription.clone())`
onto `WhatsAppWebChannel::new(...)` in the `"web"` factory branch so
transcription is active whenever the global `[transcription]` section
is enabled.
Activation: set `[transcription] enabled = true` and export
`GROQ_API_KEY` in the environment.
(cherry picked from commit 325241aeb6)
Register /new, /model, and /models commands with Telegram's Bot API
on startup so they appear in the command menu for users. Registration
is non-fatal — if the API call fails, a warning is logged and the
bot continues listening normally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split the catch-all `_` match arm on the deleteMessage result into
separate `Ok(r)` and `Err(e)` arms so that HTTP status codes and
network errors are logged individually. The response body is not
logged (security policy).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When editMessageText returns 'message is not modified', the draft
already contains the correct content from update_draft. Detect this
Telegram API response and treat it as success rather than falling
through to the delete+send fallback, which would create a visible
duplicate message.
Also guard the final fallback: only send a new message after
successfully deleting the draft. If deleteMessage fails, the draft
still shows the response text, so sending would create a duplicate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add support for Chrome, Firefox, and Edge browsers to the browser_open tool,
which previously only supported Brave. Users can now specify the browser
via the browser_open config option.
Changes:
- Add browser_open config field: "disable" | "brave" | "chrome" | "firefox" | "edge" | "default"
- Implement platform-specific launch commands for Chrome, Firefox, and Edge
- When set to "disable", only the browser automation tool is registered, not the browser_open tool
- Update tool descriptions and error messages to reflect browser selection
Co-Authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>