From 6f84e83c27caec3b41993abae388d231a1f78f03 Mon Sep 17 00:00:00 2001 From: Ruben Date: Sat, 21 Feb 2026 04:43:24 +0100 Subject: [PATCH] fix: handle array content blocks in messagesToPrompt Clients like OpenClaw send message content as an array of content blocks (e.g. [{type: "text", text: "hello"}]) rather than a plain string. The previous code pushed the array directly into parts[], which caused JavaScript to coerce it to "[object Object]" when joining. - Add OpenAIContentBlock type and update OpenAIChatMessage.content to accept string | OpenAIContentBlock[] - Add extractTextContent() to normalize both formats before use - Apply extractTextContent() across all roles in messagesToPrompt --- src/adapter/openai-to-cli.ts | 24 ++++++++++++++++++++---- src/types/openai.ts | 8 +++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/adapter/openai-to-cli.ts b/src/adapter/openai-to-cli.ts index c8ecaa1..67914f5 100644 --- a/src/adapter/openai-to-cli.ts +++ b/src/adapter/openai-to-cli.ts @@ -2,7 +2,7 @@ * Converts OpenAI chat request format to Claude CLI input */ -import type { OpenAIChatRequest } from "../types/openai.js"; +import type { OpenAIChatRequest, OpenAIContentBlock } from "../types/openai.js"; export type ClaudeModel = "opus" | "sonnet" | "haiku"; @@ -46,6 +46,21 @@ export function extractModel(model: string): ClaudeModel { return "opus"; } +/** + * Extract plain text from a message content field. + * Handles both plain strings and OpenAI-style content block arrays + * (e.g. [{type: "text", text: "hello"}]), which clients like OpenClaw send. + */ +function extractTextContent(content: string | OpenAIContentBlock[]): string { + if (typeof content === "string") return content; + return content + .filter((block): block is OpenAIContentBlock & { type: "text"; text: string } => + block.type === "text" && typeof block.text === "string" + ) + .map((block) => block.text) + .join("\n"); +} + /** * Convert OpenAI messages array to a single prompt string for Claude CLI * @@ -56,20 +71,21 @@ export function messagesToPrompt(messages: OpenAIChatRequest["messages"]): strin const parts: string[] = []; for (const msg of messages) { + const text = extractTextContent(msg.content); switch (msg.role) { case "system": // System messages become context instructions - parts.push(`\n${msg.content}\n\n`); + parts.push(`\n${text}\n\n`); break; case "user": // User messages are the main prompt - parts.push(msg.content); + parts.push(text); break; case "assistant": // Previous assistant responses for context - parts.push(`\n${msg.content}\n\n`); + parts.push(`\n${text}\n\n`); break; } } diff --git a/src/types/openai.ts b/src/types/openai.ts index c116658..f991b64 100644 --- a/src/types/openai.ts +++ b/src/types/openai.ts @@ -3,9 +3,15 @@ * Used for Clawdbot integration */ +export interface OpenAIContentBlock { + type: "text" | "image_url"; + text?: string; + image_url?: { url: string }; +} + export interface OpenAIChatMessage { role: "system" | "user" | "assistant"; - content: string; + content: string | OpenAIContentBlock[]; } export interface OpenAIChatRequest {