Merge PR #8002: fix(web-search): parse double-encoded Z.AI results (@roboomp)

This commit is contained in:
can1357
2026-08-08 19:38:32 +02:00
3 changed files with 23 additions and 8 deletions
+1
View File
@@ -10,6 +10,7 @@
- Fixed task subagent assistant turns being omitted from the per-model TPS/TTFT aggregates shown by `/models`. ([#8022](https://github.com/can1357/oh-my-pi/issues/8022))
- Fixed terminal-title spinner writes consuming CPU during WSL/ConPTY agent waits by using the same static working separator as native Windows ([#8012](https://github.com/can1357/oh-my-pi/issues/8012)).
- Fixed long-running sessions leaking memory for every completed keep-alive `task`/scout subagent: a disposed (parked) subagent's `AgentSession` stayed pinned through the lifecycle adoption record's reviver closure, and `dispose()` never released the message array, append-only provider transcript, session-manager entries, or the raw-SSE debug buffer, so heavy transcripts and captured provider wire frames accumulated for the process lifetime ([#8003](https://github.com/can1357/oh-my-pi/issues/8003)).
- 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
@@ -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);
}
}
}
@@ -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[] } {