diff options
Diffstat (limited to 'src/persist')
| -rw-r--r-- | src/persist/restore.rs | 35 | ||||
| -rw-r--r-- | src/persist/snapshot.rs | 117 |
2 files changed, 109 insertions, 43 deletions
diff --git a/src/persist/restore.rs b/src/persist/restore.rs index bd4d8382..2197e2bb 100644 --- a/src/persist/restore.rs +++ b/src/persist/restore.rs @@ -487,6 +487,9 @@ fn restore_tab( let saved_label = saved_pane.and_then(|p| p.label.clone()); let saved_agent_name = saved_pane.and_then(|p| p.agent_name.clone()); + let saved_managed_agent = saved_pane + .and_then(|pane| pane.managed_agent_kind.as_deref()) + .and_then(crate::detect::parse_canonical_agent_label); let saved_launch_argv = saved_pane.and_then(|p| p.launch_argv.clone()); let saved_agent_session = saved_pane.and_then(|p| p.agent_session.as_ref()); let saved_history = @@ -530,8 +533,12 @@ fn restore_tab( if let Some(label) = saved_label { terminal.set_manual_label(label); } - if let Some(agent_name) = saved_agent_name { - terminal.set_agent_name(agent_name); + match (saved_agent_name, saved_managed_agent) { + (Some(agent_name), Some(agent)) => { + terminal.restore_managed_agent(agent_name, agent) + } + (Some(agent_name), None) => terminal.set_agent_name(agent_name), + (None, _) => {} } if let Some(agent) = initial_restore_agent { let _ = terminal.set_detected_state_with_screen_signals_at( @@ -623,8 +630,13 @@ fn restore_tab( if let Some(label) = saved_label { terminal.set_manual_label(label); } - if let Some(agent_name) = saved_agent_name { - terminal.set_agent_name(agent_name); + match (saved_agent_name, saved_managed_agent) { + (Some(agent_name), Some(agent)) if was_imported => { + terminal.restore_managed_agent(agent_name, agent) + } + (Some(_), Some(_)) => {} + (Some(agent_name), None) => terminal.set_agent_name(agent_name), + (None, _) => {} } if let Some(agent) = initial_restore_agent { let _ = terminal.set_detected_state_with_screen_signals_at( @@ -1171,8 +1183,9 @@ mod tests { 0, super::super::snapshot::PaneSnapshot { cwd, - label: None, - agent_name: None, + label: Some("reviewer".into()), + agent_name: Some("reviewer".into()), + managed_agent_kind: Some("opencode".into()), agent_session: Some(super::super::snapshot::PaneAgentSessionSnapshot { source: "herdr:opencode".into(), agent: "opencode".into(), @@ -1218,6 +1231,8 @@ mod tests { !terminal.respawn_shell_on_exit, "agent sessions should not use native restore lifecycle when resume_agents_on_restore is disabled" ); + assert_eq!(terminal.agent_name, None); + assert_eq!(terminal.manual_label.as_deref(), Some("reviewer")); let session = terminal .persisted_agent_session .as_ref() @@ -1256,6 +1271,7 @@ mod tests { cwd: cwd.clone(), label: None, agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, @@ -1266,6 +1282,7 @@ mod tests { cwd: cwd.clone(), label: None, agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, @@ -1318,6 +1335,7 @@ mod tests { cwd: cwd.clone(), label: None, agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, @@ -1327,6 +1345,7 @@ mod tests { cwd: cwd.clone(), label: Some("planner".into()), agent_name: Some("planner".into()), + managed_agent_kind: None, agent_session: Some(super::super::snapshot::PaneAgentSessionSnapshot { source: "herdr:codex".into(), agent: "codex".into(), @@ -1416,6 +1435,8 @@ mod tests { assert_eq!(workspace.tabs[3].number, 5); assert_eq!(detail.tab_idx, 3); assert_eq!(detail.agent_label, "planner"); + let terminal_id = &workspace.tabs[3].panes[&agent_pane].attached_terminal_id; + assert_eq!(terminals[terminal_id].managed_agent_kind(), None); } #[test] @@ -1478,6 +1499,7 @@ mod tests { cwd, label: None, agent_name: None, + managed_agent_kind: None, agent_session: Some(super::super::snapshot::PaneAgentSessionSnapshot { source: "herdr:codex".into(), agent: "codex".into(), @@ -1643,6 +1665,7 @@ mod tests { cwd: cwd.clone(), label: None, agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, diff --git a/src/persist/snapshot.rs b/src/persist/snapshot.rs index f78fd836..e1e112a0 100644 --- a/src/persist/snapshot.rs +++ b/src/persist/snapshot.rs @@ -102,6 +102,8 @@ pub struct PaneSnapshot { #[serde(default, skip_serializing_if = "Option::is_none")] pub agent_name: Option<String>, #[serde(default, skip_serializing_if = "Option::is_none")] + pub managed_agent_kind: Option<String>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub agent_session: Option<PaneAgentSessionSnapshot>, #[serde(default, skip_serializing_if = "Option::is_none")] pub launch_argv: Option<Vec<String>>, @@ -319,51 +321,51 @@ fn capture_tab( let cwd = tab .cwd_for_pane(*id, terminals, terminal_runtimes) .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| "/".into())); - let label = tab - .panes - .get(id) - .and_then(|pane| terminals.get(&pane.attached_terminal_id)) - .and_then(|terminal| terminal.manual_label.clone()); - let agent_name = tab + let terminal = tab .panes .get(id) - .and_then(|pane| terminals.get(&pane.attached_terminal_id)) - .and_then(|terminal| terminal.agent_name.clone()); - let launch_argv = tab - .panes - .get(id) - .and_then(|pane| terminals.get(&pane.attached_terminal_id)) - .and_then(|terminal| terminal.launch_argv.clone()); - let agent_session = - tab.panes - .get(id) - .and_then(|pane| terminals.get(&pane.attached_terminal_id)) - .and_then(|terminal| { - if let Some(authority) = terminal.hook_authority.as_ref() { - if let Some(session_ref) = authority.session_ref.as_ref() { - return Some(PaneAgentSessionSnapshot { - source: authority.source.clone(), - agent: authority.agent_label.clone(), - kind: session_ref.kind, - value: session_ref.value.clone(), - }); - } - } - terminal.persisted_agent_session.as_ref().map(|session| { - PaneAgentSessionSnapshot { - source: session.source.clone(), - agent: session.agent.clone(), - kind: session.session_ref.kind, - value: session.session_ref.value.clone(), - } - }) - }); + .and_then(|pane| terminals.get(&pane.attached_terminal_id)); + let label = terminal.and_then(|terminal| terminal.manual_label.clone()); + let (agent_name, managed_agent_kind) = terminal + .filter(|terminal| !terminal.managed_agent_launch_pending()) + .map(|terminal| { + ( + terminal.agent_name.clone(), + terminal + .managed_agent_kind() + .map(|agent| crate::detect::agent_label(agent).to_string()), + ) + }) + .unwrap_or_default(); + let launch_argv = terminal.and_then(|terminal| terminal.launch_argv.clone()); + let agent_session = terminal.and_then(|terminal| { + if let Some(authority) = terminal.hook_authority.as_ref() { + if let Some(session_ref) = authority.session_ref.as_ref() { + return Some(PaneAgentSessionSnapshot { + source: authority.source.clone(), + agent: authority.agent_label.clone(), + kind: session_ref.kind, + value: session_ref.value.clone(), + }); + } + } + terminal + .persisted_agent_session + .as_ref() + .map(|session| PaneAgentSessionSnapshot { + source: session.source.clone(), + agent: session.agent.clone(), + kind: session.session_ref.kind, + value: session.session_ref.value.clone(), + }) + }); panes.insert( id.raw(), PaneSnapshot { cwd, label, agent_name, + managed_agent_kind, agent_session, launch_argv, }, @@ -557,6 +559,43 @@ mod tests { } #[test] + fn managed_agent_snapshot_omits_pending_and_persists_active_ownership() { + let mut state = state_with_workspaces(&["managed-snapshot"]); + let root = state.workspaces[0].tabs[0].root_pane; + let terminal_id = state.workspaces[0].tabs[0].panes[&root] + .attached_terminal_id + .clone(); + let now = std::time::Instant::now(); + state + .terminals + .get_mut(&terminal_id) + .unwrap() + .begin_managed_agent( + "reviewer".into(), + crate::detect::Agent::Pi, + now, + std::time::Duration::ZERO, + std::time::Duration::from_secs(1), + ); + + let pending = capture_from_state(&state); + let pending_pane = &pending.workspaces[0].tabs[0].panes[&root.raw()]; + assert_eq!(pending_pane.agent_name, None); + assert_eq!(pending_pane.managed_agent_kind, None); + + let terminal = state.terminals.get_mut(&terminal_id).unwrap(); + terminal.set_detected_state( + Some(crate::detect::Agent::Pi), + crate::detect::AgentState::Idle, + ); + assert!(terminal.reconcile_managed_agent_at(now, false)); + let active = capture_from_state(&state); + let active_pane = &active.workspaces[0].tabs[0].panes[&root.raw()]; + assert_eq!(active_pane.agent_name.as_deref(), Some("reviewer")); + assert_eq!(active_pane.managed_agent_kind.as_deref(), Some("pi")); + } + + #[test] fn round_trip_empty_session() { let snap = SessionSnapshot { version: SNAPSHOT_VERSION, @@ -606,6 +645,7 @@ mod tests { cwd: PathBuf::from("/home/can/Projects/herdr"), label: None, agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, @@ -616,6 +656,7 @@ mod tests { cwd: PathBuf::from("/home/can/Projects/website"), label: Some("website".into()), agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, @@ -1157,6 +1198,7 @@ mod tests { cwd: PathBuf::from("/tmp/this-directory-does-not-exist-for-herdr-test"), label: None, agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, @@ -1169,6 +1211,7 @@ mod tests { .unwrap_or_else(|_| PathBuf::from("/tmp")), label: None, agent_name: None, + managed_agent_kind: None, agent_session: None, launch_argv: None, }, |
