-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Bug Description
When a user rejects a tool execution via the Reject keybinding (handle_reject() in actions.rs), the rejection result is never fed back to the LLM as a pending tool result. This means the agentic loop stalls after a rejection — the LLM never learns the tool was rejected and cannot adapt or try an alternative approach.
Location
src/cortex-tui/src/runner/event_loop/actions.rs, lines 218–245
Root Cause
handle_reject() calls self.app_state.update_tool_result() to update the UI status, but it never calls self.app_state.add_pending_tool_result() to queue the rejection for the next LLM turn. Compare with every other tool completion path in the codebase:
handle_tool_completedintools.rs:407-408→ callsadd_pending_tool_resulthandle_tool_failedintools.rs:528-529→ callsadd_pending_tool_resultcancel_question_promptinmouse.rs:542-547→ callsadd_pending_tool_resultsubmit_question_answersinmouse.rs:517-518→ callsadd_pending_tool_result- All subagent completion/failure paths → call
add_pending_tool_result
But handle_reject() only does:
async fn handle_reject(&mut self) -> Result<()> {
if let Some(approval) = self.app_state.reject() {
self.app_state.update_tool_result(
&approval.tool_call_id,
"Tool execution rejected by user".to_string(),
false,
"Rejected".to_string(),
);
// Legacy bridge handling...
// MISSING: self.app_state.add_pending_tool_result(...)
// MISSING: continuation check (continue_with_tool_results)
}
Ok(())
}Reproduction Steps
- Start a conversation that triggers a tool requiring approval (e.g., Edit, Write, Execute)
- When the approval prompt appears, press the Reject key
- Observe that the conversation stalls — the LLM never receives the rejection and cannot continue
Expected Behavior
After rejecting a tool, the rejection message should be sent back to the LLM as a tool result (with success: false), allowing the agentic loop to continue. The LLM can then acknowledge the rejection and try an alternative approach.
Fix
Add add_pending_tool_result and a continuation check after the rejection:
async fn handle_reject(&mut self) -> Result<()> {
if let Some(approval) = self.app_state.reject() {
self.app_state.update_tool_result(
&approval.tool_call_id,
"Tool execution rejected by user".to_string(),
false,
"Rejected".to_string(),
);
// Feed rejection back to LLM for agentic loop continuation
self.app_state.add_pending_tool_result(
approval.tool_call_id.clone(),
approval.tool_name.clone(),
"Tool execution rejected by user".to_string(),
false,
);
// Continue agentic loop if no other tools are running
if self.running_tool_tasks.is_empty() && self.running_subagents.is_empty() {
if self.app_state.has_pending_tool_results() {
let _ = self.continue_with_tool_results().await;
}
}
// Legacy bridge handling...
}
Ok(())
}Impact
Any time a user rejects a tool during an agentic conversation, the entire conversation becomes stuck. The user must manually send a new message to resume interaction. This defeats the purpose of the approval/rejection flow in the agentic loop.
Version
v0.1.0