aboutsummaryrefslogtreecommitdiff
path: root/src/persist
diff options
context:
space:
mode:
Diffstat (limited to 'src/persist')
-rw-r--r--src/persist/restore.rs35
-rw-r--r--src/persist/snapshot.rs117
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,
},