agents/cli: unwrap nested claude result json

This commit is contained in:
Alec Hrdina
2026-04-14 21:20:20 +00:00
committed by Ayaan Zaidi
parent c3833f7729
commit 015e39e3cf
2 changed files with 86 additions and 2 deletions

View File

@@ -125,6 +125,33 @@ describe("parseCliJson", () => {
});
});
it("unwraps nested Claude result JSON from JSON output", () => {
const result = parseCliJson(
JSON.stringify({
session_id: "session-nested-json",
result: JSON.stringify({
type: "result",
result: JSON.stringify({
type: "result",
subtype: "success",
result: "actual response text",
}),
}),
}),
{
command: "claude",
output: "json",
sessionIdFields: ["session_id"],
},
);
expect(result).toEqual({
text: "actual response text",
sessionId: "session-nested-json",
usage: undefined,
});
});
it("parses nested OpenAI-style cached token details from CLI json payloads", () => {
const result = parseCliJson(
JSON.stringify({
@@ -295,6 +322,38 @@ describe("parseCliJsonl", () => {
});
});
it("unwraps nested Claude agent result JSON from stream-json output", () => {
const result = parseCliJsonl(
[
JSON.stringify({ type: "init", session_id: "session-nested-jsonl" }),
JSON.stringify({
type: "result",
session_id: "session-nested-jsonl",
result: JSON.stringify({
type: "result",
result: JSON.stringify({
type: "result",
subtype: "success",
result: "actual response text",
}),
}),
}),
].join("\n"),
{
command: "claude",
output: "jsonl",
sessionIdFields: ["session_id"],
},
"claude-cli",
);
expect(result).toEqual({
text: "actual response text",
sessionId: "session-nested-jsonl",
usage: undefined,
});
});
it("parses multiple JSON objects embedded on the same line", () => {
const result = parseCliJsonl(
'{"type":"init","session_id":"session-999"} {"type":"result","session_id":"session-999","result":"done"}',

View File

@@ -193,6 +193,31 @@ function collectCliText(value: unknown): string {
return "";
}
function unwrapNestedCliResultText(raw: string): string {
let text = raw;
for (let depth = 0; depth < 8; depth += 1) {
const trimmed = text.trim();
if (!trimmed.startsWith("{")) {
return text;
}
try {
const parsed = JSON.parse(trimmed);
if (
!isRecord(parsed) ||
typeof parsed.type !== "string" ||
parsed.type !== "result" ||
typeof parsed.result !== "string"
) {
return text;
}
text = parsed.result;
} catch {
return text;
}
}
return text;
}
function collectExplicitCliErrorText(parsed: Record<string, unknown>): string {
const nested = readNestedErrorMessage(parsed);
if (nested) {
@@ -260,7 +285,7 @@ export function parseCliJson(raw: string, backend: CliBackendConfig): CliOutput
collectCliText(parsed.result) ||
collectCliText(parsed.response) ||
collectCliText(parsed);
const trimmedText = nextText.trim();
const trimmedText = unwrapNestedCliResultText(nextText).trim();
if (trimmedText) {
text = trimmedText;
sawStructuredOutput = true;
@@ -292,7 +317,7 @@ function parseClaudeCliJsonlResult(params: {
params.parsed.type === "result" &&
typeof params.parsed.result === "string"
) {
const resultText = params.parsed.result.trim();
const resultText = unwrapNestedCliResultText(params.parsed.result).trim();
if (resultText) {
return { text: resultText, sessionId: params.sessionId, usage: params.usage };
}