@team-agent/installer 0.5.52 → 0.5.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +18 -2
  4. package/crates/team-agent/src/cli/emit.rs +17 -0
  5. package/crates/team-agent/src/cli/mod.rs +155 -4
  6. package/crates/team-agent/src/cli/spec.rs +3 -0
  7. package/crates/team-agent/src/cli/types.rs +11 -0
  8. package/crates/team-agent/src/diagnose/orphans.rs +24 -3
  9. package/crates/team-agent/src/kill_audit.rs +67 -0
  10. package/crates/team-agent/src/leader/lease.rs +324 -57
  11. package/crates/team-agent/src/leader/rediscover/tests.rs +3 -0
  12. package/crates/team-agent/src/leader/rediscover.rs +6 -0
  13. package/crates/team-agent/src/leader/start.rs +144 -23
  14. package/crates/team-agent/src/leader/tests/idle.rs +3 -0
  15. package/crates/team-agent/src/leader/types.rs +6 -0
  16. package/crates/team-agent/src/lib.rs +1 -0
  17. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +1 -1
  18. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +5 -0
  19. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +4 -0
  20. package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +106 -0
  21. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +216 -208
  22. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +36 -0
  23. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +238 -0
  24. package/crates/team-agent/src/lifecycle/launch/fork_state.rs +101 -5
  25. package/crates/team-agent/src/lifecycle/launch/role_source.rs +170 -0
  26. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +1 -1
  27. package/crates/team-agent/src/lifecycle/launch.rs +14 -2
  28. package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -1
  29. package/crates/team-agent/src/lifecycle/restart/common.rs +12 -0
  30. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -3
  31. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +110 -12
  32. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +8 -2
  33. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +1 -0
  34. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +3 -1
  35. package/crates/team-agent/src/lifecycle/types.rs +17 -0
  36. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +40 -5
  37. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -1
  38. package/crates/team-agent/src/mcp_server/tests/wire.rs +243 -167
  39. package/crates/team-agent/src/mcp_server/tools.rs +18 -4
  40. package/crates/team-agent/src/mcp_server/types.rs +3 -0
  41. package/crates/team-agent/src/mcp_server/wire.rs +30 -7
  42. package/crates/team-agent/src/messaging/leader_channel.rs +32 -5
  43. package/crates/team-agent/src/messaging/leader_receiver.rs +68 -21
  44. package/crates/team-agent/src/messaging/tests/runtime.rs +34 -23
  45. package/crates/team-agent/src/provider/adapter.rs +54 -13
  46. package/crates/team-agent/src/provider/adapters/claude_fork.rs +122 -0
  47. package/crates/team-agent/src/provider/adapters/copilot_fork.rs +306 -0
  48. package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
  49. package/crates/team-agent/src/provider/session/capture.rs +321 -52
  50. package/crates/team-agent/src/provider/session/context_fork.rs +499 -0
  51. package/crates/team-agent/src/provider/session/mod.rs +6 -0
  52. package/crates/team-agent/src/provider/session_scan/claude.rs +1 -1
  53. package/crates/team-agent/src/provider/session_scan/codex.rs +144 -27
  54. package/crates/team-agent/src/provider/session_scan/common.rs +39 -6
  55. package/crates/team-agent/src/provider/session_scan/copilot.rs +47 -3
  56. package/crates/team-agent/src/provider/session_scan.rs +3 -3
  57. package/crates/team-agent/src/provider/tests/copilot_fork.rs +191 -0
  58. package/crates/team-agent/src/provider/tests.rs +1 -0
  59. package/crates/team-agent/src/tmux_backend/tests.rs +51 -16
  60. package/crates/team-agent/src/tmux_backend.rs +29 -2
  61. package/package.json +4 -4
  62. package/skills/team-agent/SKILL.md +8 -3
@@ -449,6 +449,15 @@ fn restart_with_selected_team_and_transport(
449
449
  moved leader to dedicated session)",
450
450
  session_name.as_str()
451
451
  );
