use serde_json::{json, Value};
use crate::agent::Agent;
pub async fn try_load_session(agent: &Agent, sid: &str, cwd: &str) -> Result<Value, String> {
const ATTEMPTS: u32 = 6;
const BACKOFF: std::time::Duration = std::time::Duration::from_millis(250);
let mut last_err = String::new();
for attempt in 0..ATTEMPTS {
let res = agent
.request(
"session/load",
json!({
"sessionId": sid,
"cwd": cwd,
"mcpServers": []
}),
)
.await;
match res {
Ok(value) => return Ok(value),
Err(e) => {
last_err = format!("{e}");
if !is_stale_lock_error(&last_err) {
break;
}
let stole = steal_stale_session_lock(sid);
if stole {
eprintln!(
"Session {sid}: stale lock stolen on attempt {}.",
attempt + 1
);
continue;
}
if attempt + 1 < ATTEMPTS {
tokio::time::sleep(BACKOFF).await;
}
}
}
}
Err(last_err)
}
pub fn is_stale_lock_error(msg: &str) -> bool {
msg.contains("Session is active in another process")
}
pub fn steal_stale_session_lock(session_id: &str) -> bool {
let Ok(home) = std::env::var("HOME") else {
return false;
};
let path = std::path::PathBuf::from(home).join(format!(".kiro/sessions/cli/{session_id}.lock"));
let Ok(raw) = std::fs::read_to_string(&path) else {
return false;
};
let Ok(parsed) = serde_json::from_str::<Value>(&raw) else {
return false;
};
let Some(pid) = parsed.get("pid").and_then(Value::as_i64) else {
return false;
};
if pid_is_alive(pid as i32) {
return false;
}
match std::fs::remove_file(&path) {
Ok(()) => {
eprintln!(
"Stole stale Kiro session lock (pid {pid}): {}",
path.display()
);
true
}
Err(_) => false,
}
}
#[cfg(unix)]
pub fn pid_is_alive(pid: i32) -> bool {
crate::unix::send_signal(pid, 0) == 0
}
#[cfg(not(unix))]
pub fn pid_is_alive(_pid: i32) -> bool {
true
}
pub fn extract_session_info(result: &Value) -> Option<Value> {
let modes = result.get("modes").cloned();
let models = result.get("models").cloned();
if modes.is_none() && models.is_none() {
return None;
}
Some(json!({
"modes": modes,
"models": models
}))
}
pub fn short_reason(msg: &str) -> String {
if let Some(start) = msg.find("\"data\":\"") {
let rest = &msg[start + 8..];
if let Some(end) = rest.find('"') {
return rest[..end].to_string();
}
}
msg.trim_start_matches("agent error: ").trim().to_string()
}