use super::super::provider::{ChatMessage, Role};
use super::super::tool_types::ToolResultRecord;
use anyhow::Result;
pub fn repair_dangling_tool_call(
session_id: &str,
messages: &mut Vec<ChatMessage>,
) -> Result<bool> {
let Some(last) = messages.last() else {
return Ok(false);
};
let Some(call) = &last.tool_call else {
return Ok(false);
};
let record = ToolResultRecord {
call_id: call.id.clone(),
output: "interrupted — yana-rt exited (crash or force-quit) before this tool call \
was resolved; the actual outcome is unknown, do not assume it succeeded or \
failed"
.to_string(),
is_error: true,
denied: false,
};
super::append_tool_result(session_id, &record)?;
let mut msg = ChatMessage::text(Role::User, "");
msg.tool_result = Some(record);
messages.push(msg);
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::chat::tool_types::ToolCallRecord;
use uuid::Uuid;
fn unique_session_id() -> String {
Uuid::new_v4().to_string()
}
fn cleanup(session_id: &str) {
let _ = std::fs::remove_file(super::super::history_path(session_id));
}
#[test]
fn repairs_a_trailing_dangling_tool_call() {
let session_id = unique_session_id();
let call = ToolCallRecord {
id: "call_1".to_string(),
name: "run_command".to_string(),
arguments_json: "{\"command\":\"ls\"}".to_string(),
};
super::super::append_tool_call(&session_id, "anthropic", "claude-sonnet-4-6", &call)
.unwrap();
let mut messages = super::super::load(&session_id).unwrap();
assert_eq!(messages.len(), 1);
let repaired = repair_dangling_tool_call(&session_id, &mut messages).unwrap();
assert!(repaired);
assert_eq!(messages.len(), 2);
let result = messages[1].tool_result.as_ref().unwrap();
assert_eq!(result.call_id, "call_1");
assert!(result.is_error);
assert!(!result.denied);
let reloaded = super::super::load(&session_id).unwrap();
assert_eq!(reloaded.len(), 2);
assert!(!repair_dangling_tool_call(&session_id, &mut reloaded.clone()).unwrap());
cleanup(&session_id);
}
#[test]
fn does_not_touch_a_well_formed_session() {
let session_id = unique_session_id();
super::super::append_user(&session_id, "hi").unwrap();
super::super::append_assistant(
&session_id,
"anthropic",
"claude-sonnet-4-6",
"hello",
5,
5,
10,
false,
None,
)
.unwrap();
let mut messages = super::super::load(&session_id).unwrap();
assert!(!repair_dangling_tool_call(&session_id, &mut messages).unwrap());
assert_eq!(messages.len(), 2);
cleanup(&session_id);
}
#[test]
fn empty_history_is_not_a_dangling_call() {
let mut messages = Vec::new();
assert!(!repair_dangling_tool_call("nonexistent", &mut messages).unwrap());
}
}