fix(media): require HEIC conversion fallback

This commit is contained in:
Vincent Koc
2026-05-03 23:30:27 -07:00
parent d8da04e58e
commit 3dcff3b267
3 changed files with 20 additions and 2 deletions

View File

@@ -56,6 +56,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Media/images: keep HEIC/HEIF attachments fail-closed when optional Sharp conversion is unavailable instead of sending originals that still need conversion. Thanks @vincentkoc.
- Telegram/streaming: sanitize tool-progress draft preview backticks before shared compaction, so long backtick-heavy progress text still renders inside the safe code-formatted preview instead of collapsing to an ellipsis.
- UI/chat: remove the unsupported `line-clamp` declaration from the chat queue text rule to eliminate Firefox console noise without changing visible truncation behavior. Thanks @ZanderH-code.
- Agents/Pi: suppress persistence for synthetic mid-turn overflow continuation prompts, so transcript-retry recovery does not write the "continue from transcript" prompt as a new user turn. Thanks @vincentkoc.

View File

@@ -176,7 +176,9 @@ describe("loadWebMedia", () => {
throw new Error("should not optimize png");
}),
resizeToJpeg: vi.fn(async () => {
throw new Error("should not resize jpeg");
throw new Error(
"Optional dependency sharp is required for image attachment processing | Cannot find package 'sharp' imported from image-ops.js",
);
}),
}));
try {
@@ -210,6 +212,17 @@ describe("loadWebMedia", () => {
});
});
it("does not send original HEIC media when optional sharp conversion is unavailable", async () => {
await withUnavailableImageOptimizer(async () => {
const heicFile = path.join(fixtureRoot, "photo.heic");
await fs.writeFile(heicFile, Buffer.from("heic-source"));
const { loadWebMedia: loadWebMediaWithMissingOptimizer } = await import("./web-media.js");
await expect(
loadWebMediaWithMissingOptimizer(heicFile, createLocalWebMediaOptions()),
).rejects.toThrow(/Optional dependency sharp is required/);
});
});
it("resolves relative local media paths against the provided workspace directory", async () => {
const result = await loadWebMedia("chart.png", {
maxBytes: 1024 * 1024,

View File

@@ -413,7 +413,11 @@ async function loadWebMediaInternal(
try {
optimized = await optimizeImageWithFallback({ buffer, cap, meta });
} catch (err) {
if (isOptionalImageOptimizerUnavailable(err) && buffer.length <= cap) {
if (
isOptionalImageOptimizerUnavailable(err) &&
!isHeicSource(meta ?? {}) &&
buffer.length <= cap
) {
if (shouldLogVerbose()) {
logVerbose(
`Image optimizer unavailable; sending original ${formatMb(buffer.length)}MB media without optimization`,