@team-agent/installer 0.5.41 → 0.5.43

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 (152) 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 +27 -7
  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 +47 -52
  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/coordinator/tick.rs +19 -1
  51. package/crates/team-agent/src/db/message_store.rs +138 -30
  52. package/crates/team-agent/src/db/migration.rs +249 -61
  53. package/crates/team-agent/src/db/schema.rs +303 -82
  54. package/crates/team-agent/src/diagnose/comms.rs +9 -2
  55. package/crates/team-agent/src/diagnose/mod.rs +1 -3
  56. package/crates/team-agent/src/diagnose/orphans.rs +79 -61
  57. package/crates/team-agent/src/event_log.rs +70 -16
  58. package/crates/team-agent/src/layout/manager.rs +15 -4
  59. package/crates/team-agent/src/layout/mod.rs +4 -4
  60. package/crates/team-agent/src/layout/overlay.rs +10 -3
  61. package/crates/team-agent/src/layout/placement.rs +5 -1
  62. package/crates/team-agent/src/layout/recovery.rs +4 -2
  63. package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
  64. package/crates/team-agent/src/layout/sessions.rs +17 -9
  65. package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
  66. package/crates/team-agent/src/layout/worker_env.rs +52 -19
  67. package/crates/team-agent/src/leader/helpers.rs +7 -1
  68. package/crates/team-agent/src/leader/lease.rs +195 -82
  69. package/crates/team-agent/src/leader/owner_bind.rs +55 -22
  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/start.rs +75 -54
  73. package/crates/team-agent/src/leader/takeover.rs +46 -11
  74. package/crates/team-agent/src/leader/tests/basics.rs +320 -167
  75. package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
  76. package/crates/team-agent/src/leader/tests/identity.rs +428 -356
  77. package/crates/team-agent/src/leader/tests/idle.rs +285 -254
  78. package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
  79. package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
  80. package/crates/team-agent/src/leader/tests/mod.rs +115 -99
  81. package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
  82. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
  83. package/crates/team-agent/src/lib.rs +4 -4
  84. package/crates/team-agent/src/lifecycle/display.rs +7 -3
  85. package/crates/team-agent/src/lifecycle/launch.rs +6 -1
  86. package/crates/team-agent/src/lifecycle/mod.rs +9 -1
  87. package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
  88. package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
  89. package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
  90. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
  91. package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
  92. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
  93. package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
  94. package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
  95. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
  96. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
  97. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
  98. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
  99. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
  100. package/crates/team-agent/src/lifecycle/tests.rs +2 -2
  101. package/crates/team-agent/src/main.rs +4 -4
  102. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
  103. package/crates/team-agent/src/mcp_server/mod.rs +11 -2
  104. package/crates/team-agent/src/mcp_server/types.rs +14 -3
  105. package/crates/team-agent/src/mcp_server/wire.rs +230 -68
  106. package/crates/team-agent/src/messaging/delivery.rs +20 -18
  107. package/crates/team-agent/src/messaging/helpers.rs +25 -4
  108. package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
  109. package/crates/team-agent/src/messaging/mod.rs +2 -3
  110. package/crates/team-agent/src/messaging/selftest.rs +46 -26
  111. package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
  112. package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
  113. package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
  114. package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
  115. package/crates/team-agent/src/messaging/trust.rs +25 -2
  116. package/crates/team-agent/src/messaging/watchers.rs +37 -9
  117. package/crates/team-agent/src/model/enums.rs +65 -16
  118. package/crates/team-agent/src/model/ids.rs +12 -3
  119. package/crates/team-agent/src/model/paths.rs +28 -7
  120. package/crates/team-agent/src/model/permissions.rs +176 -33
  121. package/crates/team-agent/src/model/routing.rs +66 -20
  122. package/crates/team-agent/src/model/spec.rs +365 -69
  123. package/crates/team-agent/src/model/task_graph.rs +36 -9
  124. package/crates/team-agent/src/model/yaml/tests.rs +24 -6
  125. package/crates/team-agent/src/model/yaml.rs +7 -6
  126. package/crates/team-agent/src/packaging/install.rs +23 -9
  127. package/crates/team-agent/src/packaging/migrate.rs +5 -7
  128. package/crates/team-agent/src/packaging/mod.rs +9 -1
  129. package/crates/team-agent/src/packaging/repair.rs +13 -6
  130. package/crates/team-agent/src/packaging/tests.rs +63 -16
  131. package/crates/team-agent/src/packaging/types.rs +22 -7
  132. package/crates/team-agent/src/platform/argv.rs +4 -1
  133. package/crates/team-agent/src/platform/file_lock.rs +22 -8
  134. package/crates/team-agent/src/platform/process.rs +21 -22
  135. package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
  136. package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
  137. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
  138. package/crates/team-agent/src/provider/classify.rs +127 -42
  139. package/crates/team-agent/src/provider/faults.rs +17 -5
  140. package/crates/team-agent/src/provider/helpers.rs +6 -5
  141. package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
  142. package/crates/team-agent/src/state/repository.rs +27 -14
  143. package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
  144. package/crates/team-agent/src/tmux_backend.rs +80 -44
  145. package/crates/team-agent/src/topology.rs +27 -20
  146. package/crates/team-agent/src/transport/test_support.rs +20 -23
  147. package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
  148. package/crates/team-agent/src/transport/tests/mod.rs +178 -187
  149. package/crates/team-agent/src/transport/tests/wire.rs +561 -525
  150. package/crates/team-agent/src/transport.rs +12 -17
  151. package/crates/team-agent/src/transport_factory.rs +29 -14
  152. package/package.json +4 -4
