diff --git a/scripts/e2e/lib/agent-turn-output.mjs b/scripts/e2e/lib/agent-turn-output.mjs index 2c2bec0f1eb4..12838da9a470 100644 --- a/scripts/e2e/lib/agent-turn-output.mjs +++ b/scripts/e2e/lib/agent-turn-output.mjs @@ -67,6 +67,19 @@ function parseJson(text) { } } +function isJsonObjectRecordStart(text, index) { + for (let cursor = index - 1; cursor >= 0; cursor -= 1) { + const char = text[cursor]; + if (char === "\n" || char === "\r") { + return true; + } + if (char !== " " && char !== "\t") { + return false; + } + } + return true; +} + function parseJsonObjectsFromText(text) { const payloads = []; let start = -1; @@ -77,7 +90,7 @@ function parseJsonObjectsFromText(text) { for (let index = 0; index < text.length; index += 1) { const char = text[index]; if (start === -1) { - if (char === "{") { + if (char === "{" && isJsonObjectRecordStart(text, index)) { start = index; depth = 1; inString = false; diff --git a/test/scripts/e2e-agent-turn-output.test.ts b/test/scripts/e2e-agent-turn-output.test.ts index d014d962c780..de6263c714ad 100644 --- a/test/scripts/e2e-agent-turn-output.test.ts +++ b/test/scripts/e2e-agent-turn-output.test.ts @@ -80,6 +80,26 @@ describe("scripts/e2e/lib/agent-turn-output", () => { } }); + it("does not accept reply-shaped JSON embedded in diagnostic lines", () => { + const dir = mkdtempSync(join(tmpdir(), "openclaw-e2e-agent-output-")); + try { + const outputPath = join(dir, "agent.log"); + writeFileSync( + outputPath, + [ + `echo ${JSON.stringify({ payloads: [{ text: "OPENCLAW_E2E_OK_DIAGNOSTIC" }] })}`, + JSON.stringify({ payloads: [{ text: "real reply without marker" }] }), + ].join("\n"), + ); + + expect(() => + assertAgentReplyContainsMarker("OPENCLAW_E2E_OK_DIAGNOSTIC", outputPath), + ).toThrow(/agent reply payload did not contain marker/u); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }); + it("does not accept markers that only appear in error payload text", () => { const dir = mkdtempSync(join(tmpdir(), "openclaw-e2e-agent-output-")); try {