aboutsummaryrefslogtreecommitdiff
path: root/src/terminal
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-30 00:35:31 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-30 00:35:53 +0300
commitc5c307597f623099a86d847d4d6abe3c81791292 (patch)
treee4a17a9f5b6bbc7b065aa3da7f236b8729bb21fd /src/terminal
parent8b1d77acf6059e6688fb6a875c3176c95602b6c4 (diff)
fix: recover omp resume and blocked states
refs #879
Diffstat (limited to 'src/terminal')
-rw-r--r--src/terminal/state.rs125
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();