From f8c73a34288bab4b16ebf4c97d5a8b66178af3b0 Mon Sep 17 00:00:00 2001 From: chad gibson Date: Fri, 20 Mar 2026 22:28:10 -0700 Subject: [PATCH] fix: emit assistant update for tool-call-only messages from OpenAI-compatible providers When an OpenAI-compatible provider (e.g. vLLM) returns a response with only tool calls and no text content, handleMessageEnd silently skips emitting the assistant update event. This prevents the tool execution round-trip from completing, causing the session to go idle without executing the requested tools. The fix adds a check for toolCall content blocks alongside the existing text and media checks, ensuring tool-call-only responses are properly signalled to downstream handlers. Partially addresses #13603 --- src/agents/pi-embedded-subscribe.handlers.messages.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/agents/pi-embedded-subscribe.handlers.messages.ts b/src/agents/pi-embedded-subscribe.handlers.messages.ts index c3b4e92ba61..7aab04f5dd5 100644 --- a/src/agents/pi-embedded-subscribe.handlers.messages.ts +++ b/src/agents/pi-embedded-subscribe.handlers.messages.ts @@ -321,7 +321,15 @@ export function handleMessageEnd( } } - if (!ctx.state.emittedAssistantUpdate && (cleanedText || hasMedia)) { + // Tool-call-only responses from OpenAI-compatible providers (e.g. vLLM) may have + // no text or media content. Without this check, handleMessageEnd silently skips + // emitting the assistant update, which prevents downstream tool execution from + // being signalled. See: https://github.com/openclaw/openclaw/issues/13603 + const hasToolCalls = + Array.isArray(assistantMessage.content) && + assistantMessage.content.some((b: { type?: string }) => b.type === "toolCall"); + + if (!ctx.state.emittedAssistantUpdate && (cleanedText || hasMedia || hasToolCalls)) { const data = buildAssistantStreamData({ text: cleanedText, delta: cleanedText,