From 07261e09be60ac0c145685fd11f94351cc0e5ba6 Mon Sep 17 00:00:00 2001 From: Eric Yue Date: Mon, 15 Jun 2026 21:50:24 +0800 Subject: feat: add devin cli detection and restore support (#622) * feat: add devin cli detection and restore support refs #606 * fix: keep devin integration session-only --------- Co-authored-by: Ogulcan Celik --- src/agent_resume.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'src/agent_resume.rs') diff --git a/src/agent_resume.rs b/src/agent_resume.rs index cea4a641..4a97f207 100644 --- a/src/agent_resume.rs +++ b/src/agent_resume.rs @@ -82,6 +82,7 @@ pub fn is_reserved_native_state_source(source: &str, agent: &str) -> bool { ("herdr:claude", "claude") | ("herdr:codex", "codex") | ("herdr:copilot", "copilot") + | ("herdr:devin", "devin") | ("herdr:droid", "droid") | ("herdr:qodercli", "qodercli") | ("herdr:cursor", "cursor") @@ -128,6 +129,9 @@ pub fn plan(source: &str, agent: &str, session_ref: &AgentSessionRef) -> Option< ("herdr:copilot", "copilot", AgentSessionRefKind::Id) => { vec!["copilot".into(), format!("--resume={}", session_ref.value)] } + ("herdr:devin", "devin", AgentSessionRefKind::Id) => { + vec!["devin".into(), "--resume".into(), session_ref.value.clone()] + } ("herdr:droid", "droid", AgentSessionRefKind::Id) => { vec!["droid".into(), "--resume".into(), session_ref.value.clone()] } @@ -191,6 +195,7 @@ fn is_official_agent_source(source: &str, agent: &str) -> bool { ("herdr:claude", "claude") | ("herdr:codex", "codex") | ("herdr:copilot", "copilot") + | ("herdr:devin", "devin") | ("herdr:droid", "droid") | ("herdr:kimi", "kimi") | ("herdr:pi", "pi") @@ -229,6 +234,7 @@ mod tests { fn native_state_reservation_excludes_full_lifecycle_sources() { assert!(is_reserved_native_state_source("herdr:claude", "claude")); assert!(is_reserved_native_state_source("herdr:codex", "codex")); + assert!(is_reserved_native_state_source("herdr:devin", "devin")); assert!(!is_reserved_native_state_source("herdr:kimi", "kimi")); assert!(!is_reserved_native_state_source( "herdr:opencode", @@ -269,6 +275,16 @@ mod tests { .argv, vec!["copilot", "--resume=copilot-session"] ); + assert_eq!( + plan( + "herdr:devin", + "devin", + &AgentSessionRef::id("devin-session").unwrap() + ) + .unwrap() + .argv, + vec!["devin", "--resume", "devin-session"] + ); assert_eq!( plan( "herdr:droid", @@ -403,6 +419,11 @@ mod tests { .is_none() ); + let session_ref = + session_ref_from_report("herdr:devin", "devin", Some("devin-id".into()), None).unwrap(); + assert_eq!(session_ref.kind, AgentSessionRefKind::Id); + assert_eq!(session_ref.value, "devin-id"); + let session_ref = session_ref_from_report("herdr:droid", "droid", Some("droid-id".into()), None).unwrap(); assert_eq!(session_ref.kind, AgentSessionRefKind::Id); @@ -474,6 +495,9 @@ mod tests { ) .unwrap(); assert_eq!(copilot_plan.argv, vec!["copilot", "--resume=abc; rm -rf /"]); + + let devin_plan = plan("herdr:devin", "devin", &AgentSessionRef::id(id).unwrap()).unwrap(); + assert_eq!(devin_plan.argv, vec!["devin", "--resume", id]); } #[test] @@ -482,6 +506,7 @@ mod tests { let opencode_session = absolute_test_path("opencode-session"); let kilo_session = absolute_test_path("kilo-session"); let copilot_session = absolute_test_path("copilot-session"); + let devin_session = absolute_test_path("devin-session"); assert!(plan( "herdr:hermes", "hermes", @@ -506,6 +531,12 @@ mod tests { &AgentSessionRef::path(&copilot_session).unwrap() ) .is_none()); + assert!(plan( + "herdr:devin", + "devin", + &AgentSessionRef::path(&devin_session).unwrap() + ) + .is_none()); assert!(session_ref_from_snapshot( "herdr:hermes", "hermes", @@ -534,5 +565,12 @@ mod tests { "copilot-session" ) .is_some()); + assert!(session_ref_from_snapshot( + "herdr:devin", + "devin", + AgentSessionRefKind::Id, + "devin-session" + ) + .is_some()); } } -- cgit v1.2.3