@team-agent/installer 0.5.42 → 0.5.44

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 (156) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +129 -54
  4. package/crates/team-agent/src/cli/diagnose.rs +1 -2
  5. package/crates/team-agent/src/cli/emit.rs +1 -7
  6. package/crates/team-agent/src/cli/helpers.rs +3 -1
  7. package/crates/team-agent/src/cli/leader.rs +2 -1
  8. package/crates/team-agent/src/cli/mod.rs +126 -16
  9. package/crates/team-agent/src/cli/named_address.rs +14 -5
  10. package/crates/team-agent/src/cli/profile.rs +19 -7
  11. package/crates/team-agent/src/cli/send.rs +9 -3
  12. package/crates/team-agent/src/cli/status.rs +8 -30
  13. package/crates/team-agent/src/cli/status_port.rs +1341 -1272
  14. package/crates/team-agent/src/cli/tests/base.rs +738 -660
  15. package/crates/team-agent/src/cli/tests/compile.rs +45 -18
  16. package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
  21. package/crates/team-agent/src/cli/tests/mod.rs +17 -7
  22. package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
  23. package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
  24. package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
  25. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
  26. package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
  27. package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
  28. package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
  29. package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
  30. package/crates/team-agent/src/codex_app_server.rs +2 -5
  31. package/crates/team-agent/src/compiler/tests.rs +139 -33
  32. package/crates/team-agent/src/compiler.rs +55 -22
  33. package/crates/team-agent/src/conpty/backend.rs +23 -33
  34. package/crates/team-agent/src/coordinator/backoff.rs +2 -7
  35. package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
  36. package/crates/team-agent/src/coordinator/health.rs +46 -31
  37. package/crates/team-agent/src/coordinator/mod.rs +3 -3
  38. package/crates/team-agent/src/coordinator/orphan.rs +22 -10
  39. package/crates/team-agent/src/coordinator/steps/abnormal.rs +51 -56
  40. package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
  41. package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
  42. package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
  43. package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
  44. package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
  45. package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
  46. package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
  47. package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
  48. package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
  49. package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
  50. package/crates/team-agent/src/db/message_store.rs +138 -30
  51. package/crates/team-agent/src/db/migration.rs +249 -61
  52. package/crates/team-agent/src/db/schema.rs +303 -82
  53. package/crates/team-agent/src/diagnose/comms.rs +9 -2
  54. package/crates/team-agent/src/diagnose/mod.rs +1 -3
  55. package/crates/team-agent/src/diagnose/orphans.rs +79 -61
  56. package/crates/team-agent/src/event_log.rs +70 -16
  57. package/crates/team-agent/src/layout/manager.rs +15 -4
  58. package/crates/team-agent/src/layout/mod.rs +4 -4
  59. package/crates/team-agent/src/layout/overlay.rs +10 -3
  60. package/crates/team-agent/src/layout/placement.rs +5 -1
  61. package/crates/team-agent/src/layout/recovery.rs +4 -2
  62. package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
  63. package/crates/team-agent/src/layout/sessions.rs +17 -9
  64. package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
  65. package/crates/team-agent/src/layout/worker_env.rs +87 -19
  66. package/crates/team-agent/src/leader/helpers.rs +7 -1
  67. package/crates/team-agent/src/leader/lease.rs +199 -89
  68. package/crates/team-agent/src/leader/owner_bind.rs +55 -22
  69. package/crates/team-agent/src/leader/provider_attribution.rs +25 -6
  70. package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
  71. package/crates/team-agent/src/leader/rediscover.rs +74 -25
  72. package/crates/team-agent/src/leader/registry.rs +1 -1
  73. package/crates/team-agent/src/leader/start.rs +75 -54
  74. package/crates/team-agent/src/leader/takeover.rs +46 -11
  75. package/crates/team-agent/src/leader/tests/basics.rs +320 -167
  76. package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
  77. package/crates/team-agent/src/leader/tests/identity.rs +428 -356
  78. package/crates/team-agent/src/leader/tests/idle.rs +285 -254
  79. package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
  80. package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
  81. package/crates/team-agent/src/leader/tests/mod.rs +115 -99
  82. package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
  83. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
  84. package/crates/team-agent/src/lib.rs +4 -4
  85. package/crates/team-agent/src/lifecycle/display.rs +7 -3
  86. package/crates/team-agent/src/lifecycle/launch.rs +55 -15
  87. package/crates/team-agent/src/lifecycle/mod.rs +9 -1
  88. package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
  89. package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
  90. package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
  91. package/crates/team-agent/src/lifecycle/restart/common.rs +6 -2
  92. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
  93. package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
  94. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
  95. package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
  96. package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
  97. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
  98. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
  99. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
  100. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
  101. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
  102. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +3 -0
  103. package/crates/team-agent/src/lifecycle/tests.rs +2 -2
  104. package/crates/team-agent/src/main.rs +4 -4
  105. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
  106. package/crates/team-agent/src/mcp_server/mod.rs +11 -2
  107. package/crates/team-agent/src/mcp_server/types.rs +14 -3
  108. package/crates/team-agent/src/mcp_server/wire.rs +230 -68
  109. package/crates/team-agent/src/messaging/delivery.rs +20 -18
  110. package/crates/team-agent/src/messaging/helpers.rs +25 -4
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
  112. package/crates/team-agent/src/messaging/mod.rs +2 -3
  113. package/crates/team-agent/src/messaging/selftest.rs +46 -26
  114. package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
  115. package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
  116. package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
  117. package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
  118. package/crates/team-agent/src/messaging/trust.rs +25 -2
  119. package/crates/team-agent/src/messaging/watchers.rs +37 -9
  120. package/crates/team-agent/src/model/enums.rs +65 -16
  121. package/crates/team-agent/src/model/ids.rs +12 -3
  122. package/crates/team-agent/src/model/paths.rs +28 -7
  123. package/crates/team-agent/src/model/permissions.rs +176 -33
  124. package/crates/team-agent/src/model/routing.rs +66 -20
  125. package/crates/team-agent/src/model/spec.rs +365 -69
  126. package/crates/team-agent/src/model/task_graph.rs +36 -9
  127. package/crates/team-agent/src/model/yaml/tests.rs +24 -6
  128. package/crates/team-agent/src/model/yaml.rs +7 -6
  129. package/crates/team-agent/src/packaging/install.rs +23 -9
  130. package/crates/team-agent/src/packaging/migrate.rs +5 -7
  131. package/crates/team-agent/src/packaging/mod.rs +9 -1
  132. package/crates/team-agent/src/packaging/repair.rs +13 -6
  133. package/crates/team-agent/src/packaging/tests.rs +63 -16
  134. package/crates/team-agent/src/packaging/types.rs +22 -7
  135. package/crates/team-agent/src/platform/argv.rs +4 -1
  136. package/crates/team-agent/src/platform/file_lock.rs +22 -8
  137. package/crates/team-agent/src/platform/process.rs +54 -24
  138. package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
  139. package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
  140. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
  141. package/crates/team-agent/src/provider/classify.rs +127 -42
  142. package/crates/team-agent/src/provider/faults.rs +17 -5
  143. package/crates/team-agent/src/provider/helpers.rs +6 -5
  144. package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
  145. package/crates/team-agent/src/state/persist.rs +28 -0
  146. package/crates/team-agent/src/state/repository.rs +8 -3
  147. package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
  148. package/crates/team-agent/src/tmux_backend.rs +80 -44
  149. package/crates/team-agent/src/topology.rs +40 -20
  150. package/crates/team-agent/src/transport/test_support.rs +20 -23
  151. package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
  152. package/crates/team-agent/src/transport/tests/mod.rs +178 -187
  153. package/crates/team-agent/src/transport/tests/wire.rs +561 -525
  154. package/crates/team-agent/src/transport.rs +12 -17
  155. package/crates/team-agent/src/transport_factory.rs +29 -14
  156. package/package.json +4 -4
