aboutsummaryrefslogtreecommitdiff
path: root/src/app/actions.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-17 23:55:08 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-17 23:59:56 +0300
commite0758c32118f2aa006db3d8fa4b41833fe6e7ead (patch)
tree2b29dd61ebf870db8d0ac95cc0b4515178c834c4 /src/app/actions.rs
parent040956531f673c8fb7720037494ed4e61b123c6c (diff)
feat: harden named agent cli workflows
refs #1525
Diffstat (limited to 'src/app/actions.rs')
-rw-r--r--src/app/actions.rs52
1 files changed, 47 insertions, 5 deletions
diff --git a/src/app/actions.rs b/src/app/actions.rs
index 3fedeeb3..b4f352b4 100644
--- a/src/app/actions.rs
+++ b/src/app/actions.rs
@@ -1,6 +1,8 @@
//! Pure state mutations on AppState.
//! These don't need channels, async, or PTY runtime.
+use std::time::Instant;
+
use tracing::{info, warn};
use crate::detect::{Agent, AgentState};
@@ -2583,7 +2585,7 @@ impl AppState {
observed_at,
} => self
.update_terminal_state(pane_id, |terminal| {
- Some(terminal.set_detected_state_with_screen_signals_at(
+ let mutation = terminal.set_detected_state_with_screen_signals_at(
agent,
state,
visible_blocker,
@@ -2591,7 +2593,11 @@ impl AppState {
visible_working,
process_exited,
observed_at,
- ))
+ );
+ if process_exited {
+ terminal.reconcile_managed_agent_at(observed_at, true);
+ }
+ Some(mutation)
})
.into_iter()
.collect(),
@@ -2753,11 +2759,13 @@ impl AppState {
.attached_terminal_id
.clone();
let previous_seen = self.workspaces[ws_idx].pane_state(pane_id)?.seen;
- let mutation = {
+ let (mutation, managed_changed) = {
let terminal = self.terminals.get_mut(&terminal_id)?;
- update(terminal)?
+ let mutation = update(terminal)?;
+ let managed_changed = terminal.reconcile_managed_agent_at(Instant::now(), false);
+ (mutation, managed_changed)
};
- if mutation.session_ref_changed {
+ if mutation.session_ref_changed || managed_changed {
self.mark_session_dirty();
}
let change = mutation.effective_state_change?;
@@ -2785,6 +2793,40 @@ impl AppState {
Some(update)
}
+ pub(crate) fn next_managed_agent_deadline(&self) -> Option<Instant> {
+ self.terminals
+ .values()
+ .filter_map(crate::terminal::TerminalState::next_managed_agent_deadline)
+ .min()
+ }
+
+ pub(crate) fn reconcile_managed_agents_at(&mut self, now: Instant) -> Vec<(usize, PaneId)> {
+ let mut changed_terminals = std::collections::HashSet::new();
+ for (terminal_id, terminal) in &mut self.terminals {
+ if terminal.reconcile_managed_agent_at(now, false) {
+ changed_terminals.insert(terminal_id.clone());
+ }
+ }
+ if changed_terminals.is_empty() {
+ return Vec::new();
+ }
+ self.mark_session_dirty();
+ self.workspaces
+ .iter()
+ .enumerate()
+ .flat_map(|(ws_idx, workspace)| {
+ let changed_terminals = &changed_terminals;
+ workspace.tabs.iter().flat_map(move |tab| {
+ tab.panes.iter().filter_map(move |(&pane_id, pane)| {
+ changed_terminals
+ .contains(&pane.attached_terminal_id)
+ .then_some((ws_idx, pane_id))
+ })
+ })
+ })
+ .collect()
+ }
+
pub(crate) fn publish_pane_process_exit_if_agent(
&mut self,
pane_id: PaneId,