diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-30 00:35:31 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-30 00:35:53 +0300 |
| commit | c5c307597f623099a86d847d4d6abe3c81791292 (patch) | |
| tree | e4a17a9f5b6bbc7b065aa3da7f236b8729bb21fd /src/terminal | |
| parent | 8b1d77acf6059e6688fb6a875c3176c95602b6c4 (diff) | |
fix: recover omp resume and blocked states
refs #879
Diffstat (limited to 'src/terminal')
| -rw-r--r-- | src/terminal/state.rs | 125 |
1 files changed, 124 insertions, 1 deletions
diff --git a/src/terminal/state.rs b/src/terminal/state.rs index 8fea0542..3e536e6e 100644 --- a/src/terminal/state.rs +++ b/src/terminal/state.rs @@ -692,6 +692,26 @@ impl TerminalState { .is_some_and(|(current, incoming)| current != incoming) } + fn same_owner_full_lifecycle_hook_authority_session_ref( + &self, + source: &str, + agent_label: &str, + session_ref: &crate::agent_resume::AgentSessionRef, + ) -> Option<crate::agent_resume::AgentSessionRef> { + let authority = self.hook_authority.as_ref()?; + if !crate::detect::full_lifecycle_hook_authority(&authority.source, &authority.agent_label) + || authority.source != source + || authority.agent_label != agent_label + { + return None; + } + authority + .session_ref + .as_ref() + .filter(|current| *current != session_ref) + .cloned() + } + fn clear_full_lifecycle_hook_suppression_for_detected_agent( &mut self, previous_detected_agent: Option<Agent>, @@ -851,6 +871,11 @@ impl TerminalState { "codex", Some("startup" | "clear" | "resume" | "compact") ) | ("herdr:opencode", "opencode", Some("new")) + | ( + "herdr:omp", + "omp", + Some("startup" | "new" | "resume" | "fork") + ) ) } @@ -886,6 +911,11 @@ impl TerminalState { if self.known_agent_label_conflicts_with_detected_agent(&agent_label) { return None; } + let session_replacement_allowed = Self::session_start_source_allows_session_replacement( + &source, + &agent_label, + session_start_source.as_deref(), + ); if self.current_session_owner_conflicts(&source, &agent_label) || self .conflicting_same_owner_session_ref( @@ -898,8 +928,29 @@ impl TerminalState { { return None; } + let replaced_hook_session = self.same_owner_full_lifecycle_hook_authority_session_ref( + &source, + &agent_label, + &session_ref, + ); + if replaced_hook_session.is_some() && !session_replacement_allowed { + return None; + } + let now = Instant::now(); + let previous_agent_label = self.effective_agent_label().map(str::to_string); + let previous_known_agent = self.effective_known_agent(); + let previous_state = self.state; + let previous_presentation = self.effective_presentation_for_state_at(previous_state, now); let previous_session = self.current_session_identity_for_persistence(); + if let Some(replaced_hook_session) = replaced_hook_session { + self.remember_stale_full_lifecycle_hook_session( + source.clone(), + agent_label.clone(), + replaced_hook_session, + ); + self.hook_authority = None; + } self.persisted_agent_session = Some(crate::agent_resume::PersistedAgentSession { source, agent: agent_label, @@ -907,7 +958,13 @@ impl TerminalState { }); let current_session = self.current_session_identity_for_persistence(); Some(TerminalStateMutation { - effective_state_change: None, + effective_state_change: self.recompute_effective_state( + previous_agent_label, + previous_known_agent, + previous_state, + previous_presentation, + now, + ), session_ref_changed: previous_session != current_session, }) } @@ -1334,6 +1391,72 @@ mod tests { } #[test] + fn omp_resume_session_report_reanchors_full_lifecycle_authority() { + let mut terminal = test_terminal(); + let old_session = test_session_path("omp-old.jsonl"); + let new_session = test_session_path("omp-new.jsonl"); + terminal.set_detected_state(Some(Agent::Omp), AgentState::Idle); + terminal.set_hook_authority_with_session_ref( + "herdr:omp".into(), + "omp".into(), + AgentState::Working, + None, + None, + crate::agent_resume::AgentSessionRef::path(old_session.clone()), + Some(10), + ); + + let session_report = terminal.set_agent_session_ref_for_session_start( + "herdr:omp".into(), + "omp".into(), + crate::agent_resume::AgentSessionRef::path(new_session.clone()), + Some(11), + Some("resume".into()), + ); + + assert!(session_report.is_some()); + assert!(terminal.hook_authority.is_none()); + assert_eq!( + terminal + .persisted_agent_session + .as_ref() + .unwrap() + .session_ref, + crate::agent_resume::AgentSessionRef::path(new_session.clone()).unwrap() + ); + + let blocked = terminal.set_hook_authority_with_session_ref( + "herdr:omp".into(), + "omp".into(), + AgentState::Blocked, + Some("waiting".into()), + None, + crate::agent_resume::AgentSessionRef::path(new_session.clone()), + Some(12), + ); + + assert!(blocked.is_some()); + assert_eq!(terminal.state, AgentState::Blocked); + assert_eq!( + terminal.hook_authority.as_ref().unwrap().session_ref, + crate::agent_resume::AgentSessionRef::path(new_session) + ); + + let stale = terminal.set_hook_authority_with_session_ref( + "herdr:omp".into(), + "omp".into(), + AgentState::Working, + None, + None, + crate::agent_resume::AgentSessionRef::path(old_session), + Some(13), + ); + + assert!(stale.is_none()); + assert_eq!(terminal.state, AgentState::Blocked); + } + + #[test] fn process_exit_clears_matching_full_lifecycle_hook_authority() { let now = Instant::now(); let mut terminal = test_terminal(); |
