From 1b27d907d900a2815be47d7eb581936d0c097a76 Mon Sep 17 00:00:00 2001 From: ricardo Date: Thu, 29 Jan 2026 11:27:39 +0100 Subject: [PATCH] fix(cli): handle Claude CLI JSON array output format Claude CLI with --output-format json returns a JSON array containing: 1. init message (type: "system", subtype: "init") 2. assistant response (type: "assistant") 3. result object (type: "result", with "result" field containing actual text) parseCliJson only handled single JSON objects. When it encountered an array, isRecord() returned false, the function returned null, and the fallback returned raw stdout as text - sending the entire JSON blob to users instead of the extracted response. Fixes telegram/chat responses showing raw JSON when using claude-cli backend. --- src/agents/cli-runner/helpers.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/agents/cli-runner/helpers.ts b/src/agents/cli-runner/helpers.ts index 833771a1f..b5bce7d76 100644 --- a/src/agents/cli-runner/helpers.ts +++ b/src/agents/cli-runner/helpers.ts @@ -282,6 +282,35 @@ export function parseCliJson(raw: string, backend: CliBackendConfig): CliOutput } catch { return null; } + + // Handle JSON array output (Claude CLI with --output-format json returns an array) + if (Array.isArray(parsed)) { + let sessionId: string | undefined; + let usage: CliUsage | undefined; + let text = ""; + + for (const item of parsed) { + if (!isRecord(item)) continue; + + // Extract session_id from any item that has it + if (!sessionId) sessionId = pickSessionId(item, backend); + + // Look for the result object (final output) + if (item.type === "result" && typeof item.result === "string") { + text = item.result; + if (isRecord(item.usage)) { + usage = toUsage(item.usage) ?? usage; + } + break; // Use first result object found + } + } + + if (text) { + return { text: text.trim(), sessionId, usage }; + } + return null; + } + if (!isRecord(parsed)) return null; const sessionId = pickSessionId(parsed, backend); const usage = isRecord(parsed.usage) ? toUsage(parsed.usage) : undefined;