fix(agents): preserve trusted tool media provenance · openclaw/openclaw@310c853
vincentkoc
·
2026-05-16
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
|
28 | 28 | - Telegram/Gateway: route targeted Telegram `/stop@bot` messages onto the control lane without cached bot metadata and match gateway stop requests across raw/canonical session aliases. (#82298) Thanks @VACInc. |
29 | 29 | - MS Teams/media: sniff inline `data:image/*` attachment bytes before staging them, skipping payloads that are not actually images. |
30 | 30 | - WebChat/media: require trusted local-media provenance before preserving local audio reply paths for display, so untrusted audio-looking paths go through normal staging and read-policy checks. |
| 31 | +- Agents/tool media: preserve trusted local-media provenance when merging generated tool attachments into final reply payloads, so trusted audio/media survives outbound display normalization. |
31 | 32 | - Update: let package-swap `doctor --fix` persist core config repairs while plugin schemas are still converging, preventing update failures on externalized channel configs. |
32 | 33 | - Update: carry plugin-validation bypasses into config mutation pre-write reads, so package update doctor repairs can finish while externalized plugin schemas are converging. |
33 | 34 | - Update/doctor: keep plugin-validation bypasses on the top-level `$include` config write path, so package repair can update included plugin config files without flattening them into the root config. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -2516,6 +2516,7 @@ export async function runEmbeddedPiAgent(
|
2516 | 2516 | payloads, |
2517 | 2517 | toolMediaUrls: attempt.toolMediaUrls, |
2518 | 2518 | toolAudioAsVoice: attempt.toolAudioAsVoice, |
| 2519 | +toolTrustedLocalMedia: attempt.toolTrustedLocalMedia, |
2519 | 2520 | }); |
2520 | 2521 | const timedOutDuringPrompt = |
2521 | 2522 | timedOut && !timedOutDuringCompaction && !timedOutDuringToolExecution; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -4277,6 +4277,7 @@ export async function runEmbeddedAttempt(
|
4277 | 4277 | heartbeatToolResponse: getHeartbeatToolResponse(), |
4278 | 4278 | toolMediaUrls: pendingToolMediaReply?.mediaUrls, |
4279 | 4279 | toolAudioAsVoice: pendingToolMediaReply?.audioAsVoice, |
| 4280 | +toolTrustedLocalMedia: pendingToolMediaReply?.trustedLocalMedia, |
4280 | 4281 | successfulCronAdds: getSuccessfulCronAdds(), |
4281 | 4282 | cloudCodeAssistFormatError: Boolean( |
4282 | 4283 | lastAssistant?.errorMessage && isCloudCodeAssistFormatError(lastAssistant.errorMessage), |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -70,4 +70,23 @@ describe("mergeAttemptToolMediaPayloads", () => {
|
70 | 70 | deliverDespiteSourceReplySuppression: true, |
71 | 71 | }); |
72 | 72 | }); |
| 73 | + |
| 74 | +it("preserves trusted local media provenance when merging tool media", () => { |
| 75 | +expect( |
| 76 | +mergeAttemptToolMediaPayloads({ |
| 77 | +payloads: [{ text: "done" }], |
| 78 | +toolMediaUrls: ["/tmp/reply.opus"], |
| 79 | +toolAudioAsVoice: true, |
| 80 | +toolTrustedLocalMedia: true, |
| 81 | +}), |
| 82 | +).toEqual([ |
| 83 | +{ |
| 84 | +text: "done", |
| 85 | +mediaUrls: ["/tmp/reply.opus"], |
| 86 | +mediaUrl: "/tmp/reply.opus", |
| 87 | +audioAsVoice: true, |
| 88 | +trustedLocalMedia: true, |
| 89 | +}, |
| 90 | +]); |
| 91 | +}); |
73 | 92 | }); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -7,11 +7,12 @@ export function mergeAttemptToolMediaPayloads(params: {
|
7 | 7 | payloads?: EmbeddedRunPayload[]; |
8 | 8 | toolMediaUrls?: string[]; |
9 | 9 | toolAudioAsVoice?: boolean; |
| 10 | +toolTrustedLocalMedia?: boolean; |
10 | 11 | }): EmbeddedRunPayload[] | undefined { |
11 | 12 | const mediaUrls = Array.from( |
12 | 13 | new Set(params.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []), |
13 | 14 | ); |
14 | | -if (mediaUrls.length === 0 && !params.toolAudioAsVoice) { |
| 15 | +if (mediaUrls.length === 0 && !params.toolAudioAsVoice && !params.toolTrustedLocalMedia) { |
15 | 16 | return params.payloads; |
16 | 17 | } |
17 | 18 | |
@@ -25,6 +26,7 @@ export function mergeAttemptToolMediaPayloads(params: {
|
25 | 26 | mediaUrls: mergedMediaUrls.length ? mergedMediaUrls : undefined, |
26 | 27 | mediaUrl: payload.mediaUrl ?? mergedMediaUrls[0], |
27 | 28 | audioAsVoice: payload.audioAsVoice || params.toolAudioAsVoice || undefined, |
| 29 | +trustedLocalMedia: payload.trustedLocalMedia || params.toolTrustedLocalMedia || undefined, |
28 | 30 | }); |
29 | 31 | return payloads; |
30 | 32 | } |
@@ -35,6 +37,7 @@ export function mergeAttemptToolMediaPayloads(params: {
|
35 | 37 | mediaUrls: mediaUrls.length ? mediaUrls : undefined, |
36 | 38 | mediaUrl: mediaUrls[0], |
37 | 39 | audioAsVoice: params.toolAudioAsVoice || undefined, |
| 40 | +trustedLocalMedia: params.toolTrustedLocalMedia || undefined, |
38 | 41 | }, |
39 | 42 | ]; |
40 | 43 | } |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -122,6 +122,7 @@ export type EmbeddedRunAttemptResult = {
|
122 | 122 | heartbeatToolResponse?: HeartbeatToolResponse; |
123 | 123 | toolMediaUrls?: string[]; |
124 | 124 | toolAudioAsVoice?: boolean; |
| 125 | +toolTrustedLocalMedia?: boolean; |
125 | 126 | successfulCronAdds?: number; |
126 | 127 | cloudCodeAssistFormatError: boolean; |
127 | 128 | attemptUsage?: NormalizedUsage; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -173,6 +173,7 @@ export type EmbeddedPiRunResult = {
|
173 | 173 | isError?: boolean; |
174 | 174 | isReasoning?: boolean; |
175 | 175 | audioAsVoice?: boolean; |
| 176 | +trustedLocalMedia?: boolean; |
176 | 177 | channelData?: Record<string, unknown>; |
177 | 178 | }>; |
178 | 179 | meta: EmbeddedPiRunMeta; |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。