From e499dc4b5ffe7705de78a743dd1690d3e605e193 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Fri, 20 Mar 2026 06:45:13 -0400 Subject: [PATCH 1/2] Fix apps tool: coerce string arguments from inner LLM responses When the apps extension calls an inner LLM to generate app content, the LLM sometimes returns numeric values as strings (e.g., "600" instead of 600 for window width). This caused serde deserialization to fail with 'invalid type: string "600", expected u32'. Normal tool calls already go through coerce_tool_arguments() in reply_parts.rs (via categorize_tool_requests), which handles this string-to-number coercion. The inner LLM tool calls in the apps extension bypassed this by deserializing directly from the raw response. Fix: reuse the existing coerce_tool_arguments() in extract_tool_response() so inner LLM responses get the same type coercion as normal tool calls. Closes #8028 Signed-off-by: Douwe Osinga --- .../src/agents/platform_extensions/apps.rs | 22 ++++++++++++++++--- crates/goose/src/agents/reply_parts.rs | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/goose/src/agents/platform_extensions/apps.rs b/crates/goose/src/agents/platform_extensions/apps.rs index 9301b114ba52..9571b75c422e 100644 --- a/crates/goose/src/agents/platform_extensions/apps.rs +++ b/crates/goose/src/agents/platform_extensions/apps.rs @@ -1,5 +1,6 @@ use crate::agents::extension::PlatformExtensionContext; use crate::agents::mcp_client::{Error, McpClientTrait}; +use crate::agents::reply_parts::coerce_tool_arguments; use crate::agents::tool_execution::ToolCallContext; use crate::config::paths::Paths; use crate::conversation::message::Message; @@ -284,7 +285,11 @@ impl AppsManagerClient { } } - extract_tool_response(&response, "create_app_content") + extract_tool_response( + &response, + "create_app_content", + &Self::schema::(), + ) } async fn generate_updated_app_content( @@ -323,7 +328,11 @@ impl AppsManagerClient { } } - extract_tool_response(&response, "update_app_content") + extract_tool_response( + &response, + "update_app_content", + &Self::schema::(), + ) } async fn handle_list_apps( @@ -652,7 +661,10 @@ fn extract_string(args: &JsonObject, key: &str) -> Result { fn extract_tool_response( response: &Message, tool_name: &str, + tool_schema: &JsonObject, ) -> Result { + let schema_value = serde_json::Value::Object(tool_schema.clone()); + for content in &response.content { if let crate::conversation::message::MessageContent::ToolRequest(tool_req) = content { if let Ok(tool_call) = &tool_req.tool_call { @@ -662,7 +674,10 @@ fn extract_tool_response( .as_ref() .ok_or("Missing tool call parameters")?; - return serde_json::from_value(serde_json::Value::Object(params.clone())) + let coerced = coerce_tool_arguments(Some(params.clone()), &schema_value) + .unwrap_or_else(|| params.clone()); + + return serde_json::from_value(serde_json::Value::Object(coerced)) .map_err(|e| format!("Failed to parse tool response: {}", e)); } } @@ -671,3 +686,4 @@ fn extract_tool_response( Err(format!("LLM did not call the required tool: {}", tool_name)) } + diff --git a/crates/goose/src/agents/reply_parts.rs b/crates/goose/src/agents/reply_parts.rs index 7046a729f416..00fb8337fe2e 100644 --- a/crates/goose/src/agents/reply_parts.rs +++ b/crates/goose/src/agents/reply_parts.rs @@ -96,7 +96,7 @@ fn try_coerce_boolean(s: &str) -> Value { } } -fn coerce_tool_arguments( +pub(crate) fn coerce_tool_arguments( arguments: Option>, tool_schema: &Value, ) -> Option> { From b1af6b415840ce26eddab897f7c10b77184317cb Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Fri, 20 Mar 2026 07:55:07 -0400 Subject: [PATCH 2/2] fmt --- crates/goose/src/agents/platform_extensions/apps.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/goose/src/agents/platform_extensions/apps.rs b/crates/goose/src/agents/platform_extensions/apps.rs index 9571b75c422e..e3e4644a9c52 100644 --- a/crates/goose/src/agents/platform_extensions/apps.rs +++ b/crates/goose/src/agents/platform_extensions/apps.rs @@ -686,4 +686,3 @@ fn extract_tool_response( Err(format!("LLM did not call the required tool: {}", tool_name)) } -