fix: filter codex ReasoningSummary stderr noise on exit code 0
Codex writes informational ERROR messages about ReasoningSummaryDelta to stderr even on successful execution (exit code 0). The OpenClaw agent misinterprets this non-empty stderr as a failure. - Add filterStderrNoise() to strip known codex noise patterns from stderr when exit code is 0 - Preserve raw stderr in DebugInfo.rawStderr when --debug is active - Add 5 new tests covering noise filtering, preservation on failure, debug raw output, and non-codex client passthrough
This commit is contained in:
@@ -362,4 +362,83 @@ describe("executePrompt", () => {
|
||||
});
|
||||
assert.strictEqual(debugInfos[0].noisySuccess, false);
|
||||
});
|
||||
|
||||
it("filters codex ReasoningSummary noise from stderr on exit code 0", async () => {
|
||||
const noisyStderr = [
|
||||
'2026-05-20T18:33:01.969310Z ERROR codex_core::util: ReasoningSummaryPartAdded without active item',
|
||||
'2026-05-20T18:33:03.281713Z ERROR codex_core::util: ReasoningSummaryDelta without active item',
|
||||
'2026-05-20T18:33:03.348247Z ERROR codex_core::util: ReasoningSummaryDelta without active item',
|
||||
].join('\n');
|
||||
const scenarios = new Map<string, MockScenario>([
|
||||
["codex exec --yolo hello", { stdout: "Hello world!", stderr: noisyStderr, exitCode: 0 }],
|
||||
]);
|
||||
const result = await executePrompt("codex", "hello", {
|
||||
spawn: mockSpawn(scenarios),
|
||||
existsSync: () => true,
|
||||
});
|
||||
assert.strictEqual(result.exitCode, 0);
|
||||
assert.strictEqual(result.stderr, "");
|
||||
assert.strictEqual(result.stdout, "Hello world!");
|
||||
});
|
||||
|
||||
it("preserves real error stderr from codex on non-zero exit code", async () => {
|
||||
const noisyStderr = [
|
||||
'2026-05-20T18:33:01.969310Z ERROR codex_core::util: ReasoningSummaryDelta without active item',
|
||||
'Error: something actually went wrong',
|
||||
].join('\n');
|
||||
const scenarios = new Map<string, MockScenario>([
|
||||
["codex exec --yolo fail", { stdout: "", stderr: noisyStderr, exitCode: 1 }],
|
||||
]);
|
||||
const result = await executePrompt("codex", "fail", {
|
||||
spawn: mockSpawn(scenarios),
|
||||
existsSync: () => true,
|
||||
});
|
||||
assert.strictEqual(result.exitCode, 1);
|
||||
assert.ok(result.stderr.includes("ReasoningSummaryDelta"));
|
||||
assert.ok(result.stderr.includes("something actually went wrong"));
|
||||
});
|
||||
|
||||
it("provides rawStderr in debug info when noise is filtered", async () => {
|
||||
const noisyStderr = '2026-05-20T18:33:01.969310Z ERROR codex_core::util: ReasoningSummaryDelta without active item\n';
|
||||
const scenarios = new Map<string, MockScenario>([
|
||||
["codex exec --yolo hello", { stdout: "ok", stderr: noisyStderr, exitCode: 0 }],
|
||||
]);
|
||||
const debugInfos: any[] = [];
|
||||
const result = await executePrompt("codex", "hello", {
|
||||
spawn: mockSpawn(scenarios),
|
||||
existsSync: () => true,
|
||||
debug: true,
|
||||
onDebug: (info) => debugInfos.push(info),
|
||||
});
|
||||
assert.strictEqual(result.exitCode, 0);
|
||||
assert.strictEqual(result.stderr, "");
|
||||
assert.strictEqual(debugInfos[0].rawStderr, noisyStderr);
|
||||
});
|
||||
|
||||
it("does not set rawStderr when no noise filtering occurred", async () => {
|
||||
const scenarios = new Map<string, MockScenario>([
|
||||
["codex exec --yolo hello", { stdout: "ok", stderr: "", exitCode: 0 }],
|
||||
]);
|
||||
const debugInfos: any[] = [];
|
||||
await executePrompt("codex", "hello", {
|
||||
spawn: mockSpawn(scenarios),
|
||||
existsSync: () => true,
|
||||
debug: true,
|
||||
onDebug: (info) => debugInfos.push(info),
|
||||
});
|
||||
assert.strictEqual(debugInfos[0].rawStderr, undefined);
|
||||
});
|
||||
|
||||
it("does not filter stderr for non-codex clients", async () => {
|
||||
const noisyStderr = '2026-05-20T18:33:01.969310Z ERROR codex_core::util: ReasoningSummaryDelta without active item\n';
|
||||
const scenarios = new Map<string, MockScenario>([
|
||||
["claude -p hello --dangerously-skip-permissions", { stdout: "ok", stderr: noisyStderr, exitCode: 0 }],
|
||||
]);
|
||||
const result = await executePrompt("claude", "hello", {
|
||||
spawn: mockSpawn(scenarios),
|
||||
existsSync: () => true,
|
||||
});
|
||||
assert.strictEqual(result.exitCode, 0);
|
||||
assert.strictEqual(result.stderr, noisyStderr);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user