
























@@ -20,7 +20,7 @@ describe("buildWebchatAudioContentBlocksFromReplyPayloads", () => {
2020tmpDir = undefined;
2121});
222223-it("embeds a local audio file as a base64 gateway chat block when it is under localRoots", async () => {
23+it("exposes a local audio file as a media-ticketed attachment when it is under localRoots", async () => {
2424tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-webchat-audio-"));
2525const audioPath = path.join(tmpDir, "clip.mp3");
2626fs.writeFileSync(audioPath, Buffer.from([0xff, 0xfb, 0x90, 0x00]));
@@ -33,15 +33,34 @@ describe("buildWebchatAudioContentBlocksFromReplyPayloads", () => {
3333expect(blocks).toHaveLength(1);
3434const block = blocks[0] as {
3535type?: string;
36-source?: { type?: string; media_type?: string; data?: string };
36+attachment?: { url?: string; kind?: string; label?: string; mimeType?: string };
3737};
38-expect(block.type).toBe("audio");
39-expect(block.source?.type).toBe("base64");
40-expect(block.source?.media_type).toBe("audio/mpeg");
41-expect(block.source?.data?.includes("data:")).toBe(false);
42-expect(Buffer.from(block.source?.data ?? "", "base64")).toEqual(
43-Buffer.from([0xff, 0xfb, 0x90, 0x00]),
38+expect(block.type).toBe("attachment");
39+expect(block.attachment).toEqual({
40+url: fs.realpathSync(audioPath),
41+kind: "audio",
42+label: "clip.mp3",
43+mimeType: "audio/mpeg",
44+});
45+});
46+47+it("preserves voice-note metadata on local audio attachments", async () => {
48+tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-webchat-audio-"));
49+const audioPath = path.join(tmpDir, "clip.mp3");
50+fs.writeFileSync(audioPath, Buffer.from([0xff, 0xfb, 0x90, 0x00]));
51+52+const blocks = await buildWebchatAudioContentBlocksFromReplyPayloads(
53+[{ mediaUrl: audioPath, trustedLocalMedia: true, audioAsVoice: true }],
54+{ localRoots: [tmpDir] },
4455);
56+57+expect(blocks).toHaveLength(1);
58+expect(blocks[0]).toMatchObject({
59+type: "attachment",
60+attachment: {
61+isVoiceNote: true,
62+},
63+});
4564});
46654766it("suppresses reasoning payload audio", async () => {
@@ -113,7 +132,7 @@ describe("buildWebchatAudioContentBlocksFromReplyPayloads", () => {
113132);
114133115134expect(blocks).toHaveLength(1);
116-expect((blocks[0] as { type?: string }).type).toBe("audio");
135+expect((blocks[0] as { type?: string }).type).toBe("attachment");
117136});
118137119138it("drops tool-result file:// URLs with remote hosts before touching the filesystem", async () => {
@@ -171,7 +190,7 @@ describe("buildWebchatAudioContentBlocksFromReplyPayloads", () => {
171190]);
172191173192expect(blocks).toHaveLength(1);
174-expect((blocks[0] as { type?: string }).type).toBe("audio");
193+expect((blocks[0] as { type?: string }).type).toBe("attachment");
175194});
176195177196it("skips local audio when the opened file stat is over the cap", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。