parent
b7619db085
commit
048d8a2919
23
src/pane.rs
23
src/pane.rs
|
|
@ -413,6 +413,7 @@ fn spawn_basic_detection_task(
|
|||
let mut last_visible_idle = false;
|
||||
let mut last_visible_working = false;
|
||||
let mut last_visible_signal_refresh = None;
|
||||
let mut last_screen_working_at = None;
|
||||
let mut last_process_check = std::time::Instant::now();
|
||||
let mut last_foreground_pgid = None;
|
||||
let mut has_process_probe = false;
|
||||
|
|
@ -431,6 +432,7 @@ fn spawn_basic_detection_task(
|
|||
last_visible_idle = false;
|
||||
last_visible_working = false;
|
||||
last_visible_signal_refresh = None;
|
||||
last_screen_working_at = None;
|
||||
last_process_check = std::time::Instant::now();
|
||||
last_foreground_pgid = None;
|
||||
has_process_probe = false;
|
||||
|
|
@ -514,13 +516,23 @@ fn spawn_basic_detection_task(
|
|||
if agent_presence.observe_process_probe(new_agent) {
|
||||
agent = agent_presence.current_agent();
|
||||
agent_changed = previous_agent != agent;
|
||||
if agent_changed {
|
||||
last_screen_working_at = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(detection) = detection_update_for_publish(agent, &content, false) else {
|
||||
continue;
|
||||
};
|
||||
let new_state = detection.state;
|
||||
let new_state = crate::terminal::state::stabilize_agent_detection(
|
||||
agent,
|
||||
state,
|
||||
detection,
|
||||
false,
|
||||
now,
|
||||
&mut last_screen_working_at,
|
||||
);
|
||||
let visible_blocker = detection.visible_blocker && new_state == AgentState::Blocked;
|
||||
let visible_idle = detection.visible_idle && new_state == AgentState::Idle;
|
||||
let visible_working = detection.visible_working && new_state == AgentState::Working;
|
||||
|
|
@ -1529,7 +1541,7 @@ impl PaneRuntime {
|
|||
let mut foreground_shell_exit_reported = false;
|
||||
let mut release_was_active = false;
|
||||
let mut pending_restore_probe = initial_state.detected_agent.is_some();
|
||||
let mut last_claude_working_at = None;
|
||||
let mut last_screen_working_at = None;
|
||||
let mut last_visible_blocker = false;
|
||||
let mut last_visible_idle = false;
|
||||
let mut last_visible_working = false;
|
||||
|
|
@ -1562,7 +1574,7 @@ impl PaneRuntime {
|
|||
foreground_shell_exit_reported = false;
|
||||
release_was_active = false;
|
||||
pending_restore_probe = false;
|
||||
last_claude_working_at = None;
|
||||
last_screen_working_at = None;
|
||||
last_visible_blocker = false;
|
||||
last_visible_idle = false;
|
||||
last_visible_working = false;
|
||||
|
|
@ -1675,6 +1687,9 @@ impl PaneRuntime {
|
|||
}
|
||||
if changed {
|
||||
agent = agent_presence.current_agent();
|
||||
if agent != previous_agent {
|
||||
last_screen_working_at = None;
|
||||
}
|
||||
if let Some(process_name) = process_name {
|
||||
info!(
|
||||
pane = pane_id.raw(),
|
||||
|
|
@ -1722,7 +1737,7 @@ impl PaneRuntime {
|
|||
detection,
|
||||
process_exited,
|
||||
now,
|
||||
&mut last_claude_working_at,
|
||||
&mut last_screen_working_at,
|
||||
);
|
||||
let visible_blocker =
|
||||
detection.visible_blocker && new_state == AgentState::Blocked;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::terminal::TerminalId;
|
|||
mod metadata;
|
||||
pub use metadata::{AgentMetadata, AgentMetadataReport, EffectivePresentation};
|
||||
|
||||
const CLAUDE_WORKING_HOLD: Duration = Duration::from_millis(1200);
|
||||
const SCREEN_WORKING_HOLD: Duration = Duration::from_millis(1200);
|
||||
const STALE_HOOK_IDLE_GRACE: Duration = Duration::from_secs(2);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
@ -640,7 +640,7 @@ impl TerminalState {
|
|||
)
|
||||
.is_some_and(|(observed_at, reported_at)| {
|
||||
reported_at >= observed_at
|
||||
&& reported_at.duration_since(observed_at) < CLAUDE_WORKING_HOLD
|
||||
&& reported_at.duration_since(observed_at) < SCREEN_WORKING_HOLD
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -783,21 +783,21 @@ pub(crate) fn stabilize_agent_state(
|
|||
previous: AgentState,
|
||||
raw: AgentState,
|
||||
now: std::time::Instant,
|
||||
last_claude_working_at: &mut Option<std::time::Instant>,
|
||||
last_screen_working_at: &mut Option<std::time::Instant>,
|
||||
) -> AgentState {
|
||||
if agent != Some(Agent::Claude) {
|
||||
if !matches!(agent, Some(Agent::Claude | Agent::Droid)) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
match raw {
|
||||
AgentState::Working => {
|
||||
*last_claude_working_at = Some(now);
|
||||
*last_screen_working_at = Some(now);
|
||||
AgentState::Working
|
||||
}
|
||||
AgentState::Blocked => AgentState::Blocked,
|
||||
AgentState::Idle if previous == AgentState::Working => {
|
||||
if last_claude_working_at
|
||||
.is_some_and(|last_working| now.duration_since(last_working) < CLAUDE_WORKING_HOLD)
|
||||
if last_screen_working_at
|
||||
.is_some_and(|last_working| now.duration_since(last_working) < SCREEN_WORKING_HOLD)
|
||||
{
|
||||
AgentState::Working
|
||||
} else {
|
||||
|
|
@ -814,7 +814,7 @@ pub(crate) fn stabilize_agent_detection(
|
|||
detection: crate::detect::AgentDetection,
|
||||
process_exited: bool,
|
||||
now: std::time::Instant,
|
||||
last_claude_working_at: &mut Option<std::time::Instant>,
|
||||
last_screen_working_at: &mut Option<std::time::Instant>,
|
||||
) -> AgentState {
|
||||
if process_exited {
|
||||
return detection.state;
|
||||
|
|
@ -825,7 +825,7 @@ pub(crate) fn stabilize_agent_detection(
|
|||
previous,
|
||||
detection.state,
|
||||
now,
|
||||
last_claude_working_at,
|
||||
last_screen_working_at,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -871,7 +871,7 @@ mod tests {
|
|||
Some(Agent::Claude),
|
||||
AgentState::Working,
|
||||
AgentState::Idle,
|
||||
now + CLAUDE_WORKING_HOLD + std::time::Duration::from_millis(1),
|
||||
now + SCREEN_WORKING_HOLD + std::time::Duration::from_millis(1),
|
||||
&mut last_working,
|
||||
);
|
||||
assert_eq!(state, AgentState::Idle);
|
||||
|
|
@ -924,7 +924,84 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn non_claude_states_are_unchanged() {
|
||||
fn droid_working_is_sticky_for_short_gap() {
|
||||
let now = std::time::Instant::now();
|
||||
let mut last_working = None;
|
||||
|
||||
let working = stabilize_agent_state(
|
||||
Some(Agent::Droid),
|
||||
AgentState::Idle,
|
||||
AgentState::Working,
|
||||
now,
|
||||
&mut last_working,
|
||||
);
|
||||
assert_eq!(working, AgentState::Working);
|
||||
|
||||
let still_working = stabilize_agent_state(
|
||||
Some(Agent::Droid),
|
||||
AgentState::Working,
|
||||
AgentState::Idle,
|
||||
now + std::time::Duration::from_millis(400),
|
||||
&mut last_working,
|
||||
);
|
||||
assert_eq!(still_working, AgentState::Working);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn droid_transitions_to_idle_after_hold_expires() {
|
||||
let now = std::time::Instant::now();
|
||||
let mut last_working = Some(now);
|
||||
|
||||
let state = stabilize_agent_state(
|
||||
Some(Agent::Droid),
|
||||
AgentState::Working,
|
||||
AgentState::Idle,
|
||||
now + SCREEN_WORKING_HOLD + std::time::Duration::from_millis(1),
|
||||
&mut last_working,
|
||||
);
|
||||
assert_eq!(state, AgentState::Idle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_exit_idle_bypasses_droid_working_hold() {
|
||||
let now = std::time::Instant::now();
|
||||
let mut last_working = Some(now);
|
||||
|
||||
let state = stabilize_agent_detection(
|
||||
Some(Agent::Droid),
|
||||
AgentState::Working,
|
||||
AgentDetection {
|
||||
state: AgentState::Idle,
|
||||
skip_state_update: false,
|
||||
visible_blocker: false,
|
||||
visible_idle: false,
|
||||
visible_working: false,
|
||||
},
|
||||
true,
|
||||
now + std::time::Duration::from_millis(100),
|
||||
&mut last_working,
|
||||
);
|
||||
|
||||
assert_eq!(state, AgentState::Idle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn droid_blocked_bypasses_working_hold() {
|
||||
let now = std::time::Instant::now();
|
||||
let mut last_working = Some(now);
|
||||
|
||||
let state = stabilize_agent_state(
|
||||
Some(Agent::Droid),
|
||||
AgentState::Working,
|
||||
AgentState::Blocked,
|
||||
now + std::time::Duration::from_millis(100),
|
||||
&mut last_working,
|
||||
);
|
||||
assert_eq!(state, AgentState::Blocked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_states_are_unchanged_by_screen_working_hold() {
|
||||
let now = std::time::Instant::now();
|
||||
let mut last_working = None;
|
||||
|
||||
|
|
@ -1290,7 +1367,7 @@ mod tests {
|
|||
None,
|
||||
None,
|
||||
None,
|
||||
now + CLAUDE_WORKING_HOLD + Duration::from_millis(1),
|
||||
now + SCREEN_WORKING_HOLD + Duration::from_millis(1),
|
||||
);
|
||||
|
||||
assert_eq!(terminal.state, AgentState::Idle);
|
||||
|
|
@ -1325,7 +1402,7 @@ mod tests {
|
|||
Some("permission".into()),
|
||||
None,
|
||||
None,
|
||||
now + CLAUDE_WORKING_HOLD + Duration::from_millis(1),
|
||||
now + SCREEN_WORKING_HOLD + Duration::from_millis(1),
|
||||
);
|
||||
|
||||
assert_eq!(terminal.state, AgentState::Blocked);
|
||||
|
|
@ -1337,7 +1414,7 @@ mod tests {
|
|||
false,
|
||||
true,
|
||||
false,
|
||||
now + CLAUDE_WORKING_HOLD + Duration::from_millis(800),
|
||||
now + SCREEN_WORKING_HOLD + Duration::from_millis(800),
|
||||
);
|
||||
|
||||
assert_eq!(terminal.state, AgentState::Working);
|
||||
|
|
|
|||
Loading…
Reference in New Issue