From 62b697c90e185bb935f9e2c8dc566e866d474f24 Mon Sep 17 00:00:00 2001 From: jam Date: Mon, 23 Mar 2026 09:35:22 +0100 Subject: [PATCH] fix(claude-code): extract assistant text from nested message.content in stream() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude CLI ≥2.x emits type=assistant events where the response text is inside message.content[{"type":"text","text":"..."}] rather than a flat content string. The old handler only checked event.content, so every token was silently dropped and streaming always returned an empty response. The handler now checks the flat content field first (backward-compatible), then falls back to joining all text blocks from message.content[]. Refs: RightNow-AI/openfang#295 --- .../src/drivers/claude_code.rs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/openfang-runtime/src/drivers/claude_code.rs b/crates/openfang-runtime/src/drivers/claude_code.rs index a7efae1d..d3820a04 100644 --- a/crates/openfang-runtime/src/drivers/claude_code.rs +++ b/crates/openfang-runtime/src/drivers/claude_code.rs @@ -524,11 +524,30 @@ impl LlmDriver for ClaudeCodeDriver { Ok(event) => { match event.r#type.as_str() { "content" | "text" | "assistant" | "content_block_delta" => { - if let Some(ref content) = event.content { - full_text.push_str(content); + // Older CLI: flat `content` string. + // CLI ≥2.x (type=assistant): text is nested in + // `message.content[].text`; the flat `content` + // field is absent or null. + let chunk = event.content.clone().unwrap_or_default(); + let nested: String = event + .message + .as_ref() + .map(|msg| { + msg.content + .iter() + .filter(|b| b.block_type == "text") + .map(|b| b.text.as_str()) + .collect::>() + .join("") + }) + .unwrap_or_default(); + let text_chunk = + if !chunk.is_empty() { chunk } else { nested }; + if !text_chunk.is_empty() { + full_text.push_str(&text_chunk); let _ = tx .send(StreamEvent::TextDelta { - text: content.clone(), + text: text_chunk, }) .await; }