@@ -1,9 +1,9 @@
1
- use super::*;
2
1
  use super::agent_ops::lanea_team_ws;
3
2
  use super::lane_ops::{LaneSpawns, LaneTransport};
4
3
  use super::launch_spawn::{
5
4
  restart_ws_two_resumable_workers, seed_healthy_coordinator, DELEG_ROLE_ALPHA, DELEG_ROLE_BRAVO,
6
5
  };
6
+ use super::*;
7
7
 
8
8
  type RecordedSpawns = LaneSpawns;
9
9
 
@@ -46,56 +46,118 @@ impl crate::transport::Transport for SessionProbeRecordingTransport {
46
46
  fn kind(&self) -> crate::transport::BackendKind {
47
47
  crate::transport::BackendKind::Tmux
48
48
  }
49
- fn spawn_first(&self, session: &crate::transport::SessionName, window: &crate::transport::WindowName, argv: &[String], _cwd: &std::path::Path, _env: &std::collections::BTreeMap<String, String>) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
50
- self.spawns.lock().unwrap().push(("spawn_first".to_string(), argv.to_vec()));
49
+ fn spawn_first(
50
+ &self,
51
+ session: &crate::transport::SessionName,
52
+ window: &crate::transport::WindowName,
53
+ argv: &[String],
54
+ _cwd: &std::path::Path,
55
+ _env: &std::collections::BTreeMap<String, String>,
56
+ ) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
57
+ self.spawns
58
+ .lock()
59
+ .unwrap()
60
+ .push(("spawn_first".to_string(), argv.to_vec()));
51
61
  self.spawn_targets.lock().unwrap().push((
52
62
  session.as_str().to_string(),
53
63
  window.as_str().to_string(),
54
64
  "%0".to_string(),
55
65
  ));
56
- Ok(crate::transport::SpawnResult { pane_id: crate::transport::PaneId::new("%0"), session: session.clone(), window: window.clone(), child_pid: Some(6856) })
66
+ Ok(crate::transport::SpawnResult {
67
+ pane_id: crate::transport::PaneId::new("%0"),
68
+ session: session.clone(),
69
+ window: window.clone(),
70
+ child_pid: Some(6856),
71
+ })
57
72
  }
