diff options
Diffstat (limited to 'crates/dinggerd/src/watcher.rs')
| -rw-r--r-- | crates/dinggerd/src/watcher.rs | 46 |
1 files changed, 32 insertions, 14 deletions
diff --git a/crates/dinggerd/src/watcher.rs b/crates/dinggerd/src/watcher.rs index 9fd999d..d352ab2 100644 --- a/crates/dinggerd/src/watcher.rs +++ b/crates/dinggerd/src/watcher.rs @@ -6,6 +6,7 @@ use std::collections::HashMap; use detect_core::{detect_agent_with_osc, AgentState}; use tracing::{debug, info, warn}; +use crate::hooks; use crate::notify::{play_sound, send_notification, NotificationKind}; use crate::tmux; @@ -93,6 +94,12 @@ impl Watcher { // Forget panes that went away. self.panes .retain(|pane_id, _| panes.iter().any(|pane| pane.pane_id == *pane_id)); + hooks::forget( + &panes + .iter() + .map(|pane| pane.pane_id.clone()) + .collect::<Vec<_>>(), + ); for pane in &panes { let prev_state = self @@ -102,27 +109,34 @@ impl Watcher { .unwrap_or(AgentState::Unknown); // The detection result is an observation of live agent state. - // Viewer screens (transcripts, pickers) return + // Agent-reported state (hook receiver) is authoritative while the + // pane runs the agent; screen detection is the fallback for panes + // that never report. Viewer screens (transcripts, pickers) return // `skip_state_update: true` — they say nothing about state and // leave the previous one standing (herdr's publishing contract). let observed = if pane.dead { Some(AgentState::Idle) } else if let Some(agent) = pane.agent { - let screen = match tmux::capture_pane(&pane.pane_id) { - Ok(screen) => screen, - Err(err) => { - warn!(target = %pane.target, %err, "capture failed"); - continue; - } - }; - // tmux pane_title carries the last OSC title the application - // set; tmux does not re-expose OSC progress sequences. - let detection = - detect_agent_with_osc(Some(agent), &screen, &pane.pane_title, ""); - (!detection.skip_state_update).then_some(detection.state) + if let Some(state) = hooks::hook_state(&pane.pane_id) { + Some(state) + } else { + let screen = match tmux::capture_pane(&pane.pane_id) { + Ok(screen) => screen, + Err(err) => { + warn!(target = %pane.target, %err, "capture failed"); + continue; + } + }; + // tmux pane_title carries the last OSC title the application + // set; tmux does not re-expose OSC progress sequences. + let detection = + detect_agent_with_osc(Some(agent), &screen, &pane.pane_title, ""); + (!detection.skip_state_update).then_some(detection.state) + } } else { // A pane that stops running an agent (agent quit into a - // shell) is no longer an observation. + // shell) is no longer an observation; a stale hook report + // from the dead agent is not authoritative. Some(AgentState::Unknown) }; @@ -130,6 +144,10 @@ impl Watcher { debug!(target = %pane.target, "observation skipped (viewer screen)"); continue; }; + if new_state == AgentState::Unknown { + // The agent is gone; its last hook report must not outlive it. + hooks::drop_report(&pane.pane_id); + } // Keep the pane's agent label current even when the state is // unchanged (e.g. an agent swap between two idle panes). |