452
+ let targets = vec![session_name.as_str().to_string()];
453
+ crate::kill_audit::pre_kill_audit(
454
+ &crate::event_log::EventLog::new(&selected.run_workspace),
455
+ transport,
456
+ "restart.stale_worker_session",
457
+ crate::kill_audit::KILL_SESSION,
458
+ &targets,
459
+ )
460
+ .map_err(|error| LifecycleError::Transport(format!("pre-kill audit failed: {error}")))?;
452
461
  transport
453
462
  .kill_session(&session_name)
454
463
  .map_err(|e| LifecycleError::Transport(e.to_string()))?;
@@ -1984,7 +1993,7 @@ fn mark_agent_respawned(
1984
1993
  }
1985
1994
  agent.insert(
1986
1995
  "spawned_at".to_string(),
1987
- serde_json::json!(chrono::Utc::now().to_rfc3339()),
1996
+ serde_json::json!(spawn.spawned_at.as_str()),
1988
1997
  );
1989
1998
  agent.insert(
1990
1999
  "spawn_cwd".to_string(),
@@ -2462,6 +2471,7 @@ impl Clone for ParallelSpawnResult {
2462
2471
  Self {
2463
2472
  spawn: SpawnedAgentWindow {
2464
2473
  spawn: self.spawn.spawn.clone(),
2474
+ spawned_at: self.spawn.spawned_at.clone(),
2465
2475
  plan: self.spawn.plan.clone(),
2466
2476
  profile_launch: self.spawn.profile_launch.clone(),
2467
2477
  layout_placement: self.spawn.layout_placement.clone(),
@@ -3689,6 +3699,7 @@ tasks:
3689
3699
  }
3690
3700
  }
3691
3701
  });
3702
+ let before = chrono::Utc::now();
3692
3703
  let spawn = SpawnedAgentWindow {
3693
3704
  spawn: SpawnResult {
3694
3705
  pane_id: PaneId::new("%new"),
@@ -3696,14 +3707,13 @@ tasks:
3696
3707
  window: WindowName::new("w1"),
3697
3708
  child_pid: Some(2020),
3698
3709
  },
3710
+ spawned_at: chrono::Utc::now().to_rfc3339(),
3699
3711
  plan: crate::provider::CommandPlan::argv_only(vec!["codex".to_string()]),
3700
3712
  profile_launch: crate::provider::ProviderProfileLaunch::default(),
3701
3713
  layout_placement: None,
3702
3714
  spawn_cwd: std::path::PathBuf::from("/tmp/team-epoch"),
3703
3715
  owner_team_id: None,
3704
3716
  };