58
- fn spawn_into(&self, session: &crate::transport::SessionName, window: &crate::transport::WindowName, argv: &[String], _cwd: &std::path::Path, _env: &std::collections::BTreeMap<String, String>) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
59
- self.spawns.lock().unwrap().push(("spawn_into".to_string(), argv.to_vec()));
73
+ fn spawn_into(
74
+ &self,
75
+ session: &crate::transport::SessionName,
76
+ window: &crate::transport::WindowName,
77
+ argv: &[String],
78
+ _cwd: &std::path::Path,
79
+ _env: &std::collections::BTreeMap<String, String>,
80
+ ) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
81
+ self.spawns
82
+ .lock()
83
+ .unwrap()
84
+ .push(("spawn_into".to_string(), argv.to_vec()));
60
85
  let pane = format!("%{}", window.as_str());
61
86
  self.spawn_targets.lock().unwrap().push((
62
87
  session.as_str().to_string(),
63
88
  window.as_str().to_string(),
64
89
  pane.clone(),
65
90
  ));
66
- Ok(crate::transport::SpawnResult { pane_id: crate::transport::PaneId::new(pane), session: session.clone(), window: window.clone(), child_pid: Some(6856) })
91
+ Ok(crate::transport::SpawnResult {
92
+ pane_id: crate::transport::PaneId::new(pane),
93
+ session: session.clone(),
94
+ window: window.clone(),
95
+ child_pid: Some(6856),
96
+ })
67
97
  }
