Skip to content

Commit 81c6943

Browse files
committed
🤖 fix: only send MUX_OUTPUT/MUX_PROMPT to LLM, not raw stdout
Change-Id: If5a22a790a0d8dc94255fd810c181de0c503059a Signed-off-by: Thomas Kosiewski <[email protected]>
1 parent c4af174 commit 81c6943

File tree

2 files changed

+110
-11
lines changed

2 files changed

+110
-11
lines changed

src/browser/utils/messages/modelMessageTransform.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -209,23 +209,42 @@ export function injectModeTransition(
209209
* - Preserves the rest of the message structure (id, role, other metadata)
210210
*/
211211
export function transformScriptMessagesForLLM(messages: MuxMessage[]): MuxMessage[] {
212-
return messages.map((msg) => {
212+
return messages.flatMap((msg) => {
213213
if (msg.metadata?.muxMetadata?.type !== "script-execution") {
214-
return msg;
214+
return [msg];
215215
}
216216

217217
const scriptMeta = msg.metadata.muxMetadata;
218-
const exitCode = scriptMeta.result.exitCode;
219-
const output = scriptMeta.result.output ?? "(no output)";
218+
const result = scriptMeta.result;
219+
// Only include the message if the script explicitly wrote to MUX_PROMPT (promptFile)
220+
// or MUX_OUTPUT (outputFile).
221+
// This respects the "Visible only to you" promise for stdout/stderr (result.output).
222+
const promptContent = result.promptFile?.trim();
223+
const outputContent = result.outputFile?.trim();
224+
const hasContent = (promptContent && promptContent.length > 0) || (outputContent && outputContent.length > 0);
220225

221-
// Construct a concise representation for the LLM
222-
const llmContent = `Script '${scriptMeta.scriptName}' executed (exit code ${exitCode}).\nStdout/Stderr:\n${output}`;
226+
if (hasContent) {
227+
let llmContent = `Script '${scriptMeta.scriptName}' executed (exit code ${result.exitCode}).`;
228+
229+
if (outputContent && outputContent.length > 0) {
230+
llmContent += `\nMUX_OUTPUT:\n${outputContent}`;
231+
}
232+
233+
if (promptContent && promptContent.length > 0) {
234+
llmContent += `\nMUX_PROMPT:\n${promptContent}`;
235+
}
223236

224-
return {
225-
...msg,
226-
parts: [{ type: "text", text: llmContent }],
227-
// Keep metadata for debugging but ensure downstream consumers use the new parts
228-
};
237+
return [
238+
{
239+
...msg,
240+
parts: [{ type: "text", text: llmContent }],
241+
// Keep metadata for debugging but ensure downstream consumers use the new parts
242+
},
243+
];
244+
}
245+
246+
// Otherwise, exclude from LLM context entirely
247+
return [];
229248
});
230249
}
231250

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { describe, it, expect } from "@jest/globals";
2+
import { transformScriptMessagesForLLM } from "./modelMessageTransform";
3+
import type { MuxMessage } from "@/common/types/message";
4+
5+
describe("transformScriptMessagesForLLM", () => {
6+
it("should remove script execution messages that have no promptFile content", () => {
7+
const messages: MuxMessage[] = [
8+
{
9+
id: "1",
10+
role: "user",
11+
parts: [{ type: "text", text: "Execute script" }],
12+
},
13+
{
14+
id: "script-1",
15+
role: "user",
16+
parts: [{ type: "text", text: "Executed script: /script test" }],
17+
metadata: {
18+
muxMetadata: {
19+
type: "script-execution",
20+
id: "script-1",
21+
historySequence: 0,
22+
timestamp: 123,
23+
command: "/script test",
24+
scriptName: "test.sh",
25+
args: [],
26+
result: {
27+
success: true,
28+
output: "some stdout output",
29+
exitCode: 0,
30+
wall_duration_ms: 100,
31+
} as any,
32+
},
33+
},
34+
},
35+
];
36+
37+
const result = transformScriptMessagesForLLM(messages);
38+
expect(result).toHaveLength(1);
39+
expect(result[0].id).toBe("1");
40+
});
41+
42+
it("should include script execution messages that HAVE promptFile content", () => {
43+
const messages: MuxMessage[] = [
44+
{
45+
id: "script-1",
46+
role: "user",
47+
parts: [{ type: "text", text: "Executed script: /script test" }],
48+
metadata: {
49+
muxMetadata: {
50+
type: "script-execution",
51+
id: "script-1",
52+
historySequence: 0,
53+
timestamp: 123,
54+
command: "/script test",
55+
scriptName: "test.sh",
56+
args: [],
57+
result: {
58+
success: true,
59+
output: "some stdout output",
60+
exitCode: 0,
61+
wall_duration_ms: 100,
62+
promptFile: "This is relevant for the LLM",
63+
} as any,
64+
},
65+
},
66+
},
67+
];
68+
69+
const result = transformScriptMessagesForLLM(messages);
70+
expect(result).toHaveLength(1);
71+
const textPart = result[0].parts[0];
72+
expect(textPart.type).toBe("text");
73+
if (textPart.type === "text") {
74+
expect(textPart.text).toContain("Script 'test.sh' executed");
75+
expect(textPart.text).toContain("Output:");
76+
expect(textPart.text).toContain("This is relevant for the LLM");
77+
expect(textPart.text).not.toContain("some stdout output");
78+
}
79+
});
80+
});

0 commit comments

Comments
 (0)