use std::time::{SystemTime, UNIX_EPOCH};
use super::DaemonState;
use super::session_claudes::ResumeGrant;
use crate::core::session::SessionId;
use crate::core::twin_identity::ClaudeProcess;
impl DaemonState {
pub fn grant_resume_of(
&self,
record: &crate::session_manager::SessionRecord,
) -> Option<(SessionId, ResumeGrant)> {
self.grant_resumed_session(
record.claude_session_id.as_deref(),
&record.tmux_name,
record.pane_id.as_deref(),
)
}
pub fn revoke_resume_grant(&self, granted: Option<(SessionId, ResumeGrant)>) {
if let Some((session, grant)) = granted {
self.session_claudes.revoke_resume(session, &grant);
}
}
pub fn grant_resumed_session(
&self,
claude_session_id: Option<&str>,
tmux_name: &str,
pane_id: Option<&str>,
) -> Option<(SessionId, ResumeGrant)> {
let session = claude_session_id
.and_then(|id| uuid::Uuid::parse_str(id).ok())
.map(SessionId)?;
let issued_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| d.as_secs());
let grant = ResumeGrant {
tmux_name: tmux_name.to_string(),
pane_id: pane_id.map(str::to_string),
issued_at,
};
self.session_claudes.grant_resume(session, grant.clone());
Some((session, grant))
}
pub(crate) fn rebind_resumed_claude_with(
&self,
session: SessionId,
claude: ClaudeProcess,
pane_claude: impl Fn(&ResumeGrant) -> Result<ClaudeProcess, String>,
) -> Result<(), String> {
let claudes = &self.session_claudes;
let grant = claudes
.resume_grant(session)
.ok_or("the daemon has not resumed this session")?;
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| d.as_secs());
if !grant.holds_reap(now) {
claudes.revoke_resume(session, &grant);
tracing::warn!(
session = ?session,
issued_at = grant.issued_at,
"dropped an expired resume grant; nothing rebound (#9010)"
);
return Err(format!(
"the daemon's resume grant for {} expired before the claude announced",
grant.tmux_name
));
}
if claude.start_time < grant.issued_at {
return Err(format!(
"claude pid {} started before the daemon resumed the session",
claude.pid
));
}
let launched = pane_claude(&grant).map_err(|e| {
format!(
"the claude in the daemon's pane for {} could not be found: {e}",
grant.tmux_name
)
})?;
if launched != claude {
return Err(format!(
"claude pid {} is not the claude the daemon resumed in {} (pid {})",
claude.pid, grant.tmux_name, launched.pid
));
}
claudes.rebind(session, claude)?;
claudes.revoke_resume(session, &grant);
Ok(())
}
}
pub(crate) fn pane_claude(grant: &ResumeGrant) -> Result<ClaudeProcess, String> {
let pane = grant.pane_id.as_deref().ok_or(
"the managed record has no pane id, so its claude cannot be told from a sibling's",
)?;
let pid = crate::core::process::find_claude_pid_in_pane(&grant.tmux_name, pane)
.ok_or("no claude runs in the pane")?;
let facts = crate::core::twin_arming::process_facts(pid)?;
Ok(ClaudeProcess {
pid,
start_time: facts.start_time,
})
}