Skip to content

Commit 2fcd481

Browse files
fix(slack): keep downloaded files out of reply media (#86318)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9239f94 commit 2fcd481

4 files changed

Lines changed: 65 additions & 0 deletions

File tree

extensions/slack/src/action-runtime.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ describe("handleSlackAction", () => {
448448
});
449449
expect(details.media).toEqual({
450450
mediaUrl: "/tmp/openclaw-media/report.pdf",
451+
outbound: false,
451452
contentType: "application/pdf",
452453
});
453454
});

extensions/slack/src/action-runtime.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ export async function handleSlackAction(
492492
placeholder: downloaded.placeholder,
493493
media: {
494494
mediaUrl: downloaded.path,
495+
outbound: false,
495496
...(downloaded.contentType ? { contentType: downloaded.contentType } : {}),
496497
},
497498
});
@@ -504,6 +505,7 @@ export async function handleSlackAction(
504505
fileId,
505506
path: downloaded.path,
506507
...(downloaded.contentType ? { contentType: downloaded.contentType } : {}),
508+
media: { outbound: false },
507509
},
508510
});
509511
}

src/agents/pi-embedded-subscribe.handlers.tools.media.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,61 @@ describe("handleToolExecutionEnd media emission", () => {
664664
expect(ctx.state.pendingToolTrustedLocalMedia).toBe(true);
665665
});
666666

667+
it("does NOT queue structured media marked as non-outbound", async () => {
668+
const ctx = createMockContext({
669+
shouldEmitToolOutput: false,
670+
onToolResult: vi.fn(),
671+
builtinToolNames: new Set(["message"]),
672+
});
673+
674+
await handleToolExecutionEnd(ctx, {
675+
type: "tool_execution_end",
676+
toolName: "message",
677+
toolCallId: "tc-1",
678+
isError: false,
679+
result: {
680+
content: [{ type: "text", text: "Downloaded Slack file to /tmp/report.pdf" }],
681+
details: {
682+
media: {
683+
mediaUrl: "/tmp/report.pdf",
684+
outbound: false,
685+
},
686+
},
687+
},
688+
});
689+
690+
expect(ctx.state.pendingToolMediaUrls).toStrictEqual([]);
691+
});
692+
693+
it("does NOT queue image fallback paths when media is marked as non-outbound", async () => {
694+
const ctx = createMockContext({
695+
shouldEmitToolOutput: false,
696+
onToolResult: vi.fn(),
697+
builtinToolNames: new Set(["message"]),
698+
});
699+
700+
await handleToolExecutionEnd(ctx, {
701+
type: "tool_execution_end",
702+
toolName: "message",
703+
toolCallId: "tc-1",
704+
isError: false,
705+
result: {
706+
content: [
707+
{ type: "text", text: "Downloaded Slack image" },
708+
{ type: "image", data: "base64", mimeType: "image/png" },
709+
],
710+
details: {
711+
path: "/tmp/slack-image.png",
712+
media: {
713+
outbound: false,
714+
},
715+
},
716+
},
717+
});
718+
719+
expect(ctx.state.pendingToolMediaUrls).toStrictEqual([]);
720+
});
721+
667722
it("queues trusted TTS local media when the exact built-in name is absent", async () => {
668723
const ctx = createMockContext({
669724
shouldEmitToolOutput: false,

src/agents/pi-embedded-subscribe.tools.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,10 @@ function collectStructuredMediaUrls(media: Record<string, unknown>): string[] {
463463
return Array.from(new Set(urls));
464464
}
465465

466+
function isNonOutboundToolResultMedia(media: Record<string, unknown>): boolean {
467+
return media.outbound === false;
468+
}
469+
466470
function extractTextContentMediaArtifact(content: unknown[]): {
467471
mediaUrls: string[];
468472
audioAsVoice?: boolean;
@@ -510,6 +514,9 @@ export function extractToolResultMediaArtifact(
510514
const record = result as Record<string, unknown>;
511515
const detailsMedia = readToolResultDetailsMedia(record);
512516
if (detailsMedia) {
517+
if (isNonOutboundToolResultMedia(detailsMedia)) {
518+
return undefined;
519+
}
513520
const mediaUrls = collectStructuredMediaUrls(detailsMedia);
514521
if (mediaUrls.length > 0) {
515522
return {

0 commit comments

Comments
 (0)