use std::path::{Path, PathBuf};
use super::probe::{LockProbe, Probe, WriterProbe};
use super::streaming::latest_step_dir;
use super::terminal::last_segment_complete;
use super::{INBOX_DIR, STEPS_DIR};
pub(super) const RESPONSE_FILE: &str = "response.json";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AgentState {
Live,
InFlight,
Quiescent,
Stopped,
}
pub(super) fn classify(
workspace: &Path,
agent_id: &str,
lock: &dyn LockProbe,
writer: &dyn WriterProbe,
) -> (AgentState, bool) {
let inbox_dir = workspace.join(INBOX_DIR).join(agent_id);
match lock.lock_state(&inbox_dir) {
Probe::Held => live_substate(workspace, agent_id, writer),
Probe::Free => (framing_state(workspace, agent_id), false),
Probe::Unknown => (framing_state(workspace, agent_id), true),
}
}
fn live_substate(workspace: &Path, agent_id: &str, writer: &dyn WriterProbe) -> (AgentState, bool) {
match latest_response_path(workspace, agent_id) {
Some(path) => match writer.writer_state(&path) {
Probe::Held => (AgentState::InFlight, false),
Probe::Free => (AgentState::Live, false),
Probe::Unknown => (AgentState::Live, true),
},
None => (AgentState::Live, false),
}
}
fn framing_state(workspace: &Path, agent_id: &str) -> AgentState {
if latest_response_complete(workspace, agent_id) {
AgentState::Quiescent
} else {
AgentState::Stopped
}
}
fn latest_response_path(workspace: &Path, agent_id: &str) -> Option<PathBuf> {
let steps = workspace.join(STEPS_DIR).join(agent_id);
Some(latest_step_dir(&steps)?.join(RESPONSE_FILE))
}
fn latest_response_complete(workspace: &Path, agent_id: &str) -> bool {
let Some(path) = latest_response_path(workspace, agent_id) else {
return false;
};
match std::fs::read(&path) {
Ok(bytes) => last_segment_complete(&bytes),
Err(_) => false,
}
}