@team-agent/installer 0.5.92 → 0.5.97

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 (32) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +381 -157
  4. package/crates/team-agent/src/cli/diagnose.rs +40 -10
  5. package/crates/team-agent/src/cli/emit.rs +6 -4
  6. package/crates/team-agent/src/cli/mod.rs +483 -470
  7. package/crates/team-agent/src/cli/models.rs +1 -0
  8. package/crates/team-agent/src/cli/spec.rs +2 -2
  9. package/crates/team-agent/src/cli/tests/base.rs +20 -10
  10. package/crates/team-agent/src/cli/tests/divergence.rs +12 -21
  11. package/crates/team-agent/src/cli/tests/main_preserved.rs +6 -1
  12. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +20 -11
  13. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +197 -30
  14. package/crates/team-agent/src/cli/triage.rs +31 -31
  15. package/crates/team-agent/src/cli/types.rs +3 -1
  16. package/crates/team-agent/src/coordinator/health.rs +92 -30
  17. package/crates/team-agent/src/coordinator/tick.rs +62 -8
  18. package/crates/team-agent/src/coordinator/types.rs +2 -0
  19. package/crates/team-agent/src/kill_audit.rs +41 -2
  20. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +48 -14
  21. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +4 -8
  22. package/crates/team-agent/src/lifecycle/launch.rs +17 -0
  23. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -0
  24. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +10 -4
  25. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +10 -7
  26. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +91 -4
  27. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +128 -11
  28. package/crates/team-agent/src/platform/process.rs +4 -45
  29. package/crates/team-agent/src/tmux_backend.rs +113 -3
  30. package/crates/team-agent/src/transport/test_support.rs +30 -0
  31. package/crates/team-agent/src/transport.rs +41 -0
  32. package/package.json +4 -4
@@ -161,6 +161,19 @@ pub(crate) fn diagnose_runtime(state: &Value, backend: &dyn Transport) -> (Value
161
161
  }
162
162
  issues.push(issue);
163
163
  }
164
+ // Preserve distinct worker failures even when the shared session probe
165
+ // short-circuits pane enumeration. This keeps scope evidence without
166
+ // issuing another transport probe per worker.
167
+ if let Some(agents) = state.get("agents").and_then(Value::as_object) {
168
+ for agent_id in agents.keys() {
169
+ let issue = format!("worker_window_missing:{agent_id}");
170
+ issues.push(json!(issue.clone()));
171
+ repairs.push(json!({
172
+ "issue": issue,
173
+ "action": "team-agent restart",
174
+ }));
175
+ }
176
+ }
164
177
  return (Value::Array(issues), Value::Array(repairs));
165
178
  }
166
179
 