68
- fn inject(&self, _t: &crate::transport::Target, _p: &crate::transport::InjectPayload, _s: crate::transport::Key, _b: bool) -> Result<crate::transport::InjectReport, crate::transport::TransportError> {
98
+ fn inject(
99
+ &self,
100
+ _t: &crate::transport::Target,
101
+ _p: &crate::transport::InjectPayload,
102
+ _s: crate::transport::Key,
103
+ _b: bool,
104
+ ) -> Result<crate::transport::InjectReport, crate::transport::TransportError> {
69
105
  unimplemented!("not reached by start_agent respawn")
70
106
  }
71
- fn send_keys(&self, _t: &crate::transport::Target, _k: &[crate::transport::Key]) -> Result<(), crate::transport::TransportError> {
107
+ fn send_keys(
108
+ &self,
109
+ _t: &crate::transport::Target,
110
+ _k: &[crate::transport::Key],
111
+ ) -> Result<(), crate::transport::TransportError> {
72
112
  unimplemented!("not reached")
73
113
  }
74
- fn capture(&self, _t: &crate::transport::Target, _r: crate::transport::CaptureRange) -> Result<crate::transport::CapturedText, crate::transport::TransportError> {
114
+ fn capture(
115
+ &self,
116
+ _t: &crate::transport::Target,
117
+ _r: crate::transport::CaptureRange,
118
+ ) -> Result<crate::transport::CapturedText, crate::transport::TransportError> {
75
119
  Ok(crate::transport::CapturedText {
76
120
  text: String::new(),
77
121
  range: crate::transport::CaptureRange::Full,
78
122
  })
79
123
  }
80
- fn query(&self, _t: &crate::transport::Target, _f: crate::transport::PaneField) -> Result<Option<String>, crate::transport::TransportError> {
124
+ fn query(
125
+ &self,
126
+ _t: &crate::transport::Target,
127
+ _f: crate::transport::PaneField,
128
+ ) -> Result<Option<String>, crate::transport::TransportError> {
81
129
  unimplemented!("not reached")
82
130
  }
83
- fn liveness(&self, p: &crate::transport::PaneId) -> Result<crate::model::enums::PaneLiveness, crate::transport::TransportError> {
131
+ fn liveness(
132
+ &self,
133
+ p: &crate::transport::PaneId,
134
+ ) -> Result<crate::model::enums::PaneLiveness, crate::transport::TransportError> {
84
135
  let killed = self.killed_panes.lock().unwrap();
85
136
  if killed.contains(p.as_str()) {
86
137
  return Ok(crate::model::enums::PaneLiveness::Dead);
87
138
  }
88
139
  Ok(crate::model::enums::PaneLiveness::Live)
89
140
  }
90
- fn has_pane(&self, p: &crate::transport::PaneId) -> Result<Option<bool>, crate::transport::TransportError> {
141
+ fn has_pane(
142
+ &self,
143
+ p: &crate::transport::PaneId,
144
+ ) -> Result<Option<bool>, crate::transport::TransportError> {
91
145
  let killed = self.killed_panes.lock().unwrap();
92
146
  Ok(Some(!killed.contains(p.as_str())))
93
147
  }
94
- fn kill_pane(&self, p: &crate::transport::PaneId) -> Result<(), crate::transport::TransportError> {
95
- self.killed_panes.lock().unwrap().insert(p.as_str().to_string());
148
+ fn kill_pane(
149
+ &self,
150
+ p: &crate::transport::PaneId,
151
+ ) -> Result<(), crate::transport::TransportError> {
152
+ self.killed_panes
153
+ .lock()
154
+ .unwrap()
155
+ .insert(p.as_str().to_string());
96
156
  Ok(())
97
157
  }
98
- fn list_targets(&self) -> Result<Vec<crate::transport::PaneInfo>, crate::transport::TransportError> {
158
+ fn list_targets(
159
+ &self,
160
+ ) -> Result<Vec<crate::transport::PaneInfo>, crate::transport::TransportError> {
99
161
  Ok(self
100
162
  .spawn_targets
101
163
  .lock()
@@ -116,23 +178,47 @@ impl crate::transport::Transport for SessionProbeRecordingTransport {
116
178
  })
117
179
  .collect())
118
180
  }
119
- fn has_session(&self, _s: &crate::transport::SessionName) -> Result<bool, crate::transport::TransportError> {
181
+ fn has_session(
182
+ &self,
183
+ _s: &crate::transport::SessionName,
184
+ ) -> Result<bool, crate::transport::TransportError> {
120
185
  Ok(self.session_exists)
121
186
  }
122
- fn list_windows(&self, _s: &crate::transport::SessionName) -> Result<Vec<crate::transport::WindowName>, crate::transport::TransportError> {
187
+ fn list_windows(
188
+ &self,
189
+ _s: &crate::transport::SessionName,
190
+ ) -> Result<Vec<crate::transport::WindowName>, crate::transport::TransportError> {
123
191
  // a dead server has no windows; a live one has the agent window.
124
- Ok(if self.session_exists { vec![crate::transport::WindowName::new("alpha")] } else { Vec::new() })
192
+ Ok(if self.session_exists {
193
+ vec![crate::transport::WindowName::new("alpha")]
194
+ } else {
195
+ Vec::new()
196
+ })
125
197
  }
126
- fn set_session_env(&self, _s: &crate::transport::SessionName, _k: &str, _v: &str) -> Result<crate::transport::SetEnvOutcome, crate::transport::TransportError> {
198
+ fn set_session_env(
199
+ &self,
200
+ _s: &crate::transport::SessionName,
201
+ _k: &str,
202
+ _v: &str,
203
+ ) -> Result<crate::transport::SetEnvOutcome, crate::transport::TransportError> {
127
204
  unimplemented!("not reached")
128
205
  }
129
- fn kill_session(&self, _s: &crate::transport::SessionName) -> Result<(), crate::transport::TransportError> {
206
+ fn kill_session(
207
+ &self,
208
+ _s: &crate::transport::SessionName,
209
+ ) -> Result<(), crate::transport::TransportError> {
130
210
  unimplemented!("not reached")
131
211
  }
132
- fn kill_window(&self, _t: &crate::transport::Target) -> Result<(), crate::transport::TransportError> {
212
+ fn kill_window(
213
+ &self,
214
+ _t: &crate::transport::Target,
215
+ ) -> Result<(), crate::transport::TransportError> {
133
216
  Ok(())
134
217
  }
135
- fn attach_session(&self, _s: &crate::transport::SessionName) -> Result<crate::transport::AttachOutcome, crate::transport::TransportError> {
218
+ fn attach_session(
219
+ &self,
220
+ _s: &crate::transport::SessionName,
221
+ ) -> Result<crate::transport::AttachOutcome, crate::transport::TransportError> {
136
222
  unimplemented!("not reached")
137
223
  }
138
224
  }
@@ -163,10 +249,27 @@ fn start_agent_respawn_into_dead_session_uses_new_session_not_new_window() {
163
249
  let ws = respawn_ws_one_resumable_worker();
164
250
  let spawns = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
165
251
  // session_exists=false models the dead CP-1 `-L` server after --discard-session.
166
- let transport = SessionProbeRecordingTransport { spawns: std::sync::Arc::clone(&spawns), spawn_targets: std::sync::Mutex::new(Vec::new()), session_exists: false, killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()) };
167
- let _ = start_agent_with_transport(&ws, &AgentId::new("alpha"), false, false, false, None, &transport);
252
+ let transport = SessionProbeRecordingTransport {
253
+ spawns: std::sync::Arc::clone(&spawns),
254
+ spawn_targets: std::sync::Mutex::new(Vec::new()),
255
+ session_exists: false,
256
+ killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()),
257
+ };
258
+ let _ = start_agent_with_transport(
259
+ &ws,
260
+ &AgentId::new("alpha"),
261
+ false,
262
+ false,
263
+ false,
264
+ None,
265
+ &transport,
266
+ );
168
267
  let recorded = spawns.lock().unwrap().clone();
169
- assert_eq!(recorded.len(), 1, "exactly one respawn for alpha; got {recorded:?}");
268
+ assert_eq!(
269
+ recorded.len(),
270
+ 1,
271
+ "exactly one respawn for alpha; got {recorded:?}"
272
+ );
170
273
  assert_eq!(
171
274
  recorded[0].0, "spawn_first",
172
275
  "golden runtime.py:1017: when `tmux has-session` is FALSE (dead -L server after \
@@ -195,7 +298,12 @@ fn reset_agent_discard_session_rebuilds_window_via_start_respawn() {
195
298
  let ws = restart_ws_two_resumable_workers(); // compiled spec + state(alpha,bravo running) + seeded coordinator
196
299
  let spawns = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
197
300
  // live session present (other workers keep it alive) but alpha's window was discarded.
198
- let transport = SessionProbeRecordingTransport { spawns: std::sync::Arc::clone(&spawns), spawn_targets: std::sync::Mutex::new(Vec::new()), session_exists: true, killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()) };
301
+ let transport = SessionProbeRecordingTransport {
302
+ spawns: std::sync::Arc::clone(&spawns),
303
+ spawn_targets: std::sync::Mutex::new(Vec::new()),
304
+ session_exists: true,
305
+ killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()),
306
+ };
199
307
  let _ = reset_agent_with_transport(&ws, &AgentId::new("alpha"), true, false, None, &transport);
200
308
  let recorded = spawns.lock().unwrap().clone();
201
309
  assert!(
@@ -233,9 +341,15 @@ fn reset_agent_discard_session_syncs_projection_epoch_inputs_for_restart_agent_c
233
341
 
234
342
  let before = chrono::Utc::now();
235
343
  let spawns = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
236
- let transport = SessionProbeRecordingTransport { spawns: std::sync::Arc::clone(&spawns), spawn_targets: std::sync::Mutex::new(Vec::new()), session_exists: true, killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()) };
237
- let outcome = reset_agent_with_transport(&ws, &AgentId::new("alpha"), true, false, None, &transport)
238
- .expect("reset-agent alpha --discard-session --no-display");
344
+ let transport = SessionProbeRecordingTransport {
345
+ spawns: std::sync::Arc::clone(&spawns),
346
+ spawn_targets: std::sync::Mutex::new(Vec::new()),
347
+ session_exists: true,
348
+ killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()),
349
+ };
350
+ let outcome =
351
+ reset_agent_with_transport(&ws, &AgentId::new("alpha"), true, false, None, &transport)
352
+ .expect("reset-agent alpha --discard-session --no-display");
239
353
  assert!(
240
354
  matches!(outcome, ResetAgentOutcome::Reset { .. }),
241
355
  "restart-agent alpha --discard-session --no-display must complete reset; got {outcome:?}"
@@ -323,7 +437,10 @@ fn bugb_routed_team_ws() -> PathBuf {
323
437
  let yaml = crate::model::yaml::dumps(&spec);
324
438
  // sanity: the UNMODIFIED compiled spec really routes bravo — that route-bravo rule is the dangling
325
439
  // ref the cascade-prune must drop (and which makes golden's validate_spec raise + rollback).
326
- assert!(yaml.contains("assign_to: \"bravo\""), "fixture: compiled spec must carry route-bravo (assign_to bravo); got:\n{yaml}");
440
+ assert!(
441
+ yaml.contains("assign_to: \"bravo\""),
442
+ "fixture: compiled spec must carry route-bravo (assign_to bravo); got:\n{yaml}"
443
+ );
327
444
  std::fs::write(ws.join("team.spec.yaml"), yaml).unwrap();
328
445
  crate::state::persist::save_runtime_state(
329
446
  &ws,
@@ -383,7 +500,10 @@ fn remove_routed_worker_prunes_dangling_routing_rule_divergence() {
383
500
  );
384
501
  let state = crate::state::persist::load_runtime_state(&ws).expect("load state");
385
502
  assert!(
386
- state.get("agents").and_then(serde_json::Value::as_object).is_some_and(|a| !a.contains_key("bravo")),
503
+ state
504
+ .get("agents")
505
+ .and_then(serde_json::Value::as_object)
506
+ .is_some_and(|a| !a.contains_key("bravo")),
387
507
  "bravo must also be removed from state.agents; got {state:?}"
388
508
  );
389
509
  let _ = std::fs::remove_dir_all(&ws);
@@ -720,11 +720,7 @@ impl Transport for BRealTransport {
720
720
  })
721
721
  }
722
722
 
723
- fn query(
724
- &self,
725
- _target: &Target,
726
- _field: PaneField,
727
- ) -> Result<Option<String>, TransportError> {
723
+ fn query(&self, _target: &Target, _field: PaneField) -> Result<Option<String>, TransportError> {
728
724
  Ok(Some("node".to_string()))
729
725
  }
730
726
 
@@ -545,6 +545,9 @@ fn normalize_value(value: Value, ctx: &mut NormalizeCtx, key: Option<&str>) -> V
545
545
  if matches!(key, Some("env_overlay_keys" | "env_unset_keys")) {
546
546
  return json!("<ENV_KEYS>");
547
547
  }
548
+ if matches!(key, Some("env_unset")) {
549
+ return json!([]);
550
+ }
548
551
  match value {
549
552
  Value::Object(map) => {
550
553
  let sorted = map.into_iter().collect::<BTreeMap<_, _>>();
@@ -20,11 +20,11 @@ fn sess(s: &str) -> SessionName {
20
20
  SessionName(s.to_string())
21
21
  }
22
22
 
23
+ mod agent_ops;
23
24
  mod core;
25
+ mod lane_ops;
24
26
  mod launch_spawn;
25
27
  mod lifecycle_lock;
26
- mod agent_ops;
27
- mod lane_ops;
28
28
  mod main_preserved;
29
29
  mod phase_b_contracts;
30
30
  mod phase_golden;
@@ -13,10 +13,10 @@ fn main() -> anyhow::Result<()> {
13
13
  other => return Err(anyhow::anyhow!("unknown fake-worker argument: {other}")),
14
14
  }
15
15
  }
16
- let workspace = workspace
17
- .ok_or_else(|| anyhow::anyhow!("fake-worker requires --workspace <path>"))?;
18
- let agent_id = agent_id
19
- .ok_or_else(|| anyhow::anyhow!("fake-worker requires --agent-id <id>"))?;
16
+ let workspace =
17
+ workspace.ok_or_else(|| anyhow::anyhow!("fake-worker requires --workspace <path>"))?;
18
+ let agent_id =
19
+ agent_id.ok_or_else(|| anyhow::anyhow!("fake-worker requires --agent-id <id>"))?;
20
20
  let stdin = std::io::stdin();
21
21
  let stdout = std::io::stdout();
22
22
  team_agent::fake_worker::run(&workspace, &agent_id, stdin.lock(), stdout.lock())
@@ -3,8 +3,8 @@ use std::path::{Path, PathBuf};
3
3
  use serde_json::Value;
4
4
 
5
5
  use crate::lifecycle::{ResetAgentOutcome, ResetRefusal};
6
- use crate::model::yaml::{self, Value as YamlValue};
7
6
  use crate::model::ids::{AgentId, TeamKey};
7
+ use crate::model::yaml::{self, Value as YamlValue};
8
8
 
9
9
  use super::super::helpers::{enum_value, object_fields, tool_runtime_error};
10
10
  use super::super::{ToolOk, ToolResult};
@@ -126,7 +126,9 @@ pub(crate) fn fork_agent(
126
126
 
127
127
  fn reset_refusal_reason(reason: ResetRefusal) -> Value {
128
128
  match reason {
129
- ResetRefusal::DiscardSessionRequired => Value::String("discard_session_required".to_string()),
129
+ ResetRefusal::DiscardSessionRequired => {
130
+ Value::String("discard_session_required".to_string())
131
+ }
130
132
  }
131
133
  }
132
134
 
@@ -211,7 +213,10 @@ fn state_spec_workspace(state: &Value, team: Option<&str>) -> Option<PathBuf> {
211
213
  state_spec_workspace_from_entry(state).or_else(|| {
212
214
  let teams = state.get("teams").and_then(Value::as_object)?;
213
215
  if teams.len() == 1 {
214
- teams.values().next().and_then(state_spec_workspace_from_entry)
216
+ teams
217
+ .values()
218
+ .next()
219
+ .and_then(state_spec_workspace_from_entry)
215
220
  } else {
216
221
  None
217
222
  }
@@ -294,20 +299,35 @@ fn materialize_mcp_lifecycle_spec(
294
299
  ),
295
300
  (
296
301
  "leader".to_string(),
297
- YamlValue::Map(vec![("provider".to_string(), YamlValue::Str("codex".to_string()))]),
302
+ YamlValue::Map(vec![(
303
+ "provider".to_string(),
304
+ YamlValue::Str("codex".to_string()),
305
+ )]),
298
306
  ),
299
307
  ("agents".to_string(), YamlValue::List(agent_items)),
300
308
  ]);
301
309
  let spec_workspace = workspace.join(".team").join(&team_name);
302
310
  std::fs::create_dir_all(&spec_workspace).map_err(|e| {
303
- tool_runtime_error(format!("create MCP lifecycle spec dir {}: {e}", spec_workspace.display()))
311
+ tool_runtime_error(format!(
312
+ "create MCP lifecycle spec dir {}: {e}",
313
+ spec_workspace.display()
314
+ ))
304
315
  })?;
305
316
  let spec_path = spec_workspace.join("team.spec.yaml");
306
317
  std::fs::write(&spec_path, yaml::dumps(&spec)).map_err(|e| {
307
- tool_runtime_error(format!("write MCP lifecycle spec {}: {e}", spec_path.display()))
318
+ tool_runtime_error(format!(
319
+ "write MCP lifecycle spec {}: {e}",
320
+ spec_path.display()
321
+ ))
308
322
  })?;
309
323
  if prepare_state {
310
- prepare_selected_team_state(workspace, &mut state, &team_name, &spec_workspace, &spec_path)?;
324
+ prepare_selected_team_state(
325
+ workspace,
326
+ &mut state,
327
+ &team_name,
328
+ &spec_workspace,
329
+ &spec_path,
330
+ )?;
311
331
  }
312
332
  Ok(spec_workspace)
313
333
  }
@@ -316,16 +336,15 @@ fn selected_agents<'a>(
316
336
  state: &'a Value,
317
337
  team: Option<&str>,
318
338
  ) -> Option<&'a serde_json::Map<String, Value>> {
319
- team
320
- .and_then(|team| {
321
- state
322
- .get("teams")
323
- .and_then(Value::as_object)
324
- .and_then(|teams| teams.get(team))
325
- .and_then(|entry| entry.get("agents"))
326
- .and_then(Value::as_object)
327
- })
328
- .or_else(|| state.get("agents").and_then(Value::as_object))
339
+ team.and_then(|team| {
340
+ state
341
+ .get("teams")
342
+ .and_then(Value::as_object)
343
+ .and_then(|teams| teams.get(team))
344
+ .and_then(|entry| entry.get("agents"))
345
+ .and_then(Value::as_object)
346
+ })
347
+ .or_else(|| state.get("agents").and_then(Value::as_object))
329
348
  }
330
349
 
331
350
  fn prepare_selected_team_state(
@@ -370,19 +389,27 @@ fn prepare_selected_team_state(
370
389
  if let Some(top_agents) = top_agents {
371
390
  if let Some(team_agents) = entry.get_mut("agents").and_then(Value::as_object_mut) {
372
391
  for (agent_id, top_agent) in top_agents {
373
- let Some(team_agent) = team_agents.get_mut(&agent_id).and_then(Value::as_object_mut) else {
392
+ let Some(team_agent) = team_agents
393
+ .get_mut(&agent_id)
394
+ .and_then(Value::as_object_mut)
395
+ else {
374
396
  continue;
375
397
  };
376
398
  let Some(top_agent) = top_agent.as_object() else {
377
399
  continue;
378
400
  };
379
401
  for (key, value) in top_agent {
380
- team_agent.entry(key.clone()).or_insert_with(|| value.clone());
402
+ team_agent
403
+ .entry(key.clone())
404
+ .or_insert_with(|| value.clone());
381
405
  }
382
406
  }
383
407
  }
384
408
  }
385
409
  crate::state::persist::save_runtime_state(workspace, state).map_err(|e| {
386
- tool_runtime_error(format!("save MCP lifecycle scoped state {}: {e}", workspace.display()))
410
+ tool_runtime_error(format!(
411
+ "save MCP lifecycle scoped state {}: {e}",
412
+ workspace.display()
413
+ ))
387
414
  })
388
415
  }
@@ -56,7 +56,15 @@
56
56
 
57
57
  // ROUND-0 skeleton: fn bodies are all unimplemented!() so imports/fields/methods are
58
58
  // not yet exercised; P2 porter removes this when implementing.
59
- #![allow(dead_code, unused_imports, unused_variables, clippy::result_large_err, clippy::doc_overindented_list_items, clippy::doc_lazy_continuation, clippy::io_other_error)]
59
+ #![allow(
60
+ dead_code,
61
+ unused_imports,
62
+ unused_variables,
63
+ clippy::result_large_err,
64
+ clippy::doc_overindented_list_items,
65
+ clippy::doc_lazy_continuation,
66
+ clippy::io_other_error
67
+ )]
60
68
  // §10:MCP stdio handlers 实现层禁 unwrap/expect/panic(unimplemented!() stub 不被拦);tests 子模块各自 allow。
61
69
  #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
62
70
 
@@ -104,7 +112,8 @@ pub(crate) use helpers::{
104
112
  object_fields, text_field, text_of_value, tool_error_reason_wire, tool_runtime_error,
105
113
  };
106
114
  pub(crate) use normalize::{
107
- normalize_artifacts, normalize_changes, normalize_next_actions, normalize_risks, normalize_tests,
115
+ normalize_artifacts, normalize_changes, normalize_next_actions, normalize_risks,
116
+ normalize_tests,
108
117
  };
109
118
  pub(crate) use wire::dispatch_tool;
110
119
 
@@ -275,7 +275,11 @@ impl ToolError {
275
275
 
276
276
  /// `_tool_error_result(reason, message, exc_type)` (`server.py:98-106`) for the
277
277
  /// `unknown_tool` / `peer_not_in_scope` non-exception paths.
278
- pub fn new(reason: ToolErrorReason, message: impl Into<String>, exc_type: impl Into<String>) -> Self {
278
+ pub fn new(
279
+ reason: ToolErrorReason,
280
+ message: impl Into<String>,
281
+ exc_type: impl Into<String>,
282
+ ) -> Self {
279
283
  Self {
280
284
  reason,
281
285
  exc_type: exc_type.into(),
@@ -289,7 +293,11 @@ impl ToolError {
289
293
  pub fn public_exception_message(raw: &str, exc_type: &str) -> String {
290
294
  let cleaned = raw.replace(['\n', '\r'], " ");
291
295
  let trimmed = cleaned.trim();
292
- let base = if trimmed.is_empty() { exc_type } else { trimmed };
296
+ let base = if trimmed.is_empty() {
297
+ exc_type
298
+ } else {
299
+ trimmed
300
+ };
293
301
  base.chars().take(200).collect()
294
302
  }
295
303
  }
@@ -320,7 +328,10 @@ impl SendOutcome {
320
328
  /// the [`ToolResult`] `Ok` body.
321
329
  pub fn to_value(&self) -> Value {
322
330
  match self {
323
- SendOutcome::WorkerAccepted { message_id, poll_via } => {
331
+ SendOutcome::WorkerAccepted {
332
+ message_id,
333
+ poll_via,
334
+ } => {
324
335
  let mut obj = serde_json::Map::new();
325
336
  obj.insert("status".to_string(), Value::String("accepted".to_string()));
326
337
  obj.insert("delivery_pending".to_string(), Value::Bool(true));