diff --git a/app/src/ai/bedrock/stream.rs b/app/src/ai/bedrock/stream.rs index dd5dd2b4..41c4f7fc 100644 --- a/app/src/ai/bedrock/stream.rs +++ b/app/src/ai/bedrock/stream.rs @@ -500,7 +500,7 @@ fn build_tool_call_message( api::message::tool_call::RunShellCommand { command, is_read_only: false, - uses_pager: false, + uses_pager: true, citations: vec![], is_risky: false, risk_category: 0, diff --git a/app/src/ai/blocklist/action_model/execute/shell_command.rs b/app/src/ai/blocklist/action_model/execute/shell_command.rs index a244d627..64931d75 100644 --- a/app/src/ai/blocklist/action_model/execute/shell_command.rs +++ b/app/src/ai/blocklist/action_model/execute/shell_command.rs @@ -236,9 +236,16 @@ impl ShellCommandExecutor { .active_block() .is_active_and_long_running() { - // If there is an active block, we can't execute another command. + // Another command is still running (e.g. stuck in a pager). Return an error + // result so the model receives feedback and can adapt. Using Completed with a + // non-zero exit code ensures a follow-up request is triggered. return ActionExecution::Sync(AIAgentActionResultType::RequestCommandOutput( - RequestCommandOutputResult::CancelledBeforeExecution, + RequestCommandOutputResult::Completed { + command: command.clone(), + block_id: model.block_list().active_block().id().clone(), + output: "Error: Cannot execute command because another command is still running in the terminal.".to_string(), + exit_code: ExitCode::from(1), + }, )); } // If the command might use pager and can't be interacted with, diff --git a/app/src/ai/blocklist/controller.rs b/app/src/ai/blocklist/controller.rs index 4b20cf2e..3200b227 100644 --- a/app/src/ai/blocklist/controller.rs +++ b/app/src/ai/blocklist/controller.rs @@ -2417,7 +2417,11 @@ impl BlocklistAIController { log::warn!("Conversation not found."); return; }; - let new_exchange_ids = conversation.new_exchange_ids_for_response(&stream_id); + let new_exchange_ids: Vec<_> = conversation.new_exchange_ids_for_response(&stream_id).collect(); + log::info!( + "[bedrock-debug] AfterStreamFinished: stream_id={:?}, conversation_id={:?}, new_exchange_ids count={}", + stream_id, conversation_id, new_exchange_ids.len() + ); let mut was_passive_request = false; let mut is_any_exchange_unfinished = false; let mut actions_to_queue = vec![]; @@ -2429,12 +2433,30 @@ impl BlocklistAIController { }; was_passive_request |= exchange.has_passive_request(); is_any_exchange_unfinished |= !exchange.output_status.is_finished(); + log::info!( + "[bedrock-debug] AfterStreamFinished: exchange_id={:?}, is_finished={}, output_status={:?}", + new_exchange_id, + exchange.output_status.is_finished(), + std::mem::discriminant(&exchange.output_status) + ); if let AIAgentOutputStatus::Finished { finished_output: FinishedAIAgentOutput::Success { output }, .. } = &exchange.output_status { + let action_count = output.get().actions().count(); + let msg_count = output.get().messages.len(); + log::info!( + "[bedrock-debug] AfterStreamFinished: output has {} messages, {} actions", + msg_count, action_count + ); + for msg in output.get().messages.iter() { + log::info!( + "[bedrock-debug] AfterStreamFinished: msg type={:?}", + std::mem::discriminant(&msg.message) + ); + } actions_to_queue.extend(output.get().actions().cloned()); } } @@ -2484,9 +2506,18 @@ impl BlocklistAIController { ); }); } else if !actions_to_queue.is_empty() { + log::info!( + "[bedrock-debug] AfterStreamFinished: queuing {} actions", + actions_to_queue.len() + ); self.action_model.update(ctx, |action_model, ctx| { action_model.queue_actions(actions_to_queue, conversation_id, ctx); }); + } else { + log::warn!( + "[bedrock-debug] AfterStreamFinished: NO actions to queue, was_passive={}, is_any_unfinished={}", + was_passive_request, is_any_exchange_unfinished + ); } // Cancelled streams will handle pending_response_stream updates synchronously.