@@ -174,7 +174,10 @@ fn prepare_profile_launch(
174
174
  claude_config_dir = Some(dir);
175
175
  }
176
176
 
177
- env_overlay.extend(compatible_api_network_exports(agent.auth_mode, &loaded.values));
177
+ env_overlay.extend(compatible_api_network_exports(
178
+ agent.auth_mode,
179
+ &loaded.values,
180
+ ));
178
181
  if agent.auth_mode == AuthMode::CompatibleApi && profile_proxy_mode(&loaded.values) == "direct"
179
182
  {
180
183
  for key in COMPATIBLE_NETWORK_ENV_KEYS {
@@ -307,7 +310,11 @@ fn validate_profile(
307
310
  {
308
311
  let profile_model = profile_model(&loaded.values);
309
312
  if let Some(profile_model) = profile_model {
310
- if agent.model.as_deref().is_some_and(|model| model != profile_model) {
313
+ if agent
314
+ .model
315
+ .as_deref()
316
+ .is_some_and(|model| model != profile_model)
317
+ {
311
318
  return Err(LifecycleError::RequirementUnmet(format!(
312
319
  "role/team model does not match profile MODEL in {}",
313
320
  loaded.path.display()
@@ -320,8 +327,7 @@ fn validate_profile(
320
327
  if key == &"API_KEY" {
321
328
  continue;
322
329
  }
323
- if !profile_value_or_alternate(&loaded.values, key).is_some_and(|value| !value.is_empty())
324
- {
330
+ if !profile_value_or_alternate(&loaded.values, key).is_some_and(|value| !value.is_empty()) {
325
331
  missing.push(*key);
326
332
  }
327
333
  }
@@ -393,16 +399,24 @@ fn provider_env_exports(
393
399
  // Subscription/OfficialApi 不导出 COPILOT_PROVIDER_*(避免误改 auth 通道)。
394
400
  Provider::Copilot => {
395
401
  if auth_mode == AuthMode::CompatibleApi {
396
- if let Some(value) = value_or_alternate(values, "COPILOT_PROVIDER_BASE_URL", "BASE_URL") {
402
+ if let Some(value) =
403
+ value_or_alternate(values, "COPILOT_PROVIDER_BASE_URL", "BASE_URL")
404
+ {
397
405
  exports.insert("COPILOT_PROVIDER_BASE_URL".to_string(), value.to_string());
398
406
  }
399
- if let Some(value) = value_or_alternate(values, "COPILOT_PROVIDER_TYPE", "PROVIDER_TYPE") {
407
+ if let Some(value) =
408
+ value_or_alternate(values, "COPILOT_PROVIDER_TYPE", "PROVIDER_TYPE")
409
+ {
400
410
  exports.insert("COPILOT_PROVIDER_TYPE".to_string(), value.to_string());
401
411
  }
402
- if let Some(value) = value_or_alternate(values, "COPILOT_PROVIDER_API_KEY", "API_KEY") {
412
+ if let Some(value) =
413
+ value_or_alternate(values, "COPILOT_PROVIDER_API_KEY", "API_KEY")
414
+ {
403
415
  exports.insert("COPILOT_PROVIDER_API_KEY".to_string(), value.to_string());
404
416
  }
405
- if let Some(value) = value_or_alternate(values, "COPILOT_PROVIDER_WIRE_API", "WIRE_API") {
417
+ if let Some(value) =
418
+ value_or_alternate(values, "COPILOT_PROVIDER_WIRE_API", "WIRE_API")
419
+ {
406
420
  exports.insert("COPILOT_PROVIDER_WIRE_API".to_string(), value.to_string());
407
421
  }
408
422
  if let Some(value) = value_or_alternate(values, "COPILOT_MODEL", "MODEL") {
@@ -505,8 +519,8 @@ fn provider_command_overrides(
505
519
  // Python provider_env.py:62-79 (_provider_command_overrides codex branch).
506
520
  match agent.provider {
507
521
  Provider::Codex => {
508
- codex_profile = value_or_alternate(values, "CODEX_PROFILE", "NATIVE_PROFILE")
509
- .map(str::to_string);
522
+ codex_profile =
523
+ value_or_alternate(values, "CODEX_PROFILE", "NATIVE_PROFILE").map(str::to_string);
510
524
  let model_provider = values.get("MODEL_PROVIDER").filter(|v| !v.is_empty());
511
525
  let base_url = values.get("BASE_URL").filter(|v| !v.is_empty());
512
526
  if agent.auth_mode == AuthMode::CompatibleApi {
@@ -620,8 +634,14 @@ fn ensure_compatible_claude_config(dir: &Path, workspace: &Path) -> Result<(), L
620
634
  "skipDangerousModePermissionPrompt",
621
635
  serde_json::json!(true),
622
636
  );
623
- settings.insert("hasCompletedOnboarding".to_string(), serde_json::json!(true));
624
- settings.insert("hasTrustDialogAccepted".to_string(), serde_json::json!(true));
637
+ settings.insert(
638
+ "hasCompletedOnboarding".to_string(),
639
+ serde_json::json!(true),
640
+ );
641
+ settings.insert(
642
+ "hasTrustDialogAccepted".to_string(),
643
+ serde_json::json!(true),
644
+ );
625
645
  insert_if_missing(
626
646
  &mut settings,
627
647
  "lastOnboardingVersion",
@@ -642,7 +662,10 @@ fn ensure_compatible_claude_config(dir: &Path, workspace: &Path) -> Result<(), L
642
662
 
643
663
  let state_path = dir.join(".claude.json");
644
664
  let mut state = read_json_object(&state_path)?;
645
- state.insert("hasCompletedOnboarding".to_string(), serde_json::json!(true));
665
+ state.insert(
666
+ "hasCompletedOnboarding".to_string(),
667
+ serde_json::json!(true),
668
+ );
646
669
  insert_if_missing(
647
670
  &mut state,
648
671
  "lastOnboardingVersion",
@@ -665,8 +688,14 @@ fn ensure_compatible_claude_mcp_config(
665
688
  ) -> Result<(), LifecycleError> {
666
689
  let state_path = dir.join(".claude.json");
667
690
  let mut state = read_json_object(&state_path)?;
668
- state.insert("hasCompletedOnboarding".to_string(), serde_json::json!(true));
669
- state.insert("hasTrustDialogAccepted".to_string(), serde_json::json!(true));
691
+ state.insert(
692
+ "hasCompletedOnboarding".to_string(),
693
+ serde_json::json!(true),
694
+ );
695
+ state.insert(
696
+ "hasTrustDialogAccepted".to_string(),
697
+ serde_json::json!(true),
698
+ );
670
699
  insert_if_missing(
671
700
  &mut state,
672
701
  "projectOnboardingSeenCount",
@@ -686,7 +715,10 @@ fn ensure_claude_projects(
686
715
  workspace: &Path,
687
716
  mcp_servers: Option<&serde_json::Value>,
688
717
  ) {
689
- if !state.get("projects").is_some_and(serde_json::Value::is_object) {
718
+ if !state
719
+ .get("projects")
720
+ .is_some_and(serde_json::Value::is_object)
721
+ {
690
722
  state.insert("projects".to_string(), serde_json::json!({}));
691
723
  }
692
724
  let Some(projects) = state
@@ -703,7 +735,10 @@ fn ensure_claude_projects(
703
735
  let Some(project) = entry.as_object_mut() else {
704
736
  continue;
705
737
  };
706
- project.insert("hasTrustDialogAccepted".to_string(), serde_json::json!(true));
738
+ project.insert(
739
+ "hasTrustDialogAccepted".to_string(),
740
+ serde_json::json!(true),
741
+ );
707
742
  insert_if_missing(project, "projectOnboardingSeenCount", serde_json::json!(1));
708
743
  if let Some(mcp_servers) = mcp_servers {
709
744
  insert_if_missing(project, "allowedTools", serde_json::json!([]));
@@ -892,22 +927,26 @@ pub(crate) fn profile_value_or_alternate<'a>(
892
927
  .get(key)
893
928
  .filter(|value| !value.is_empty())
894
929
  .map(String::as_str)
895
- .or_else(|| match key {
896
- "API_KEY" => values
897
- .get("ANTHROPIC_API_KEY")
898
- .or_else(|| values.get("ANTHROPIC_AUTH_TOKEN"))
899
- .or_else(|| values.get("AUTH_TOKEN"))
900
- .or_else(|| values.get("CLAUDE_API_KEY"))
901
- .or_else(|| values.get("CLAUDE_AUTH_TOKEN"))
902
- .or_else(|| values.get("BEARER_TOKEN"))
903
- .or_else(|| values.get("TEAM_AGENT_PROVIDER_API_KEY"))
904
- .or_else(|| values.get("OPENAI_API_KEY")),
905
- "BASE_URL" => values.get("ANTHROPIC_BASE_URL").or_else(|| values.get("OPENAI_BASE_URL")),
906
- "MODEL" => values.get("ANTHROPIC_MODEL"),
907
- _ => None,
908
- }
909
- .filter(|value| !value.is_empty())
910
- .map(String::as_str))
930
+ .or_else(|| {
931
+ match key {
932
+ "API_KEY" => values
933
+ .get("ANTHROPIC_API_KEY")
934
+ .or_else(|| values.get("ANTHROPIC_AUTH_TOKEN"))
935
+ .or_else(|| values.get("AUTH_TOKEN"))
936
+ .or_else(|| values.get("CLAUDE_API_KEY"))
937
+ .or_else(|| values.get("CLAUDE_AUTH_TOKEN"))
938
+ .or_else(|| values.get("BEARER_TOKEN"))
939
+ .or_else(|| values.get("TEAM_AGENT_PROVIDER_API_KEY"))
940
+ .or_else(|| values.get("OPENAI_API_KEY")),
941
+ "BASE_URL" => values
942
+ .get("ANTHROPIC_BASE_URL")
943
+ .or_else(|| values.get("OPENAI_BASE_URL")),
944
+ "MODEL" => values.get("ANTHROPIC_MODEL"),
945
+ _ => None,
946
+ }
947
+ .filter(|value| !value.is_empty())
948
+ .map(String::as_str)
949
+ })
911
950
  }
912
951
 
913
952
  pub(crate) fn value_or_alternate<'a>(
@@ -926,7 +965,11 @@ pub(crate) fn profile_model(values: &BTreeMap<String, String>) -> Option<&str> {
926
965
  values
927
966
  .get("MODEL")
928
967
  .filter(|value| !value.is_empty())
929
- .or_else(|| values.get("ANTHROPIC_MODEL").filter(|value| !value.is_empty()))
968
+ .or_else(|| {
969
+ values
970
+ .get("ANTHROPIC_MODEL")
971
+ .filter(|value| !value.is_empty())
972
+ })
930
973
  .map(String::as_str)
931
974
  }
932
975
 
@@ -187,7 +187,9 @@ impl SmokeTarget {
187
187
  }
188
188
  // C-7-1 cr verdict: copilot 一期 subscription-only,无 BYOK HTTP smoke
189
189
  // 入口;同 GeminiCli/Fake 走 unsupported_provider_smoke_skipped。
190
- Provider::Copilot | Provider::GeminiCli | Provider::Fake => Err("unsupported_provider_smoke_skipped"),
190
+ Provider::Copilot | Provider::GeminiCli | Provider::Fake => {
191
+ Err("unsupported_provider_smoke_skipped")
192
+ }
191
193
  }
192
194
  }
193
195
 
@@ -826,12 +826,7 @@ pub(crate) fn start_agent_at_paths_for_recovery(
826
826
  // subsequent start creates a fresh provider process instead of a Noop.
827
827
  // Stop errors are absorbed into the start attempt result — the important
828
828
  // invariant is that a successful start returns Running, never Noop.
829
- let _ = stop_agent_with_transport(
830
- &paths.run_workspace,
831
- agent_id,
832
- team,
833
- transport,
834
- );
829
+ let _ = stop_agent_with_transport(&paths.run_workspace, agent_id, team, transport);
835
830
  let start_result = start_agent_with_transport(
836
831
  &paths.run_workspace,
837
832
  agent_id,
@@ -26,10 +26,7 @@ pub fn halt_plan(
26
26
  }
27
27
 
28
28
  /// `plan_status(workspace, plan_id)`(`orchestrator/__init__.py:177`)。读 plan 持久态。
29
- pub fn plan_status(
30
- workspace: &Path,
31
- plan_id: &PlanId,
32
- ) -> Result<PlanState, LifecycleError> {
29
+ pub fn plan_status(workspace: &Path, plan_id: &PlanId) -> Result<PlanState, LifecycleError> {
33
30
  let path = plan_state_path(workspace, plan_id);
34
31
  if !path.exists() {
35
32
  return Err(LifecycleError::InvalidPlan(format!(
@@ -37,11 +37,12 @@ impl SessionPreflight {
37
37
  /// kill is safe. Pure: no I/O, no mutation.
38
38
  pub fn check_session_preflight(state: &serde_json::Value) -> SessionPreflight {
39
39
  let sessions = RuntimeSessions::from_state(state);
40
- if let Some(anomaly) = sessions
41
- .anomalies
42
- .iter()
43
- .find(|a| matches!(a, RuntimeSessionAnomaly::WorkerSessionNameIsLeaderPrefixed { .. }))
44
- {
40
+ if let Some(anomaly) = sessions.anomalies.iter().find(|a| {
41
+ matches!(
42
+ a,
43
+ RuntimeSessionAnomaly::WorkerSessionNameIsLeaderPrefixed { .. }
44
+ )
45
+ }) {
45
46
  if let RuntimeSessionAnomaly::WorkerSessionNameIsLeaderPrefixed { value } = anomaly {
46
47
  return SessionPreflight::WorkerSessionIsLeaderSession {
47
48
  session_name: value.clone(),
@@ -523,7 +523,10 @@ fn restart_with_selected_team_and_transport(
523
523
  // as pre-0.5.38 (fake harness + missing agent branches). Where a
524
524
  // parallel Some(Ok(spawn)) is present we skip the transport spawn
525
525
  // and jump straight to verify + mark.
526
- let parallel_result = parallel_outcomes.get(decision_index).cloned().unwrap_or(None);
526
+ let parallel_result = parallel_outcomes
527
+ .get(decision_index)
528
+ .cloned()
529
+ .unwrap_or(None);
527
530
  if let Some(parallel_result) = parallel_result {
528
531
  match parallel_result {
529
532
  Ok(pspawn) => {
@@ -642,11 +645,7 @@ fn restart_with_selected_team_and_transport(
642
645
  "tmux_server_crashed: session {} disappeared during replacement build; refusing to touch original agent state",
643
646
  session_name.as_str()
644
647
  );
645
- failed_agents.push(restart_failed_agent(
646
- decision,
647
- "tmux_server_crashed",
648
- error,
649
- ));
648
+ failed_agents.push(restart_failed_agent(decision, "tmux_server_crashed", error));
650
649
  continue;
651
650
  }
652
651
  if let Some(previous) = successful_agents.pop() {
@@ -741,10 +740,9 @@ fn restart_with_selected_team_and_transport(
741
740
  continue;
742
741
  }
743
742
  let pane_verify_ms = u64::try_from(verify_start.elapsed().as_millis()).unwrap_or(u64::MAX);
744
- let transport_spawn_ms = u64::try_from(
745
- (verify_start.saturating_duration_since(spawn_start)).as_millis(),
746
- )
747
- .unwrap_or(u64::MAX);
743
+ let transport_spawn_ms =
744
+ u64::try_from((verify_start.saturating_duration_since(spawn_start)).as_millis())
745
+ .unwrap_or(u64::MAX);
748
746
  // 0.5.38 Step 1: per-worker spawn timing so operators can identify
749
747
  // whether wall time is spent in command-plan compilation, transport
750
748
  // spawn, pane verification, or provider startup prompts.
@@ -1096,7 +1094,11 @@ fn restart_with_selected_team_and_transport(
1096
1094
  drop(lifecycle_lock);
1097
1095
  let coordinator =
1098
1096
  start_coordinator_for_workspace(&selected.run_workspace, Some(&selected.team_key))?;
1099
- phase_timer.emit(&selected.run_workspace, "restart.phase", "coordinator_start");
1097
+ phase_timer.emit(
1098
+ &selected.run_workspace,
1099
+ "restart.phase",
1100
+ "coordinator_start",
1101
+ );
1100
1102
  let coordinator_started = coordinator.ok;
1101
1103
  phase_timer.emit(&selected.run_workspace, "restart.phase", "readiness_wait");
1102
1104
  wait_restart_readiness_or_timeout(
@@ -2420,12 +2422,11 @@ fn run_bounded_parallel_worker_spawns(
2420
2422
  let _ = concurrency_placeholder(inputs_len); // reserved for future config
2421
2423
  let inputs_shared: Vec<Option<SpawnInput>> = inputs.into_iter().map(Some).collect();
2422
2424
  let inputs_shared = std::sync::Arc::new(std::sync::Mutex::new(inputs_shared));
2423
- let next_submit_slot = std::sync::Arc::new((
2424
- std::sync::Mutex::new(0usize),
2425
- std::sync::Condvar::new(),
2426
- ));
2427
- let results: std::sync::Arc<std::sync::Mutex<Vec<(usize, Result<ParallelSpawnResult, String>)>>> =
2428
- std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
2425
+ let next_submit_slot =
2426
+ std::sync::Arc::new((std::sync::Mutex::new(0usize), std::sync::Condvar::new()));
2427
+ let results: std::sync::Arc<
2428
+ std::sync::Mutex<Vec<(usize, Result<ParallelSpawnResult, String>)>>,
2429
+ > = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
2429
2430
  std::thread::scope(|scope| {
2430
2431
  for slot in 0..inputs_len {
2431
2432
  let inputs_shared = std::sync::Arc::clone(&inputs_shared);
@@ -2530,8 +2531,8 @@ fn apply_marked_respawn(
2530
2531
  fatal_resume_failure: &mut bool,
2531
2532
  ) {
2532
2533
  let verify_start = std::time::Instant::now();
2533
- if let Err(error) = verify_spawned_agent_live(&decision.agent_id, spawn, transport)
2534
- .and_then(|_| {
2534
+ if let Err(error) =
2535
+ verify_spawned_agent_live(&decision.agent_id, spawn, transport).and_then(|_| {
2535
2536
  mark_agent_respawned(
2536
2537
  state,
2537
2538
  &decision.agent_id,
@@ -2553,9 +2554,12 @@ fn apply_marked_respawn(
2553
2554
  return;
2554
2555
  }
2555
2556
  let pane_verify_ms = u64::try_from(verify_start.elapsed().as_millis()).unwrap_or(u64::MAX);
2556
- let transport_spawn_ms =
2557
- u64::try_from(verify_start.saturating_duration_since(spawn_start).as_millis())
2558
- .unwrap_or(u64::MAX);
2557
+ let transport_spawn_ms = u64::try_from(
2558
+ verify_start
2559
+ .saturating_duration_since(spawn_start)
2560
+ .as_millis(),
2561
+ )
2562
+ .unwrap_or(u64::MAX);
2559
2563
  write_worker_spawn_timing_event(
2560
2564
  run_workspace,
2561
2565
  phase_timer.elapsed_ms(),
@@ -1,7 +1,9 @@
1
- use super::*;
2
- use super::agent::{resolve_team_scoped_state_or_refuse, start_agent_at_paths, stop_agent_at_paths};
1
+ use super::agent::{
2
+ resolve_team_scoped_state_or_refuse, start_agent_at_paths, stop_agent_at_paths,
3
+ };
3
4
  use super::common::*;
4
5
  use super::team_state::write_team_state;
6
+ use super::*;
5
7
  use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest};
6
8
 
7
9
  /// `remove_agent(workspace, agent_id, from_spec, force, team)`(`lifecycle/agents.py:22`)。
@@ -21,8 +23,7 @@ pub fn remove_agent(
21
23
  team,
22
24
  agent_id: Some(agent_id),
23
25
  })?;
24
- let transport =
25
- lifecycle_worker_tmux_backend_for_selected_state(&paths.run_workspace, team)?;
26
+ let transport = lifecycle_worker_tmux_backend_for_selected_state(&paths.run_workspace, team)?;
26
27
  remove_agent_at_paths(
27
28
  &paths.run_workspace,
28
29
  &paths.spec_workspace,
@@ -66,8 +67,7 @@ pub fn remove_agent_flag_requirements(
66
67
  team: Option<&str>,
67
68
  ) -> Result<RemoveAgentFlagRequirements, LifecycleError> {
68
69
  let paths = lifecycle_paths(workspace, team)?;
69
- let transport =
70
- lifecycle_worker_tmux_backend_for_selected_state(&paths.run_workspace, team)?;
70
+ let transport = lifecycle_worker_tmux_backend_for_selected_state(&paths.run_workspace, team)?;
71
71
  Ok(remove_agent_preflight(
72
72
  &paths.run_workspace,
73
73
  &paths.spec_workspace,
@@ -207,7 +207,8 @@ fn remove_agent_at_paths(
207
207
  // golden agents.py:110-133: restore is best-effort (collects per-artifact errors, restores ALL),
208
208
  // and the ORIGINAL operation error is ALWAYS re-raised, annotated with rollback_ok — a
209
209
  // restore-step failure only flips rollback_ok, it never replaces the surfaced cause.
210
- let restore_errors = rollback.restore(paths.run_workspace, paths.spec_workspace, team, transport);
210
+ let restore_errors =
211
+ rollback.restore(paths.run_workspace, paths.spec_workspace, team, transport);
211
212
  let rollback_ok = restore_errors.is_empty();
212
213
  let rollback_event = RemoveRollbackEvent {
213
214
  agent_id,
@@ -243,7 +244,13 @@ fn remove_agent_inner(
243
244
  let mut stopped = false;
244
245
  let mut cleared_locations = Vec::new();
245
246
  if force && agent_is_running(&working_state, agent_id, transport) {
246
- let stop = stop_agent_at_paths(paths.run_workspace, paths.spec_workspace, agent_id, team, transport)?;
247
+ let stop = stop_agent_at_paths(
248
+ paths.run_workspace,
249
+ paths.spec_workspace,
250
+ agent_id,
251
+ team,
252
+ transport,
253
+ )?;
247
254
  stopped = stop.stopped;
248
255
  write_remove_step_event(
249
256
  paths.run_workspace,
@@ -265,7 +272,7 @@ fn remove_agent_inner(
265
272
  &removed_state,
266
273
  &[agent_id.as_str()],
267
274
  )
268
- .map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
275
+ .map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
269
276
  cleared_locations.push(serde_json::json!("state.json:agents"));
270
277
  write_remove_step_event(
271
278
  paths.run_workspace,
@@ -282,7 +289,9 @@ fn remove_agent_inner(
282
289
  }
283
290
  // golden agents.py:96-100,157: state_file = the team_state.md path written from removed_spec/state.
284
291
  let team_state_path = write_team_state(paths.spec_workspace, &removed_spec, &removed_state)?;
285
- cleared_locations.push(serde_json::json!(team_state_path.to_string_lossy().to_string()));
292
+ cleared_locations.push(serde_json::json!(team_state_path
293
+ .to_string_lossy()
294
+ .to_string()));
286
295
  write_remove_step_event(
287
296
  paths.run_workspace,
288
297
  agent_id,
@@ -290,8 +299,11 @@ fn remove_agent_inner(
290
299
  &team_state_path.to_string_lossy(),
291
300
  None,
292
301
  )?;
293
- std::fs::write(paths.spec_workspace.join("team.spec.yaml"), yaml::dumps(&removed_spec))
294
- .map_err(|e| LifecycleError::StatePersist(format!("write spec: {e}")))?;
302
+ std::fs::write(
303
+ paths.spec_workspace.join("team.spec.yaml"),
304
+ yaml::dumps(&removed_spec),
305
+ )
306
+ .map_err(|e| LifecycleError::StatePersist(format!("write spec: {e}")))?;
295
307
  cleared_locations.push(serde_json::json!("team.spec.yaml"));
296
308
  write_remove_step_event(
297
309
  paths.run_workspace,
@@ -519,7 +531,11 @@ fn runtime_without_startup_agent(runtime: &YamlValue, agent_id: &AgentId) -> Yam
519
531
  .map(|items| {
520
532
  items
521
533
  .iter()
522
- .filter(|item| item.as_str().map(|id| id != agent_id.as_str()).unwrap_or(true))
534
+ .filter(|item| {
535
+ item.as_str()
536
+ .map(|id| id != agent_id.as_str())
537
+ .unwrap_or(true)
538
+ })
523
539
  .cloned()
524
540
  .collect::<Vec<_>>()
525
541
  })
@@ -538,9 +554,7 @@ fn routing_without_agent(routing: &YamlValue, agent_id: &AgentId) -> YamlValue {
538
554
  };
539
555
  let mut out = Vec::new();
540
556
  for (key, value) in pairs {
541
- if key == "default_assignee"
542
- && value.as_str().is_some_and(|id| id == agent_id.as_str())
543
- {
557
+ if key == "default_assignee" && value.as_str().is_some_and(|id| id == agent_id.as_str()) {
544
558
  out.push((key.clone(), YamlValue::Str(String::new())));
545
559
  } else if key == "rules" {
546
560
  let rules = value
@@ -549,8 +563,7 @@ fn routing_without_agent(routing: &YamlValue, agent_id: &AgentId) -> YamlValue {
549
563
  items
550
564
  .iter()
551
565
  .filter(|rule| {
552
- rule
553
- .get("assign_to")
566
+ rule.get("assign_to")
554
567
  .and_then(YamlValue::as_str)
555
568
  .map(|id| id != agent_id.as_str())
556
569
  .unwrap_or(true)
@@ -587,11 +600,7 @@ fn task_without_agent_assignee(task: &YamlValue, agent_id: &AgentId) -> YamlValu
587
600
  pairs
588
601
  .iter()
589
602
  .map(|(key, value)| {
590
- if key == "assignee"
591
- && value
592
- .as_str()
593
- .is_some_and(|id| id == agent_id.as_str())
594
- {
603
+ if key == "assignee" && value.as_str().is_some_and(|id| id == agent_id.as_str()) {
595
604
  (key.clone(), YamlValue::Str(String::new()))
596
605
  } else {
597
606
  (key.clone(), value.clone())
@@ -601,18 +610,12 @@ fn task_without_agent_assignee(task: &YamlValue, agent_id: &AgentId) -> YamlValu
601
610
  )
602
611
  }
603
612
 
604
- fn remove_dynamic_role_file(
605
- path: &Path,
606
- required: bool,
607
- ) -> Result<bool, LifecycleError> {
613
+ fn remove_dynamic_role_file(path: &Path, required: bool) -> Result<bool, LifecycleError> {
608
614
  match std::fs::remove_file(path) {
609
615
  Ok(()) => Ok(true),
610
- Err(e) if e.kind() == std::io::ErrorKind::NotFound && required => {
611
- Err(LifecycleError::StatePersist(format!(
612
- "dynamic role file missing: {}",
613
- path.display()
614
- )))
615
- }
616
+ Err(e) if e.kind() == std::io::ErrorKind::NotFound && required => Err(
617
+ LifecycleError::StatePersist(format!("dynamic role file missing: {}", path.display())),
618
+ ),
616
619
  Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
617
620
  Err(e) => Err(LifecycleError::StatePersist(format!(
618
621
  "remove role file {}: {e}",
@@ -660,7 +663,10 @@ fn delete_agent_health(workspace: &Path, agent_id: &AgentId) -> Result<bool, Lif
660
663
  let conn = crate::db::schema::open_db(store.db_path())
661
664
  .map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
662
665
  let changed = conn
663
- .execute("delete from agent_health where agent_id = ?1", [agent_id.as_str()])
666
+ .execute(
667
+ "delete from agent_health where agent_id = ?1",
668
+ [agent_id.as_str()],
669
+ )
664
670
  .map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
665
671
  Ok(changed > 0)
666
672
  }
@@ -739,7 +745,11 @@ impl RemoveRollback {
739
745
  let team_state_text = match std::fs::read_to_string(&team_state_path) {
740
746
  Ok(text) => Some(text),
741
747
  Err(e) if e.kind() == std::io::ErrorKind::NotFound => None,
742
- Err(e) => return Err(LifecycleError::StatePersist(format!("read team_state: {e}"))),
748
+ Err(e) => {
749
+ return Err(LifecycleError::StatePersist(format!(
750
+ "read team_state: {e}"
751
+ )))
752
+ }
743
753
  };
744
754
  let dynamic_role_path = dynamic_role_file_path(workspace, state, agent_id);
745
755
  let dynamic_role_bytes = match std::fs::read(&dynamic_role_path) {