End-to-end testing on macOS + BlueBubbles + ElevenLabs walked through three CAF flavors before landing on the format Apple's Messages.app actually emits when a user records a native iMessage voice memo:
- PCM int16 @ 44.1 kHz CAF: BlueBubbles' internal `afconvert -f m4af -d aac` conversion fails; the original CAF reaches iMessage but renders with 0 s duration.
- AAC @ 22.05 kHz mono CAF: BlueBubbles' conversion succeeds and the server silently downgrades the delivery, sending the converted MP3 as a generic audio attachment.
- **Opus @ 24 kHz mono CAF**: byte-identical to the descriptor block Apple's Messages.app produces; BlueBubbles passes it through unchanged and iMessage renders a native voice-memo bubble with proper duration and waveform UI.
Adds an opt-in `tts.voice.preferAudioFileFormat` channel capability and a macOS `afconvert`-backed pre-transcode in the speech-core pipeline. BlueBubbles declares `preferAudioFileFormat: "caf"`. Other channels are unaffected. Falls back to the original buffer when the host platform, the source/target pair, or the transcoder process can't produce the preferred container — so non-Darwin hosts and unsupported provider combinations are unchanged.
Also adds a `caff` magic-byte sniff in `src/media/mime.ts` so the auto-reply host-local-media validator (which uses `file-type` and didn't recognize CAF natively) accepts the buffer instead of dropping it as "⚠️ Media failed."
Fixes#72506.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remediate current-profile CodeQL findings for file SecretRef id validation and release workflow job permissions. Includes changelog credit. Thanks @vincentkoc.
Feishu config defaults groupPolicy to 'allowlist'. Inbound group handling read groupAllowFrom and called isFeishuGroupAllowed before resolveFeishuReplyPolicy was reached, so a config that only set channels.feishu.groups.<chat_id>.requireMention=false (with no groupAllowFrom) was rejected with 'group not in groupAllowFrom' before per-group requireMention could take effect. Treat the explicit presence of a group entry under channels.feishu.groups as the operator's allowlist signal: if groupConfig is defined, skip the empty-allowlist rejection. resolveFeishuReplyPolicy still owns mention gating, and existing groupConfig.enabled=false / groupAllowFrom-driven rejections are preserved. Adds a regression test that exercises the reporter's exact config shape and confirms inbound text reaches finalize/dispatch.
The ACP dispatch path calls applyMediaUnderstanding without the agentDir
parameter. This prevents the media understanding pipeline from locating
agent-specific models.json and auth profiles, causing image understanding
to fail silently for non-visual models configured with a separate image
understanding model.
The non-ACP reply path (get-reply.ts) already passes agentDir correctly.
This aligns the ACP path with the same behavior.
Closes#55046
AI-assisted (built with Hermes orchestration).
Address review feedback on PR #72888. triggerInternalHook passes the
same event reference to all handlers sequentially. Mutating evt.context
leaks pluginConfig to subsequent handlers and causes cross-plugin
overwrites. Shallow-copy event and context instead.
When plugins register hooks via api.registerHook(), pluginConfig from
openclaw.json was not available in the hook event context. Plugins that
accessed ctx.pluginConfig or event.context.pluginConfig received
undefined, causing silent failures or fallback to defaults.
Changes:
- Add pluginConfig parameter to registerHook() function
- Wrap handler to inject pluginConfig into event.context before invocation
- Pass params.pluginConfig through createApi() call site
Fixes#72880
Closes#72837. The 15s narrative-subagent timeout was empirically too
tight for warm-gateway runs across light, REM, and deep phases —
gpt-5.4-mini latency through OpenAI alone routinely brushes 12s+, so the
first sweep after a restart deterministically times out across all three
phases. 60s gives realistic LLM-call headroom while still capping the
worst case at one minute, preserving the original comment's "don't leave
parent cron running for minutes" constraint.
Test: updates the matching toMatchObject assertion in
dreaming-narrative.test.ts from 15_000 to 60_000.
When the system hostname exceeds 63 bytes (common with Kubernetes pod
names), the @homebridge/ciao DNS label encoder throws an AssertionError
that crashes the gateway on startup.
Add truncateToDnsLabel() that safely truncates UTF-8 strings at byte
boundaries, applied to both the service instance name and hostname
before passing them to ciao.
Closes#37705
AI-assisted (built with Hermes orchestration).
Commit 2cd23957c0 ("build: use slim docker runtime") switched the
runtime image from `node:24-bookworm` (full) to `node:24-bookworm-slim`.
The slim base does not ship `ca-certificates`, and the runtime stage's
`apt-get install` line was not updated to add it.
Result on the resulting image:
- `/etc/ssl/certs/` is empty (`ls /etc/ssl/certs/ | wc -l` == 0)
- `dpkg -l ca-certificates` reports `un` (not installed)
- `update-ca-certificates` is missing in `$PATH` (exit 127)
- every HTTPS outbound from the gateway dies at TLS handshake with
`error setting certificate file: /etc/ssl/certs/ca-certificates.crt`
- channel plugins that use `node fetch` (telegram/discord/slack)
crash-loop with `Network request for 'deleteWebhook' failed!`
and pin the gateway main thread at ~100% CPU on retry.
Verified by rebuilding the runtime image with this patch and
confirming inside the container:
- `ls /etc/ssl/certs/ | wc -l` -> 285
- `curl -4 https://api.telegram.org/` -> 302
- `curl -4 https://www.google.com/` -> 200
- channel plugins (telegram/discord/slack) register cleanly,
gateway main-thread CPU returns to idle.
Add `ca-certificates` to the apt-install list and call
`update-ca-certificates` to populate the CA bundle.
Signed-off-by: ryuhaneul <luj.moonlight@gmail.com>
When the post-update completion cache refresh times out (slow disk,
large bundled plugin tree, Docker overlayfs), the user previously saw
the opaque 'Completion cache update failed: Error: spawnSync
/usr/bin/node ETIMEDOUT'. Detect ETIMEDOUT specifically, surface
'timed out after 30s', and append a manual refresh hint pointing at
'openclaw completion --write-state' so users know it's non-fatal and
how to recover.
Fixes#72842
Add an end anchor to the type/subtype match and explicitly accept the
RFC 9110 ;parameter tail. Inputs like "image/png<script>" or
"application/json garbage" now return undefined instead of silently
matching the leading prefix.
Closes#9795