diff --git a/src/agent/oneshot.rs b/src/agent/oneshot.rs index 67a7497..10558f9 100644 --- a/src/agent/oneshot.rs +++ b/src/agent/oneshot.rs @@ -13,48 +13,6 @@ use crate::subconscious::{defs, prompts}; use std::fs; use std::path::PathBuf; -use std::sync::atomic::{AtomicPtr, Ordering}; - -// Global pid path for signal handler cleanup — stored as a leaked CString -// so the signal handler can unlink it without allocation. -static PID_CPATH: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); - -/// RAII guard that removes the pid file on drop (normal exit, panic). -struct PidGuard; - -impl Drop for PidGuard { - fn drop(&mut self) { - let ptr = PID_CPATH.swap(std::ptr::null_mut(), Ordering::SeqCst); - if !ptr.is_null() { - unsafe { libc::unlink(ptr); } - unsafe { drop(std::ffi::CString::from_raw(ptr)); } - } - } -} - -fn register_pid_cleanup(pid_path: &std::path::Path) { - let c_path = std::ffi::CString::new(pid_path.to_string_lossy().as_bytes()) - .expect("pid path contains null"); - let old = PID_CPATH.swap(c_path.into_raw(), Ordering::SeqCst); - if !old.is_null() { - unsafe { drop(std::ffi::CString::from_raw(old)); } - } - unsafe { - libc::signal(libc::SIGTERM, pid_cleanup_handler as *const () as libc::sighandler_t); - libc::signal(libc::SIGINT, pid_cleanup_handler as *const () as libc::sighandler_t); - } -} - -extern "C" fn pid_cleanup_handler(sig: libc::c_int) { - let ptr = PID_CPATH.swap(std::ptr::null_mut(), Ordering::SeqCst); - if !ptr.is_null() { - unsafe { libc::unlink(ptr); } - } - unsafe { - libc::signal(sig, libc::SIG_DFL); - libc::raise(sig); - } -} // --------------------------------------------------------------------------- // Agent execution @@ -80,7 +38,13 @@ pub fn run_one_agent( let def = defs::get_def(agent_name) .ok_or_else(|| format!("no .agent file for {}", agent_name))?; - let (state_dir, pid_path, _guard) = setup_agent_state(agent_name, &def)?; + // State dir for agent output files + let state_dir = std::env::var("POC_AGENT_OUTPUT_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| store::memory_dir().join("agent-output").join(agent_name)); + fs::create_dir_all(&state_dir) + .map_err(|e| format!("create state dir: {}", e))?; + unsafe { std::env::set_var("POC_AGENT_OUTPUT_DIR", &state_dir); } // Build prompt batch — either from explicit keys or the agent's query let agent_batch = if let Some(keys) = keys { @@ -153,7 +117,6 @@ pub fn run_one_agent( .map(|s| s.prompt.clone()).collect(); let step_phases: Vec = agent_batch.steps.iter() .map(|s| s.phase.clone()).collect(); - let step_phases_for_bail = step_phases.clone(); if std::env::var("POC_AGENT_VERBOSE").is_ok() { for (i, s) in agent_batch.steps.iter().enumerate() { @@ -165,14 +128,9 @@ pub fn run_one_agent( // Bail check: if the agent defines a bail script, run it between steps. let bail_script = def.bail.as_ref().map(|name| defs::agents_dir().join(name)); let state_dir_for_bail = state_dir.clone(); - let pid_path_for_bail = pid_path.clone(); let bail_fn = move |step_idx: usize| -> Result<(), String> { - if step_idx < step_phases_for_bail.len() { - fs::write(&pid_path_for_bail, &step_phases_for_bail[step_idx]).ok(); - } if let Some(ref script) = bail_script { let status = std::process::Command::new(script) - .arg(&pid_path_for_bail) .current_dir(&state_dir_for_bail) .status() .map_err(|e| format!("bail script {:?} failed: {}", script, e))?; @@ -197,33 +155,9 @@ pub fn run_one_agent( } // --------------------------------------------------------------------------- -// PID file management +// Process management — PID tracking and subprocess spawning // --------------------------------------------------------------------------- -fn setup_agent_state( - agent_name: &str, - def: &defs::AgentDef, -) -> Result<(PathBuf, PathBuf, PidGuard), String> { - let state_dir = std::env::var("POC_AGENT_OUTPUT_DIR") - .map(PathBuf::from) - .unwrap_or_else(|_| store::memory_dir().join("agent-output").join(agent_name)); - fs::create_dir_all(&state_dir) - .map_err(|e| format!("create state dir: {}", e))?; - unsafe { std::env::set_var("POC_AGENT_OUTPUT_DIR", &state_dir); } - - scan_pid_files(&state_dir, 0); - - let pid = std::process::id(); - let pid_path = state_dir.join(format!("pid-{}", pid)); - let first_phase = def.steps.first() - .map(|s| s.phase.as_str()) - .unwrap_or("step-0"); - fs::write(&pid_path, first_phase).ok(); - - register_pid_cleanup(&pid_path); - Ok((state_dir, pid_path, PidGuard)) -} - /// Check for live agent processes in a state dir. Returns (phase, pid) pairs. /// Cleans up stale pid files and kills timed-out processes. pub fn scan_pid_files(state_dir: &std::path::Path, timeout_secs: u64) -> Vec<(String, u32)> { @@ -260,10 +194,6 @@ pub fn scan_pid_files(state_dir: &std::path::Path, timeout_secs: u64) -> Vec<(St live } -// --------------------------------------------------------------------------- -// Subprocess spawning -// --------------------------------------------------------------------------- - pub struct SpawnResult { pub child: std::process::Child, pub log_path: PathBuf,