@@ -289,6 +302,7 @@ pub(crate) fn diagnose_runtime_for_workspace(
289
302
  state: &Value,
290
303
  backend: &dyn Transport,
291
304
  selected_team_key: Option<&str>,
305
+ coordinator_health: &crate::coordinator::HealthReport,
292
306
  ) -> (Value, Value) {
293
307
  let (mut issues, mut repairs) = diagnose_runtime(state, backend);
294
308
  append_live_leader_workspace_mismatch_issue(
@@ -306,7 +320,7 @@ pub(crate) fn diagnose_runtime_for_workspace(
306
320
  &mut repairs,
307
321
  );
308
322
  append_legacy_snapshot_issue(workspace, state, &mut issues);
309
- append_coordinator_health_issue(workspace, state, &mut issues, &mut repairs);
323
+ append_coordinator_health_issue(workspace, state, coordinator_health, &mut issues, &mut repairs);
310
324
  append_runtime_bindings_stale_after_boot_issue(workspace, state, &mut issues, &mut repairs);
311
325
  (issues, repairs)
312
326
  }
@@ -322,8 +336,27 @@ pub(crate) fn workspace_has_existing_team_runtime(
322
336
  workspace: &std::path::Path,
323
337
  team_key: &str,
324
338
  ) -> bool {
325
- workspace.join("team.spec.yaml").is_file()
326
- || crate::model::paths::runtime_spec_path(workspace, team_key).is_file()
339
+ // A source TEAM.md/team.spec.yaml is install/configuration context, not a
340
+ // live runtime. Only persisted runtime state/specs make topology checks
341
+ // applicable; an empty workspace therefore remains a healthy no-runtime
342
+ // report instead of a fabricated missing-session issue.
343
+ if let Ok(state) = crate::state::persist::load_runtime_state_without_migrations(workspace) {
344
+ // The workspace's top-level view may describe a stopped sibling.
345
+ // Preserve the top-level fallback for legacy single-team state only
346
+ // when there is no selected team entry.
347
+ let team_state = state
348
+ .get("teams")
349
+ .and_then(|teams| teams.get(team_key))
350
+ .unwrap_or(&state);
351
+ let terminal = team_state
352
+ .get("status")
353
+ .and_then(Value::as_str)
354
+ .is_some_and(|status| matches!(status, "stopped" | "shutdown" | "archived" | "terminal"));
355
+ if terminal {
356
+ return false;
357
+ }
358
+ }
359
+ crate::model::paths::runtime_spec_path(workspace, team_key).is_file()
327
360
  || crate::state::persist::runtime_state_path(workspace).is_file()
328
361
  }
329
362
 
@@ -790,21 +823,18 @@ fn append_legacy_snapshot_issue(workspace: &std::path::Path, state: &Value, issu
790
823
  fn append_coordinator_health_issue(
791
824
  workspace: &std::path::Path,
792
825
  state: &Value,
826
+ health: &crate::coordinator::HealthReport,
793
827
  issues: &mut Value,
794
828
  repairs: &mut Value,
795
829
  ) {
796
- // Diagnose must observe coordinator state without initializing or rewriting
797
- // `.team/runtime/team.db`, including when the file is missing or empty.
798
- let workspace = crate::coordinator::WorkspacePath::new(workspace.to_path_buf());
799
- let health = crate::coordinator::coordinator_health_read_only(&workspace);
800
- let Some(id) = coordinator_issue_id(state, &health) else {
830
+ let Some(id) = coordinator_issue_id(state, health) else {
801
831
  return;
802
832
  };
803
833
  if let Some(items) = issues.as_array_mut() {
804
- items.push(coordinator_issue_value(id, &health, workspace.as_path()));
834
+ items.push(coordinator_issue_value(id, health, workspace));
805
835
  }
806
836
  if let Some(items) = repairs.as_array_mut() {
807
- items.push(coordinator_repair_hint(id, &health));
837
+ items.push(coordinator_repair_hint(id, health));
808
838
  }
809
839
  }
810
840
 
@@ -225,7 +225,8 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
225
225
  "profile" => cmd_profile(&profile_args(args, cwd)?).map(emit_result),
226
226
  "results" => cmd_results(&results_args(args, cwd)?).map(emit_result),
227
227
  "wait" => cmd_wait(&wait_args(args, cwd)?).map(emit_result),
228
- "diagnose" => cmd_diagnose(&diagnose_args(args, cwd)).map(emit_result),
228
+ // Compatibility spelling: parse and execute the exact doctor contract.
229
+ "diagnose" => cmd_doctor(&doctor_args(args, cwd)).map(emit_result),
229
230
  "preflight" => cmd_preflight(&preflight_args(args, cwd)).map(emit_result),
230
231
  "wait-ready" => cmd_wait_ready(&wait_ready_args(args, cwd)).map(emit_result),
231
232
  "e2e" => cmd_e2e(&e2e_args(args, cwd)).map(emit_result),
@@ -340,7 +341,7 @@ pub(crate) fn default_help() -> String {
340
341
  "reset-agent",
341
342
  ],
342
343
  );
343
- append_help_section(&mut out, "Diagnose", &["diagnose"]);
344
+ append_help_section(&mut out, "Diagnose", &["doctor"]);
344
345
  append_help_section(
345
346
  &mut out,
346
347
  "Guided recovery",
@@ -444,7 +445,7 @@ fn command_help(command: Option<&str>) -> String {
444
445
  Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--proxy-mode direct|inherit] [--json]".to_string(),
445
446
  Some("results") => "usage: team-agent results --case CASE_ID [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
446
447
  Some("wait") => "usage: team-agent wait --task TASK [--workspace WORKSPACE] [--json]".to_string(),
447
- Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
448
+ Some("diagnose") => "usage: team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--gate orphans|comms] [--comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]".to_string(),
448
449
  Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
449
450
  Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--team TEAM] [--timeout SECONDS] [--json]".to_string(),
450
451
  Some("e2e") => "usage: team-agent e2e [--workspace WORKSPACE] [--providers LIST] [--real] [--json]".to_string(),
@@ -2172,7 +2173,8 @@ mod tests {
2172
2173
  fn hidden_commands_not_in_default_help() {
2173
2174
  let top_help = command_help(None);
2174
2175
  let visible = visible_help_commands(&top_help);
2175
- for command in ["leaders", "doctor", "e2e", "peek", "coordinator"] {
2176
+ assert!(visible.iter().any(|command| command == "doctor"));
2177
+ for command in ["leaders", "diagnose", "e2e", "peek", "coordinator"] {
2176
2178
  assert!(
2177
2179
  !visible.iter().any(|visible| visible == command),
2178
2180
  "`{command}` must stay hidden from default help"