Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions crates/goose-cli/src/session/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ mod tests {
let tool_request = ToolRequest {
id: "test-id".to_string(),
tool_call: Ok(tool_call),
thought_signature: None,
metadata: None,
};

let result = tool_request_to_markdown(&tool_request, true);
Expand All @@ -560,7 +560,7 @@ mod tests {
let tool_request = ToolRequest {
id: "test-id".to_string(),
tool_call: Ok(tool_call),
thought_signature: None,
metadata: None,
};

let result = tool_request_to_markdown(&tool_request, true);
Expand All @@ -580,6 +580,7 @@ mod tests {
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "test-id".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand All @@ -605,6 +606,7 @@ mod tests {
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "test-id".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -698,7 +700,7 @@ mod tests {
let tool_request = ToolRequest {
id: "shell-cat".to_string(),
tool_call: Ok(tool_call),
thought_signature: None,
metadata: None,
};

let python_code = r#"#!/usr/bin/env python3
Expand All @@ -716,6 +718,7 @@ if __name__ == "__main__":
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "shell-cat".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -750,7 +753,7 @@ if __name__ == "__main__":
let tool_request = ToolRequest {
id: "git-status".to_string(),
tool_call: Ok(git_status_call),
thought_signature: None,
metadata: None,
};

let git_output = " M src/main.rs\n?? temp.txt\n A new_feature.rs";
Expand All @@ -762,6 +765,7 @@ if __name__ == "__main__":
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "git-status".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -794,7 +798,7 @@ if __name__ == "__main__":
let _tool_request = ToolRequest {
id: "cargo-build".to_string(),
tool_call: Ok(cargo_build_call),
thought_signature: None,
metadata: None,
};

let build_output = r#" Compiling goose-cli v0.1.0 (/Users/user/goose)
Expand All @@ -816,6 +820,7 @@ warning: unused variable `x`
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "cargo-build".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -844,7 +849,7 @@ warning: unused variable `x`
let _tool_request = ToolRequest {
id: "curl-api".to_string(),
tool_call: Ok(curl_call),
thought_signature: None,
metadata: None,
};

let api_response = r#"{
Expand All @@ -868,6 +873,7 @@ warning: unused variable `x`
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "curl-api".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -898,7 +904,7 @@ warning: unused variable `x`
let tool_request = ToolRequest {
id: "editor-write".to_string(),
tool_call: Ok(editor_call),
thought_signature: None,
metadata: None,
};

let text_content = TextContent {
Expand All @@ -909,6 +915,7 @@ warning: unused variable `x`
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "editor-write".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -944,7 +951,7 @@ warning: unused variable `x`
let _tool_request = ToolRequest {
id: "editor-view".to_string(),
tool_call: Ok(editor_call),
thought_signature: None,
metadata: None,
};

let python_code = r#"import os
Expand All @@ -971,6 +978,7 @@ def process_data(data: List[Dict]) -> List[Dict]:
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "editor-view".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -999,7 +1007,7 @@ def process_data(data: List[Dict]) -> List[Dict]:
let _tool_request = ToolRequest {
id: "shell-error".to_string(),
tool_call: Ok(error_call),
thought_signature: None,
metadata: None,
};

let error_output = r#"python: can't open file 'nonexistent_script.py': [Errno 2] No such file or directory
Expand All @@ -1013,6 +1021,7 @@ Command failed with exit code 2"#;
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "shell-error".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -1040,7 +1049,7 @@ Command failed with exit code 2"#;
let tool_request = ToolRequest {
id: "script-exec".to_string(),
tool_call: Ok(script_call),
thought_signature: None,
metadata: None,
};

let script_output = r#"Python 3.11.5 (main, Aug 24 2023, 15:18:16) [Clang 14.0.3 ]
Expand All @@ -1058,6 +1067,7 @@ Command failed with exit code 2"#;
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "script-exec".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -1092,7 +1102,7 @@ Command failed with exit code 2"#;
let _tool_request = ToolRequest {
id: "multi-cmd".to_string(),
tool_call: Ok(multi_call),
thought_signature: None,
metadata: None,
};

let multi_output = r#"total 24
Expand All @@ -1110,6 +1120,7 @@ drwx------ 3 user staff 96 Dec 6 16:20 com.apple.launchd.abc
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "multi-cmd".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -1142,7 +1153,7 @@ drwx------ 3 user staff 96 Dec 6 16:20 com.apple.launchd.abc
let tool_request = ToolRequest {
id: "grep-search".to_string(),
tool_call: Ok(grep_call),
thought_signature: None,
metadata: None,
};

let grep_output = r#"src/main.rs:15:async fn process_request(req: Request) -> Result<Response> {
Expand All @@ -1158,6 +1169,7 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "grep-search".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -1191,7 +1203,7 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
let _tool_request = ToolRequest {
id: "json-test".to_string(),
tool_call: Ok(tool_call),
thought_signature: None,
metadata: None,
};

let json_output = r#"{"status": "success", "data": {"count": 42}}"#;
Expand All @@ -1203,6 +1215,7 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "json-test".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down Expand Up @@ -1231,7 +1244,7 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
let tool_request = ToolRequest {
id: "npm-install".to_string(),
tool_call: Ok(npm_call),
thought_signature: None,
metadata: None,
};

let npm_output = r#"added 57 packages, and audited 58 packages in 3s
Expand All @@ -1249,6 +1262,7 @@ found 0 vulnerabilities"#;
annotations: None,
};
let tool_response = ToolResponse {
metadata: None,
id: "npm-install".to_string(),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
Expand Down
35 changes: 30 additions & 5 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ use crate::context_mgmt::{
check_if_compaction_needed, compact_messages, DEFAULT_COMPACTION_THRESHOLD,
};
use crate::conversation::message::{
ActionRequiredData, Message, MessageContent, SystemNotificationType, ToolRequest,
ActionRequiredData, Message, MessageContent, ProviderMetadata, SystemNotificationType,
ToolRequest,
};
use crate::conversation::{debug_conversation_fix, fix_conversation, Conversation};
use crate::mcp_utils::ToolResult;
Expand Down Expand Up @@ -359,14 +360,15 @@ impl Agent {
for request in &permission_check_result.denied {
if let Some(response_msg) = request_to_response_map.get(&request.id) {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response(
*response = response.clone().with_tool_response_with_metadata(
request.id.clone(),
Ok(CallToolResult {
content: vec![rmcp::model::Content::text(DECLINED_RESPONSE)],
structured_content: None,
is_error: Some(true),
meta: None,
}),
request.metadata.as_ref(),
);
}
}
Expand Down Expand Up @@ -1081,8 +1083,10 @@ impl Agent {
.collect();

let mut request_to_response_map = HashMap::new();
let mut request_metadata: HashMap<String, Option<ProviderMetadata>> = HashMap::new();
for (idx, request) in frontend_requests.iter().chain(remaining_requests.iter()).enumerate() {
request_to_response_map.insert(request.id.clone(), tool_response_messages[idx].clone());
request_metadata.insert(request.id.clone(), request.metadata.clone());
}

for (idx, request) in frontend_requests.iter().enumerate() {
Expand All @@ -1100,14 +1104,15 @@ impl Agent {
for request in remaining_requests.iter() {
if let Some(response_msg) = request_to_response_map.get(&request.id) {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response(
*response = response.clone().with_tool_response_with_metadata(
request.id.clone(),
Ok(CallToolResult {
content: vec![Content::text(CHAT_MODE_TOOL_SKIPPED_RESPONSE)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
request.metadata.as_ref(),
);
}
}
Expand Down Expand Up @@ -1199,8 +1204,9 @@ impl Agent {
all_install_successful = false;
}
if let Some(response_msg) = request_to_response_map.get(&request_id) {
let metadata = request_metadata.get(&request_id).and_then(|m| m.as_ref());
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response(request_id, output);
*response = response.clone().with_tool_response_with_metadata(request_id, output, metadata);
}
}
ToolStreamItem::Message(msg) => {
Expand All @@ -1222,11 +1228,30 @@ impl Agent {
}
}

// Preserve thinking content from the original response
// Gemini (and other thinking models) require thinking to be echoed back
let thinking_content: Vec<MessageContent> = response.content.iter()
.filter(|c| matches!(c, MessageContent::Thinking(_)))
.cloned()
.collect();
if !thinking_content.is_empty() {
let thinking_msg = Message::new(
response.role.clone(),
response.created,
thinking_content,
).with_id(format!("msg_{}", Uuid::new_v4()));
messages_to_add.push(thinking_msg);
}

for (idx, request) in frontend_requests.iter().chain(remaining_requests.iter()).enumerate() {
if request.tool_call.is_ok() {
let request_msg = Message::assistant()
.with_id(format!("msg_{}", Uuid::new_v4()))
.with_tool_request(request.id.clone(), request.tool_call.clone());
.with_tool_request_with_metadata(
request.id.clone(),
request.tool_call.clone(),
request.metadata.as_ref(),
);
messages_to_add.push(request_msg);
let final_response = tool_response_messages[idx]
.lock().await.clone();
Expand Down
9 changes: 7 additions & 2 deletions crates/goose/src/agents/tool_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,15 @@ impl Agent {
// User declined - update the specific response message for this request
if let Some(response_msg) = request_to_response_map.get(&request.id) {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response(
*response = response.clone().with_tool_response_with_metadata(
request.id.clone(),
Ok(rmcp::model::CallToolResult {
content: vec![Content::text(DECLINED_RESPONSE)],
structured_content: None,
is_error: Some(true),
meta: None,
}),
request.metadata.as_ref(),
);
}
}
Expand Down Expand Up @@ -155,7 +156,11 @@ impl Agent {

if let Some((id, result)) = self.tool_result_rx.lock().await.recv().await {
let mut response = message_tool_response.lock().await;
*response = response.clone().with_tool_response(id, result);
*response = response.clone().with_tool_response_with_metadata(
id,
result,
tool_request.metadata.as_ref(),
);
}
}
}
Expand Down
Loading