fix: harden async media completion delivery

This commit is contained in:
Peter Steinberger 2026-04-06 03:21:50 +01:00
parent 427997f989
commit 2cb057fcd9
No known key found for this signature in database
4 changed files with 11 additions and 10 deletions

View File

@ -1,5 +1,6 @@
import crypto from "node:crypto";
import { parseReplyDirectives } from "../../auto-reply/reply/reply-directives.js";
import { SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { parseAgentSessionKey } from "../../sessions/session-key-utils.js";
import {
@ -147,8 +148,8 @@ function buildMediaGenerationReplyInstruction(params: {
if (params.status === "ok") {
return [
`A completed ${params.completionLabel} generation task is ready for user delivery.`,
`Reply in your normal assistant voice and post the finished ${params.completionLabel} to the original message channel now.`,
"If the result includes MEDIA: lines, include those exact MEDIA: lines in your reply so OpenClaw attaches the generated media.",
`Prefer the message tool for delivery: use action="send" to the current/original chat, put your user-facing caption in message, attach each generated file with path/filePath using the exact path from the result, then reply ONLY: ${SILENT_REPLY_TOKEN}.`,
`If you cannot use the message tool, reply in your normal assistant voice and include the exact MEDIA: lines from the result so OpenClaw attaches the finished ${params.completionLabel}.`,
"Keep internal task/session details private and do not copy the internal event text verbatim.",
].join(" ");
}

View File

@ -148,16 +148,16 @@ describe("music generate background helpers", () => {
to: "channel:1",
}),
expectsCompletionMessage: true,
internalEvents: [
internalEvents: expect.arrayContaining([
expect.objectContaining({
source: "music_generation",
announceType: "music generation task",
status: "ok",
result: expect.stringContaining("MEDIA:/tmp/generated-night-drive.mp3"),
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction: expect.stringContaining("include those exact MEDIA: lines"),
replyInstruction: expect.stringContaining("Prefer the message tool for delivery"),
}),
],
]),
}),
);
});

View File

@ -148,16 +148,16 @@ describe("video generate background helpers", () => {
to: "channel:1",
}),
expectsCompletionMessage: true,
internalEvents: [
internalEvents: expect.arrayContaining([
expect.objectContaining({
source: "video_generation",
announceType: "video generation task",
status: "ok",
result: expect.stringContaining("MEDIA:/tmp/generated-lobster.mp4"),
mediaUrls: ["/tmp/generated-lobster.mp4"],
replyInstruction: expect.stringContaining("include those exact MEDIA: lines"),
replyInstruction: expect.stringContaining("Prefer the message tool for delivery"),
}),
],
]),
}),
);
});

View File

@ -400,7 +400,7 @@ describe("dispatchReplyFromConfig reply_dispatch hook", () => {
ctx: createHookCtx(),
cfg: emptyConfig,
dispatcher: createDispatcher(),
fastAbortResolver: () => ({ handled: false, aborted: false }),
fastAbortResolver: async () => ({ handled: false, aborted: false }),
formatAbortReplyTextResolver: () => "⚙️ Agent was aborted.",
replyResolver: async () => ({ text: "model reply" }),
});
@ -425,7 +425,7 @@ describe("dispatchReplyFromConfig reply_dispatch hook", () => {
handled: false,
queuedFinal: false,
counts: { tool: 0, block: 0, final: 0 },
});
} satisfies PluginHookReplyDispatchResult);
const result = await dispatchReplyFromConfig({
ctx: createHookCtx(),