3705
- let before = chrono::Utc::now();
3706
-
3707
3717
  mark_agent_respawned(
3708
3718
  &mut state,
3709
3719
  &AgentId::new("w1"),
@@ -8,6 +8,60 @@ const DELEG_ROLE_ALPHA_COMPAT: &str = "---\nname: alpha\nrole: Alpha Worker\npro
8
8
  type LaneKills = std::sync::Arc<std::sync::Mutex<Vec<String>>>;
9
9
  pub(super) type LaneSpawns = std::sync::Arc<std::sync::Mutex<Vec<(String, Vec<String>)>>>;
10
10
 
11
+ fn emit_codex_fork_backing(argv: &[String], cwd: &std::path::Path) {
12
+ if !argv.windows(2).any(|pair| pair == ["codex", "fork"]) {
13
+ return;
14
+ }
15
+ static SESSION_SEQ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16
+ let sequence = SESSION_SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
17
+ let session_id = format!("lane-fork-{}-{sequence}", std::process::id());
18
+ let root = std::env::var_os("HOME")
19
+ .map(std::path::PathBuf::from)
20
+ .unwrap_or_else(|| std::path::PathBuf::from("."))
21
+ .join(".codex/sessions/lane-fixture");
22
+ std::fs::create_dir_all(&root).unwrap();
23
+ std::fs::write(
24
+ root.join(format!("rollout-{session_id}.jsonl")),
25
+ format!(
26
+ "{{\"session_meta\":{{\"payload\":{{\"id\":\"{session_id}\",\"cwd\":\"{}\"}}}},\"created_at\":\"{}\"}}\n",
27
+ cwd.to_string_lossy(),
28
+ chrono::Utc::now().to_rfc3339()
29
+ ),
30
+ )
31
+ .unwrap();
32
+ }
33
+
34
+ pub(super) struct LaneHomeGuard {
35
+ previous: Option<std::ffi::OsString>,
36
+ root: PathBuf,
37
+ }
38
+
39
+ impl LaneHomeGuard {
40
+ pub(super) fn enter(tag: &str) -> Self {
41
+ static HOME_SEQ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
42
+ let root = std::env::temp_dir().join(format!(
43
+ "ta-lane-home-{tag}-{}-{}",
44
+ std::process::id(),
45
+ HOME_SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
46
+ ));
47
+ let _ = std::fs::remove_dir_all(&root);
48
+ std::fs::create_dir_all(&root).unwrap();
49
+ let previous = std::env::var_os("HOME");
50
+ std::env::set_var("HOME", &root);
51
+ Self { previous, root }
52
+ }
53
+ }
54
+
55
+ impl Drop for LaneHomeGuard {
56
+ fn drop(&mut self) {
57
+ match &self.previous {
58
+ Some(value) => std::env::set_var("HOME", value),
59
+ None => std::env::remove_var("HOME"),
60
+ }
61
+ let _ = std::fs::remove_dir_all(&self.root);
62
+ }
63
+ }
64
+
11
65
  /// Recording transport for Lane-A v2: `list_windows`/`list_targets` answer from a configurable window
12
66
  /// set (golden's `_tmux_window_exists` primitive = `tmux list-windows`); `kill_window` + spawn_first/into
13
67
  /// are RECORDED. Every other method returns a benign Ok (never panics) so stop/reset/remove/fork run
@@ -18,6 +72,7 @@ pub(super) struct LaneTransport {
18
72
  foreign: std::sync::Arc<std::sync::Mutex<Vec<(String, String)>>>,
19
73
  killed: LaneKills,
20
74
  spawns: LaneSpawns,
75
+ spawn_observed_at: std::sync::Arc<std::sync::Mutex<Vec<String>>>,
21
76
  }
22
77
  impl LaneTransport {
23
78
  pub(super) fn new(session: &str, windows: &[&str]) -> Self {
@@ -29,6 +84,7 @@ impl LaneTransport {
29
84
  foreign: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
30
85
  killed: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
31
86
  spawns: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
87
+ spawn_observed_at: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
32
88
  }
33
89
  }
34
90
  pub(super) fn killed(&self) -> Vec<String> {
@@ -37,6 +93,9 @@ impl LaneTransport {
37
93
  pub(super) fn spawns(&self) -> Vec<(String, Vec<String>)> {
38
94
  self.spawns.lock().unwrap().clone()
39
95
  }
96
+ fn spawn_observed_at(&self) -> Vec<String> {
97
+ self.spawn_observed_at.lock().unwrap().clone()
98
+ }
40
99
  fn add_foreign(&self, session: &str, window: &str) {
41
100
  self.foreign
42
101
  .lock()
@@ -53,9 +112,14 @@ impl crate::transport::Transport for LaneTransport {
53
112
  session: &crate::transport::SessionName,
54
113
  window: &crate::transport::WindowName,
55
114
  argv: &[String],
56
- _c: &std::path::Path,
115
+ c: &std::path::Path,
57
116
  _e: &std::collections::BTreeMap<String, String>,
58
117
  ) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
118
+ self.spawn_observed_at
119
+ .lock()
120
+ .unwrap()
121
+ .push(chrono::Utc::now().to_rfc3339());
122
+ emit_codex_fork_backing(argv, c);
59
123
  self.spawns
60
124
  .lock()
61
125
  .unwrap()
@@ -84,9 +148,14 @@ impl crate::transport::Transport for LaneTransport {
84
148
  session: &crate::transport::SessionName,
85
149
  window: &crate::transport::WindowName,
86
150
  argv: &[String],
87
- _c: &std::path::Path,
151
+ c: &std::path::Path,
88
152
  _e: &std::collections::BTreeMap<String, String>,
89
153
  ) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
154
+ self.spawn_observed_at
155
+ .lock()
156
+ .unwrap()
157
+ .push(chrono::Utc::now().to_rfc3339());
158
+ emit_codex_fork_backing(argv, c);
90
159
  self.spawns
91
160
  .lock()
92
161
  .unwrap()
@@ -147,7 +216,7 @@ impl crate::transport::Transport for LaneTransport {
147
216
  &self,
148
217
  _p: &crate::transport::PaneId,
149
218
  ) -> Result<crate::model::enums::PaneLiveness, crate::transport::TransportError> {
150
- Ok(crate::model::enums::PaneLiveness::Unknown)
219
+ Ok(crate::model::enums::PaneLiveness::Live)
151
220
  }
152
221
  fn list_targets(
153
222
  &self,
@@ -1594,14 +1663,11 @@ fn lanea_fork_gate_error_text_and_spec_rollback_on_adapter_arm() {
1594
1663
  );
1595
1664
  }
1596
1665
 
1597
- // ── FORK (fork-report-session-id-is-pane-id) [RED] report session_id is the captured id / None, not pane
1598
- // Golden operations.py:399,408 returns state['agents'][as_agent_id].get('session_id') — the captured
1599
- // provider session id (or None if capture missed, raise_on_missed=False). Rust sets
1600
- // session_id: Some(SessionId::new(spawn.pane_id)) (launch.rs:502) — the tmux pane id ('%newfork'),
1601
- // a different value kind. RED: the report session_id must NOT be the pane id (None, since the Rust fork
1602
- // path performs no session capture).
1666
+ // Successful fork reports the verified provider session id, never the pane id.
1603
1667
  #[test]
1668
+ #[serial_test::serial(env)]
1604
1669
  fn lanea_fork_report_session_id_is_not_pane_id() {
1670
+ let _home = LaneHomeGuard::enter("fork-report");
1605
1671
  let ws = fork_ws(DELEG_ROLE_ALPHA); // codex+subscription -> native fork supported -> full success path
1606
1672
  let tx = LaneTransport::new("team-laneateam", &[]);
1607
1673
  let report =
@@ -1613,9 +1679,41 @@ fn lanea_fork_report_session_id_is_not_pane_id() {
1613
1679
  "golden operations.py:399,408: report.session_id is the captured provider session id / None, NEVER the \
1614
1680
  tmux pane id; Rust returns Some(pane_id='%newfork')"
1615
1681
  );
1616
- assert_eq!(
1617
- report.session_id, None,
1618
- "the Rust fork path captures no session -> report.session_id must be None (golden capture-missed), not the pane id"
1682
+ assert!(
1683
+ report.session_id.is_some(),
1684
+ "a successful fork must carry the verified NEW provider session id"
1685
+ );
1686
+ let state = crate::state::persist::load_runtime_state(&ws).unwrap();
1687
+ let row = &state["agents"]["newfork"];
1688
+ assert!(row["spawned_at"]
1689
+ .as_str()
1690
+ .is_some_and(|value| chrono::DateTime::parse_from_rfc3339(value).is_ok()));
1691
+ assert_eq!(row["spawn_epoch"], serde_json::json!(1));
1692
+ }
1693
+
1694
+ #[test]
1695
+ fn fresh_start_persists_boundary_from_before_transport_spawn() {
1696
+ let ws = lanea_one_agent_ws("stopped");
1697
+ let tx = LaneTransport::new("team-laneateam", &[]);
1698
+ crate::lifecycle::start_agent_with_transport(&ws, &aid("alpha"), false, false, true, None, &tx)
1699
+ .expect("fresh start");
1700
+
1701
+ let transport_observed_at = tx
1702
+ .spawn_observed_at()
1703
+ .into_iter()
1704
+ .next()
1705
+ .expect("transport spawn observation");
1706
+ let transport_observed_at =
1707
+ chrono::DateTime::parse_from_rfc3339(&transport_observed_at).unwrap();
1708
+ let state = crate::state::persist::load_runtime_state(&ws).unwrap();
1709
+ let spawned_at = state["agents"]["alpha"]["spawned_at"]
1710
+ .as_str()
1711
+ .and_then(|value| chrono::DateTime::parse_from_rfc3339(value).ok())
1712
+ .expect("persisted spawn boundary");
1713
+ assert!(
1714
+ spawned_at <= transport_observed_at,
1715
+ "the persisted capture boundary must precede the transport spawn; \
1716
+ spawned_at={spawned_at}, transport_observed_at={transport_observed_at}"
1619
1717
  );
1620
1718
  }
1621
1719
 
@@ -71,10 +71,11 @@ fn codex_identity_rollout(
71
71
  std::fs::write(
72
72
  &path,
73
73
  format!(
74
- "{{\"session_meta\":{{\"payload\":{{\"id\":\"{session_id}\",\"cwd\":\"{}\"}}}}}}\n\
74
+ "{{\"session_meta\":{{\"payload\":{{\"id\":\"{session_id}\",\"cwd\":\"{}\",\"created_at\":\"{}\"}}}}}}\n\
75
75
  {{\"type\":\"turn_context\",\"payload\":{{}}}}\n\
76
76
  {{\"type\":\"response_item\",\"payload\":{{\"content\":[{{\"type\":\"input_text\",\"text\":\"You are Team Agent worker `{embedded_agent_id}` with role `fixture`.\"}}]}}}}\n",
77
- workspace.to_string_lossy()
77
+ workspace.to_string_lossy(),
78
+ chrono::Utc::now().to_rfc3339()
78
79
  ),
79
80
  )
80
81
  .unwrap();
@@ -2481,6 +2482,10 @@ fn restart_allow_fresh_copilot_keeps_session_id_null_until_capture_confirms() {
2481
2482
  .windows(2)
2482
2483
  .find_map(|pair| (pair[0] == "--session-id").then(|| pair[1].clone()))
2483
2484
  .expect("copilot fresh spawn argv must include --session-id");
2485
+ let instructions =
2486
+ std::fs::read_to_string(ws.join(".team/runtime/copilot-instructions/alpha/AGENTS.md"))
2487
+ .expect("restart/add/clone spawn must materialize Copilot instructions");
2488
+ assert!(instructions.contains("Alpha Worker"));
2484
2489
  let state = crate::state::persist::load_runtime_state(&ws).expect("load state");
2485
2490
  let agent = state.pointer("/agents/alpha").expect("alpha state");
2486
2491
  // 0.4.6 contract: session_id stays null until scanner confirms sqlite.
@@ -3084,6 +3089,7 @@ fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
3084
3089
  "captured_at",
3085
3090
  "captured_via",
3086
3091
  "attribution_confidence",
3092
+ "capture_state",
3087
3093
  "spawn_cwd",
3088
3094
  "spawned_at",
3089
3095
  "pane_id",
@@ -318,6 +318,7 @@ fn reset_agent_discard_session_rebuilds_window_via_start_respawn() {
318
318
  }
319
319
 
320
320
  #[test]
321
+ #[serial_test::serial(env)]
321
322
  fn reset_agent_discard_session_syncs_projection_epoch_inputs_for_restart_agent_command() {
322
323
  let ws = restart_ws_two_resumable_workers();
323
324
  let mut state = crate::state::persist::load_runtime_state(&ws).expect("load state");
@@ -1,5 +1,5 @@
1
1
  use super::agent_ops::lanea_team_ws;
2
- use super::lane_ops::{fork_ws, LaneTransport};
2
+ use super::lane_ops::{fork_ws, LaneHomeGuard, LaneTransport};
3
3
  use super::launch_spawn::{
4
4
  quick_start_team_dir, seed_healthy_coordinator, DELEG_ROLE_ALPHA, QS_VALID_ROLE,
5
5
  };
@@ -129,7 +129,9 @@ fn concurrent_reset_discard_session_serializes() {
129
129
  }
130
130
 
131
131
  #[test]
132
+ #[serial_test::serial(env)]
132
133
  fn add_fork_remove_share_lifecycle_lock_behavior() {
134
+ let _home = LaneHomeGuard::enter("phase-b-fork");
133
135
  let (add_team, add_workspace, add_role, add_transport) = add_fixture();
134
136
  let add_agent = aid("w2");
135
137
  let add_state_before =
@@ -666,6 +666,23 @@ pub struct ForkAgentReport {
666
666
  pub session_id: Option<SessionId>,
667
667
  }
668
668
 
669
+ #[derive(Debug, Clone, PartialEq, Eq)]
670
+ pub struct CloneAgentReport {
671
+ pub source_agent_id: AgentId,
672
+ pub new_agent_id: AgentId,
673
+ pub env: AgentActionEnvelope,
674
+ pub session_id: Option<SessionId>,
675
+ pub backing_path: Option<PathBuf>,
676
+ pub backing_state: CloneBackingState,
677
+ }
678
+
679
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
680
+ #[serde(rename_all = "snake_case")]
681
+ pub enum CloneBackingState {
682
+ Verified,
683
+ PendingFirstTurn,
684
+ }
685
+
669
686
  /// Read-only remove-agent flag requirements for the target's current state.
670
687
  #[derive(Debug, Clone, PartialEq, Eq)]
671
688
  pub struct RemoveAgentFlagRequirements {
@@ -134,6 +134,41 @@ pub(crate) fn fork_agent(
134
134
  })
135
135
  }
136
136
 
137
+ pub(crate) fn clone_agent(
138
+ workspace: &Path,
139
+ owner_team: Option<&TeamKey>,
140
+ source_agent_id: &str,
141
+ as_agent_id: &str,
142
+ label: Option<&str>,
143
+ ) -> ToolResult {
144
+ emit_newer_daemon_preserved_if_present(workspace)?;
145
+ let lifecycle_workspace = lifecycle_workspace(workspace, owner_team, false)?;
146
+ let report = crate::lifecycle::launch::clone_agent(
147
+ &lifecycle_workspace,
148
+ &AgentId::new(source_agent_id),
149
+ &AgentId::new(as_agent_id),
150
+ label,
151
+ false,
152
+ owner_team.map(TeamKey::as_str),
153
+ )
154
+ .map_err(tool_runtime_error)?;
155
+ Ok(ToolOk {
156
+ fields: object_fields(serde_json::json!({
157
+ "ok": true,
158
+ "status": "cloned",
159
+ "source_agent_id": report.source_agent_id.as_str(),
160
+ "agent_id": report.new_agent_id.as_str(),
161
+ "new_agent_id": report.new_agent_id.as_str(),
162
+ "state_file": report.env.state_file.to_string_lossy().to_string(),
163
+ "coordinator_started": report.env.coordinator_started,
164
+ "session_id": report.session_id.as_ref().map(|session| session.as_str()),
165
+ "new_session_id": report.session_id.as_ref().map(|session| session.as_str()),
166
+ "backing_path": report.backing_path.as_ref().map(|path| path.to_string_lossy().to_string()),
167
+ "backing_state": report.backing_state,
168
+ })),
169
+ })
170
+ }
171
+
137
172
  fn reset_refusal_reason(reason: ResetRefusal) -> Value {
138
173
  match reason {
139
174
  ResetRefusal::DiscardSessionRequired => {
@@ -440,9 +475,9 @@ fn prepare_selected_team_state(
440
475
  state,
441
476
  )
442
477
  .map_err(|e| {
443
- tool_runtime_error(format!(
444
- "save MCP lifecycle scoped state {}: {e}",
445
- workspace.display()
446
- ))
447
- })
478
+ tool_runtime_error(format!(
479
+ "save MCP lifecycle scoped state {}: {e}",
480
+ workspace.display()
481
+ ))
482
+ })
448
483
  }
@@ -6,5 +6,5 @@
6
6
  mod agent_ops;
7
7
  mod state_status;
8
8
 
9
- pub(crate) use agent_ops::{fork_agent, reset_agent, stop_agent};
9
+ pub(crate) use agent_ops::{clone_agent, fork_agent, reset_agent, stop_agent};
10
10
  pub(crate) use state_status::{get_team_status, update_state};