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>
When an assistant message's `content` is a raw string at runtime (JSONL
transcript replay passes it through even though the type declares an array),
the OpenAI-compatible completions path crashes:
- `transformMessages` called `assistantMsg.content.flatMap(...)` ->
`TypeError: ... .flatMap is not a function` (first crash, always hit).
- Two `hasToolHistory` helpers (`openai-transport-stream.ts` and
`openai-completions.ts`) called `content.some(...)` -> `TypeError: ...
.some is not a function` (siblings, surface once the flatMap crash is fixed).
Normalize a string assistant content to an equivalent single text block
before transforming (matching the string->text handling already used in
anthropic-payload-policy.ts), and `Array.isArray`-guard both `hasToolHistory`
helpers so a string assistant simply does not count toward tool history.
Verified end-to-end through the real `buildOpenAICompletionsParams` and
`streamOpenAICompletions` entry points: before the fix a string-content
assistant followed by a toolResult throws TypeError; after the fix params are
produced correctly (string preserved as text, tool history detected). Normal
array content is unaffected.
* fix(respawn): rewrite pnpm versioned entry paths to stable wrapper
During self-update the pnpm versioned directory (node_modules/.pnpm/openclaw@<ver>/)
may be removed. If process.argv contains the versioned path, the respawned child
fails to start because the entrypoint no longer exists.
Detect pnpm versioned realpaths in spawnDetachedGatewayProcess and rewrite them
to the stable node_modules/<pkg>/openclaw.mjs wrapper before spawning.
Fixes#52313
* fix(respawn): scope pnpm entry rewrite to openclaw
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(wizard): preserve existing default model during setup auth choice
Without preserveExistingDefaultModel: true, the setup wizard
overwrite the user's configured default model when a new provider
auth is selected. This causes existing heartbeat turns to silently
consume paid API quota (e.g. Google Gemini) instead of the user's
original model.
The configure.gateway-auth.ts path already passes this flag; the
setup wizard path was missing it.
Fixes#64129
* fix(wizard): add type assertion for preserveExistingDefaultModel test
Summary:
- This PR changes the docs i18n Codex command-output preview to keep a short head plus retained tail, and adds Go unit coverage for stdout and stderr tails.
- PR surface: Other +20. Total +20 across 2 files.
- Reproducibility: yes. Source inspection of current main and `v2026.6.6` shows long output is truncated to the prefix only, and the PR's focused tests model the stdout/stderr tail cases that lose final API details.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head b510b598c6.
- Required merge gates passed before the squash merge.
Prepared head SHA: b510b598c6
Review: https://github.com/openclaw/openclaw/pull/93687#issuecomment-4720840859
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Mason Huang <8814856+hxy91819@users.noreply.github.com>
Approved-by: hxy91819
* fix(agents): handle string assistant content in getLastAssistantText
PR #93456 added an `if (!Array.isArray(message.content)) return false` guard
to hasAssistantToolCallArguments, acknowledging that a persisted/legacy
assistant message can carry a string `content` at runtime even though the
type is declared as an array. buildSessionContext pushes such entries through
unchanged, so the string can reach agent.state.messages.
getLastAssistantText() still assumed an array: iterating a string `content`
yields individual characters, none of which has `type === "text"`, so the
assistant's text was silently dropped and the function returned undefined.
Mirror extractTextContent(): when `content` is a string, treat it as the text
itself; otherwise iterate the content blocks as before. The aborted/empty
check is left untouched because `.length === 0` is already correct for both an
empty array and an empty string.
* fix(agents): safely read persisted assistant text
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
When the block reply pipeline streamed partial content, buildReplyPayloads()
unconditionally dropped all text-only final payloads. This suppressed the
complete final reply when the pipeline only streamed a partial block and
never sent the exact final text.
The fix checks hasSentPayload() for text-only payloads too, preserving
unsent finals instead of dropping them unconditionally.
The async Clipboard API is only available in secure contexts (HTTPS or
localhost). On plain-HTTP deployments navigator.clipboard is undefined, so the
code block copy button threw synchronously and silently failed. Add a shared
copyToClipboard helper that guards the secure-context path and falls back to the
legacy execCommand copy, reuse it for the code block button and the copy-as-
markdown affordance, and cover it with a unit test plus a real-browser e2e that
simulates the non-secure context.
Fixes#93628
Co-authored-by: Pick-cat <266665499+Pick-cat@users.noreply.github.com>
Summary:
- The PR changes `/status` context-window selection to ignore stale runtime snapshots after manual model switches while preserving fallback/runtime-alias context windows.
- PR surface: Source +6, Tests +128. Total +134 across 2 files.
- Reproducibility: yes. source-reproducible: current main trusts explicit runtime context before checking fall ... fer. I did not run a local failing repro, but the PR fixture models the stale prior-runtime state directly.
Automerge notes:
- PR branch already contained follow-up commit before automerge: test(status): make context fixtures type-correct
Validation:
- ClawSweeper review passed for head f14fda4279.
- Required merge gates passed before the squash merge.
Prepared head SHA: f14fda4279
Review: https://github.com/openclaw/openclaw/pull/93306#issuecomment-4708596208
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Approved-by: hxy91819