MiniMax TTS API returns HTTP 200 even on quota/billing errors, with the
error encoded in base_resp.status_code. Without this check, placeholder
audio returned alongside the error is silently accepted, preventing the
TTS dispatcher from falling back to a configured secondary provider.
This follows the same pattern used by all other MiniMax providers:
- image-generation-provider.ts
- video-generation-provider.ts
- music-generation-provider.ts
- minimax-web-search-provider.runtime.ts
Fixes#76904
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(whatsapp): extract GIF metadata and distinguish gifPlayback in media placeholders (fixes#49099)
- Add escapeAttr() helper to sanitize quotes and angle brackets in XML attribute values
- Add extractExternalAdReplyMetadata() to extract title, sourceUrl, body from contextInfo.externalAdReply
- Distinguish GIFs from videos using videoMessage.gifPlayback flag (media:gif vs media:video)
- Enrich image and video placeholders with externalAdReply metadata when available
- Add 5 test cases covering GIF detection, metadata extraction, attribute escaping, and empty fields
* fix(whatsapp): keep GIF metadata in untrusted context
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
QQBot is the only channel that root-sandboxes outbound local files. Its three
gate sites (resolveOutboundMediaPath, the voice send re-check, and
structured-payload validation) only trusted the QQ Bot media storage roots, so
framework-generated scratch media written under OpenClaw's hardened temp root
(e.g. cron auto-TTS voice files from speech-core) was rejected. The send then
returned a no-identity error, the message was silently lost, yet cron still
recorded it as delivered.
Add one shared resolver (resolveTrustedOutboundMediaPath) that also trusts the
preferred OpenClaw temp root — already a sanctioned media root in core
(buildMediaLocalRoots) — and route all three gates through it so the trust set
agrees everywhere. Fixes#92816.
Co-authored-by: zengwen <zeng_wen@foxmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Preserve concurrent prompt-time transcript updates across stale session managers, side appends, transcript navigation, nested owned writes, and doctor repair.
Fixes#93193.
Thanks @snowzlm for the report and original fix.
Co-authored-by: snowzlm <snowzlm@noreply.codeberg.org>
Adds Codex as a selectable hosted web-search provider, routes native Codex search safely across model overrides, and isolates bounded hosted-search workers from configured tools.\n\nVerification: focused post-merge regression suite passed 202/202 tests on exact head 23824af49a.
* fix(ollama): repair retired cloud provider endpoint
Route configured Ollama Cloud provider ids through plugin doctor compatibility migrations so doctor --fix can rewrite the retired ai.ollama.com endpoint before runtime reads persisted config.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(doctor): align provider fixture with typed config
Ensure the doctor registry provider-scoped migration test uses a fully typed provider fixture so the test type-check shard validates the intended behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(ollama): align doctor fixture with typed config
Use fully typed provider and model fixtures in the Ollama doctor contract tests so the extension test type-check shard validates the migration behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ollama): preserve custom cloud provider base url
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ollama): avoid logging retired endpoint secrets
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(discord): apply tool status emojis immediately to avoid override by thinking reactions
Tool emoji reactions (🛠️, 🌐, 🔎, etc.) during Discord tool/skill execution
were not appearing because setTool() used a 700ms debounce shared with
setThinking(). Rapid onReasoningStream calls from overlapping reasoning
would repeatedly overwrite the pending tool emoji with 🧠, so the tool
emoji never reached Discord.
Fix by making setTool() apply emojis immediately (skip debounce). Tool
transitions are user-facing state changes that should be visible without
delay, and the terminal done/error transitions already flush any pending
state.
Fixes#92715.
* fix(discord): forward quiet tool lifecycle status
* fix(slack): preserve tool status reactions
* test(channels): type quiet tool lifecycle options
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* [AI] fix(feishu): guard against missing inbound in channelRuntime fallback
When channelRuntime from gateway context is truthy but lacks the inbound
property, the ?? operator still selects it over getFeishuRuntime().channel,
causing TypeError at core.channel.inbound.run().
The ChannelGatewayContext types channelRuntime as ChannelRuntimeSurface
(only guarantees runtimeContexts), but channel.ts casts it to
PluginRuntimeChannel via type assertion. If a partial runtime object
without inbound is provided, the type lie becomes a runtime crash.
Fix: check channelRuntime?.inbound before using it; fall back to
getFeishuRuntime().channel when inbound is absent.
Related to #93453
* [AI] test(feishu): add regression for partial channelRuntime lacking inbound
When channelRuntime has runtimeContexts but no inbound, the guard in
bot.ts should fall back to getFeishuRuntime().channel. Add a test that
passes a partial channelRuntime and verifies dispatch does not crash.
Refs #93453
Suppress each raw commentary echo paired with a typed Codex item completion by protocol order, while preserving later raw-only notes and contributor-rewritten completion text.
Fixes#93296.
Thanks @Marvinthebored.
Verification:
- 95 focused projector tests
- full CI run 27593515603
- real behavior proof run 27593522821
- local and whole-branch autoreview clean
Co-authored-by: Peter Lindsey <peter@lindsey.jp>