@@ -323,7 +323,7 @@ impl AgentCore {
323323 if response. message . has_tool_use ( ) {
324324 let tool_uses = response. message . get_tool_uses ( ) ;
325325
326- for tool_use in tool_uses {
326+ for tool_use in & tool_uses {
327327 if let crate :: llm:: ContentBlock :: ToolUse { id, name, input } = tool_use {
328328 // Display tool execution based on output mode
329329 let tool_call = crate :: tools:: ToolCall {
@@ -730,6 +730,53 @@ impl AgentCore {
730730 . push ( LlmMessage :: system ( self . get_system_prompt ( project_path) ) ) ;
731731 }
732732
733+ // Check if the last message was an assistant message with tool calls
734+ // If so, we need to ensure there's a corresponding tool result
735+ let needs_synthetic_results = if let Some ( last_msg) = self . conversation_history . last ( ) {
736+ matches ! ( last_msg. role, crate :: llm:: MessageRole :: Assistant ) && last_msg. has_tool_use ( )
737+ } else {
738+ false
739+ } ;
740+
741+ if needs_synthetic_results {
742+ // Clone the last message to avoid borrow issues
743+ let last_msg = self . conversation_history . last ( ) . unwrap ( ) . clone ( ) ;
744+
745+ // The last assistant message has tool calls without results
746+ // This can happen if the previous task was interrupted or failed
747+ // Add a synthetic error result to maintain conversation validity
748+ let tool_uses = last_msg. get_tool_uses ( ) ;
749+ let mut error_results = Vec :: new ( ) ;
750+
751+ for tool_use in & tool_uses {
752+ if let crate :: llm:: ContentBlock :: ToolUse { id, .. } = tool_use {
753+ let error_result = LlmMessage {
754+ role : crate :: llm:: MessageRole :: Tool ,
755+ content : crate :: llm:: MessageContent :: MultiModal ( vec ! [
756+ crate :: llm:: ContentBlock :: ToolResult {
757+ tool_use_id: id. clone( ) ,
758+ is_error: Some ( true ) ,
759+ content: "Previous task interrupted or incomplete" . to_string( ) ,
760+ } ,
761+ ] ) ,
762+ metadata : None ,
763+ } ;
764+ error_results. push ( error_result) ;
765+ }
766+ }
767+
768+ // Now add all error results to the conversation history
769+ for error_result in error_results {
770+ self . conversation_history . push ( error_result) ;
771+ }
772+
773+ if !tool_uses. is_empty ( ) {
774+ tracing:: warn!(
775+ "Added synthetic tool results for incomplete tool calls from previous task"
776+ ) ;
777+ }
778+ }
779+
733780 // Add user message with task
734781 let user_message = build_user_message ( task) ;
735782 self . conversation_history
@@ -1068,16 +1115,19 @@ mod tests {
10681115 _options : Option < ChatOptions > ,
10691116 ) -> Result < LlmResponse > {
10701117 // Check if this is a continuation after tool result
1071- let has_tool_result = messages. iter ( ) . any ( |msg| {
1072- matches ! ( msg . role , crate :: llm :: MessageRole :: Tool )
1073- } ) ;
1118+ let has_tool_result = messages
1119+ . iter ( )
1120+ . any ( |msg| matches ! ( msg . role , crate :: llm :: MessageRole :: Tool ) ) ;
10741121
10751122 // If we have a tool result, return a simple text response
10761123 if has_tool_result {
10771124 Ok ( LlmResponse {
10781125 message : LlmMessage {
10791126 role : MessageRole :: Assistant ,
1080- content : MessageContent :: Text ( "Understood, the tool execution failed but I can continue." . to_string ( ) ) ,
1127+ content : MessageContent :: Text (
1128+ "Understood, the tool execution failed but I can continue."
1129+ . to_string ( ) ,
1130+ ) ,
10811131 metadata : None ,
10821132 } ,
10831133 usage : None ,
@@ -1092,7 +1142,7 @@ mod tests {
10921142 role : MessageRole :: Assistant ,
10931143 content : MessageContent :: MultiModal ( vec ! [ ContentBlock :: ToolUse {
10941144 id: "test_id" . to_string( ) ,
1095- name: "bash" . to_string( ) , // Use a real tool that can fail
1145+ name: "bash" . to_string( ) , // Use a real tool that can fail
10961146 input: serde_json:: json!( {
10971147 "command" : "/nonexistent/command/that/will/fail"
10981148 } ) ,
@@ -1119,17 +1169,15 @@ mod tests {
11191169 // Create agent with default tools (including bash)
11201170 let agent_config = AgentConfig {
11211171 max_steps : 5 ,
1122- tools : vec ! [ "bash" . to_string( ) ] , // Enable bash tool
1172+ tools : vec ! [ "bash" . to_string( ) ] , // Enable bash tool
11231173 ..Default :: default ( )
11241174 } ;
11251175
11261176 let tool_registry = crate :: tools:: ToolRegistry :: default ( ) ;
11271177 let tool_executor = tool_registry. create_executor ( & agent_config. tools ) ;
11281178
1129- let conversation_manager = ConversationManager :: new (
1130- 8192 ,
1131- std:: sync:: Arc :: new ( ToolCallLlmClient ) ,
1132- ) ;
1179+ let conversation_manager =
1180+ ConversationManager :: new ( 8192 , std:: sync:: Arc :: new ( ToolCallLlmClient ) ) ;
11331181
11341182 let ( ac, reg) = crate :: agent:: AbortController :: new ( ) ;
11351183
@@ -1171,14 +1219,20 @@ mod tests {
11711219 false
11721220 }
11731221 } ) ;
1174- assert ! ( has_error_tool_result, "Should have error tool result in history" ) ;
1222+ assert ! (
1223+ has_error_tool_result,
1224+ "Should have error tool result in history"
1225+ ) ;
11751226
11761227 // Execute second task - this should not fail with API error about missing tool results
11771228 let result2 = agent
11781229 . execute_task_with_context ( "Test task 2" , & project_path)
11791230 . await ;
11801231
11811232 // Should succeed without API errors about missing tool results
1182- assert ! ( result2. is_ok( ) , "Second task should execute without API errors" ) ;
1233+ assert ! (
1234+ result2. is_ok( ) ,
1235+ "Second task should execute without API errors"
1236+ ) ;
11831237 }
11841238}
0 commit comments