aboutsummaryrefslogtreecommitdiff
path: root/src/persist/snapshot.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-23 21:27:26 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-23 21:37:05 +0300
commitdb67e9d74ffafc9c63bdf78fcf712419359f634b (patch)
tree9d181b55e65aaf6a2fad2d6cf785bb09a8d9748f /src/persist/snapshot.rs
parent1bab1d2ce21507747ea1f506fb891fe7cd5a13c9 (diff)
feat: restore agent sessions
refs #233
Diffstat (limited to 'src/persist/snapshot.rs')
-rw-r--r--src/persist/snapshot.rs109
1 files changed, 109 insertions, 0 deletions
diff --git a/src/persist/snapshot.rs b/src/persist/snapshot.rs
index 79d5c406..a223513d 100644
--- a/src/persist/snapshot.rs
+++ b/src/persist/snapshot.rs
@@ -77,6 +77,16 @@ pub struct PaneSnapshot {
pub label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_name: Option<String>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub agent_session: Option<PaneAgentSessionSnapshot>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct PaneAgentSessionSnapshot {
+ pub source: String,
+ pub agent: String,
+ pub kind: crate::agent_resume::AgentSessionRefKind,
+ pub value: String,
}
/// Serializable BSP tree.
@@ -280,12 +290,37 @@ fn capture_tab(
.get(id)
.and_then(|pane| terminals.get(&pane.attached_terminal_id))
.and_then(|terminal| terminal.agent_name.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(),
+ }
+ })
+ });
panes.insert(
id.raw(),
PaneSnapshot {
cwd,
label,
agent_name,
+ agent_session,
},
);
}
@@ -448,6 +483,7 @@ mod tests {
cwd: PathBuf::from("/home/can/Projects/herdr"),
label: None,
agent_name: None,
+ agent_session: None,
},
);
panes.insert(
@@ -456,6 +492,7 @@ mod tests {
cwd: PathBuf::from("/home/can/Projects/website"),
label: Some("website".into()),
agent_name: None,
+ agent_session: None,
},
);
@@ -765,6 +802,76 @@ mod tests {
}
#[test]
+ fn capture_contract_tracks_hook_authority_agent_session() {
+ let mut state = state_with_workspaces(&["one"]);
+ let root = state.workspaces[0].tabs[0].root_pane;
+ state.ensure_test_terminals();
+ let terminal_id = state.workspaces[0].tabs[0].panes[&root]
+ .attached_terminal_id
+ .clone();
+ state
+ .terminals
+ .get_mut(&terminal_id)
+ .unwrap()
+ .set_hook_authority_with_session_ref(
+ "herdr:pi".into(),
+ "pi".into(),
+ crate::detect::AgentState::Working,
+ None,
+ None,
+ crate::agent_resume::AgentSessionRef::path("/tmp/pi-session.jsonl"),
+ Some(20),
+ );
+
+ let snapshot = capture_from_state(&state);
+ let agent_session = snapshot.workspaces[0].tabs[0].panes[&root.raw()]
+ .agent_session
+ .as_ref()
+ .expect("agent session should be captured");
+
+ assert_eq!(agent_session.source, "herdr:pi");
+ assert_eq!(agent_session.agent, "pi");
+ assert_eq!(
+ agent_session.kind,
+ crate::agent_resume::AgentSessionRefKind::Path
+ );
+ assert_eq!(agent_session.value, "/tmp/pi-session.jsonl");
+ }
+
+ #[test]
+ fn capture_contract_preserves_restored_agent_session() {
+ let mut state = state_with_workspaces(&["one"]);
+ let root = state.workspaces[0].tabs[0].root_pane;
+ state.ensure_test_terminals();
+ let terminal_id = state.workspaces[0].tabs[0].panes[&root]
+ .attached_terminal_id
+ .clone();
+ state
+ .terminals
+ .get_mut(&terminal_id)
+ .unwrap()
+ .set_persisted_agent_session(crate::agent_resume::PersistedAgentSession {
+ source: "herdr:opencode".into(),
+ agent: "opencode".into(),
+ session_ref: crate::agent_resume::AgentSessionRef::id("opencode-session").unwrap(),
+ });
+
+ let snapshot = capture_from_state(&state);
+ let agent_session = snapshot.workspaces[0].tabs[0].panes[&root.raw()]
+ .agent_session
+ .as_ref()
+ .expect("persisted agent session should be captured");
+
+ assert_eq!(agent_session.source, "herdr:opencode");
+ assert_eq!(agent_session.agent, "opencode");
+ assert_eq!(
+ agent_session.kind,
+ crate::agent_resume::AgentSessionRefKind::Id
+ );
+ assert_eq!(agent_session.value, "opencode-session");
+ }
+
+ #[test]
fn old_unversioned_snapshot_loads_as_version_0() {
let json = r#"{"workspaces":[],"active":null,"selected":0}"#;
let snap = parse_snapshot(json).unwrap();
@@ -793,6 +900,7 @@ mod tests {
cwd: PathBuf::from("/tmp/this-directory-does-not-exist-for-herdr-test"),
label: None,
agent_name: None,
+ agent_session: None,
},
);
panes.insert(
@@ -803,6 +911,7 @@ mod tests {
.unwrap_or_else(|_| PathBuf::from("/tmp")),
label: None,
agent_name: None,
+ agent_session: None,
},
);