diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 1cb7c55d0..38c3985a2 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Z.AI web search dropping sources and exposing raw JSON when MCP responses double-encode content text ([#8000](https://github.com/can1357/oh-my-pi/issues/8000)). + ## [17.2.11] - 2026-08-07 ### Added diff --git a/packages/coding-agent/src/web/search/providers/zai.ts b/packages/coding-agent/src/web/search/providers/zai.ts index ff8e98b92..39d052fa3 100644 --- a/packages/coding-agent/src/web/search/providers/zai.ts +++ b/packages/coding-agent/src/web/search/providers/zai.ts @@ -353,11 +353,20 @@ function parseSearchPayload(rawResult: unknown): { for (const part of content) { const text = isRecord(part) ? asString(part.text) : null; if (!text) continue; - textParts.push(text); try { - candidates.push(JSON.parse(text)); + let parsed: unknown = JSON.parse(text); + if (typeof parsed === "string") { + try { + parsed = JSON.parse(parsed); + } catch { + // The decoded string is answer text rather than another JSON payload. + } + } + candidates.push(parsed); + if (getSearchResults(parsed).length === 0) textParts.push(text); } catch { - // Not JSON payload; keep as fallback answer text. + // Non-JSON content is preserved as answer text. + textParts.push(text); } } } diff --git a/packages/coding-agent/test/web/search/zai.test.ts b/packages/coding-agent/test/web/search/zai.test.ts index 7c98466a0..27b0e54bd 100644 --- a/packages/coding-agent/test/web/search/zai.test.ts +++ b/packages/coding-agent/test/web/search/zai.test.ts @@ -9,7 +9,7 @@ interface CapturedRequest { } describe("Z.AI web search provider", () => { - it("initializes a Streamable HTTP MCP session before calling web_search_prime", async () => { + it("initializes a Streamable HTTP MCP session and parses doubly encoded results", async () => { const capturedRequests: CapturedRequest[] = []; const fetchImpl: FetchImpl = (_input, init) => { const request = { @@ -53,16 +53,20 @@ describe("Z.AI web search provider", () => { content: [ { type: "text", - text: JSON.stringify({ - search_result: [ + text: JSON.stringify( + JSON.stringify([ { title: "Z.AI search result", content: "Search result content", link: "https://example.com/zai", media: "Example", }, - ], - }), + ]), + ), + }, + { + type: "text", + text: "Plain prose answer.", }, ], }, @@ -107,6 +111,7 @@ describe("Z.AI web search provider", () => { author: "Example", }, ]); + expect(response.answer).toBe("Plain prose answer."); }); function createMcpFetch(): { fetchImpl: FetchImpl; capturedRequests: CapturedRequest[] } {