mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
fix(auto-reply): preserve silent voice payloads
This commit is contained in:
@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Auto-reply/commands: stop bare `/reset` and `/new` after reset hooks acknowledge the command, so non-ACP channels no longer fall through into empty provider calls while `/reset <message>` and `/new <message>` still seed the next model turn. Fixes #73367. Thanks @hoyanhan and @wenxu007.
|
||||
- Auto-reply: preserve voice-note media from silent turns while continuing to suppress text and non-voice media, so `NO_REPLY` TTS replies still deliver the requested audio bubble. (#73406) Thanks @zqchris.
|
||||
- Agents/Anthropic: send implicit Anthropic beta headers only to direct public Anthropic endpoints, including OAuth, so custom Anthropic-compatible providers no longer mis-handle unsupported beta flags unless explicitly configured. Refs #73346. Thanks @byBrodowski.
|
||||
- Skills: require explicit `skills.entries.coding-agent.enabled` before exposing the bundled coding-agent skill, so installs with Codex on PATH but no OpenAI auth do not silently offer Codex delegation. Fixes #73358. Thanks @LaFleurAdvertising and @Sanjays2402.
|
||||
- Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock and keep Docker bundled plugin runtime deps/mirrors in a Docker-managed volume instead of the Windows/WSL config bind mount, so cold starts avoid slow host-volume mirror writes. Fixes #73339. Thanks @1yihui.
|
||||
|
||||
@@ -371,7 +371,7 @@ describe("buildReplyPayloads media filter integration", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("drops all final payloads during silent turns, including media-only payloads", async () => {
|
||||
it("drops non-voice final payloads during silent turns, including media-only payloads", async () => {
|
||||
const { replyPayloads } = await buildReplyPayloads({
|
||||
...baseParams,
|
||||
silentExpected: true,
|
||||
@@ -381,6 +381,31 @@ describe("buildReplyPayloads media filter integration", () => {
|
||||
expect(replyPayloads).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("keeps voice media payloads during silent turns", async () => {
|
||||
const { replyPayloads } = await buildReplyPayloads({
|
||||
...baseParams,
|
||||
silentExpected: true,
|
||||
payloads: [{ text: "NO_REPLY", mediaUrl: "file:///tmp/voice.opus", audioAsVoice: true }],
|
||||
});
|
||||
|
||||
expect(replyPayloads).toHaveLength(1);
|
||||
expect(replyPayloads[0]).toMatchObject({
|
||||
text: undefined,
|
||||
mediaUrl: "file:///tmp/voice.opus",
|
||||
audioAsVoice: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("drops empty voice markers during silent turns", async () => {
|
||||
const { replyPayloads } = await buildReplyPayloads({
|
||||
...baseParams,
|
||||
silentExpected: true,
|
||||
payloads: [{ audioAsVoice: true }],
|
||||
});
|
||||
|
||||
expect(replyPayloads).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("suppresses warning text when silent media payloads fail normalization", async () => {
|
||||
const normalizeMediaPaths = async () => {
|
||||
throw new Error("file not found");
|
||||
|
||||
@@ -87,6 +87,10 @@ async function normalizeSentMediaUrlsForDedupe(params: {
|
||||
return normalizedUrls;
|
||||
}
|
||||
|
||||
function shouldKeepPayloadDuringSilentTurn(payload: ReplyPayload): boolean {
|
||||
return payload.audioAsVoice === true && resolveSendableOutboundReplyParts(payload).hasMedia;
|
||||
}
|
||||
|
||||
export async function buildReplyPayloads(params: {
|
||||
payloads: ReplyPayload[];
|
||||
isHeartbeat: boolean;
|
||||
@@ -165,7 +169,9 @@ export async function buildReplyPayloads(params: {
|
||||
}),
|
||||
)
|
||||
).filter(isRenderablePayload);
|
||||
const silentFilteredPayloads = params.silentExpected ? [] : replyTaggedPayloads;
|
||||
const silentFilteredPayloads = params.silentExpected
|
||||
? replyTaggedPayloads.filter(shouldKeepPayloadDuringSilentTurn)
|
||||
: replyTaggedPayloads;
|
||||
|
||||
// Drop final payloads only when block streaming succeeded end-to-end.
|
||||
// If streaming aborted (e.g., timeout), fall back to final payloads.
|
||||
|
||||
Reference in New Issue
Block a user