@team-agent/installer 0.3.3 → 0.3.5

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 (63) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +8 -0
  4. package/crates/team-agent/src/cli/diagnose.rs +52 -11
  5. package/crates/team-agent/src/cli/emit.rs +3 -2
  6. package/crates/team-agent/src/cli/mod.rs +225 -80
  7. package/crates/team-agent/src/cli/send.rs +1 -0
  8. package/crates/team-agent/src/cli/status_port.rs +135 -7
  9. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +8 -1
  10. package/crates/team-agent/src/cli/tests/mod.rs +1 -0
  11. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +39 -0
  12. package/crates/team-agent/src/cli/types.rs +5 -1
  13. package/crates/team-agent/src/compiler/tests.rs +2 -2
  14. package/crates/team-agent/src/compiler.rs +1 -1
  15. package/crates/team-agent/src/coordinator/backoff.rs +57 -9
  16. package/crates/team-agent/src/coordinator/health.rs +65 -2
  17. package/crates/team-agent/src/coordinator/runtime_detectors.rs +28 -16
  18. package/crates/team-agent/src/coordinator/tests/a0_lostupdate.rs +87 -0
  19. package/crates/team-agent/src/coordinator/tests/mod.rs +1 -0
  20. package/crates/team-agent/src/coordinator/tests/watch.rs +4 -2
  21. package/crates/team-agent/src/coordinator/tick.rs +195 -43
  22. package/crates/team-agent/src/leader/helpers.rs +2 -0
  23. package/crates/team-agent/src/leader/rediscover.rs +1 -0
  24. package/crates/team-agent/src/leader/start.rs +9 -1
  25. package/crates/team-agent/src/leader/takeover.rs +18 -1
  26. package/crates/team-agent/src/lifecycle/display.rs +3 -3
  27. package/crates/team-agent/src/lifecycle/launch.rs +772 -285
  28. package/crates/team-agent/src/lifecycle/mod.rs +1 -0
  29. package/crates/team-agent/src/lifecycle/profile_launch.rs +110 -4
  30. package/crates/team-agent/src/lifecycle/profile_smoke.rs +4 -1
  31. package/crates/team-agent/src/lifecycle/restart/agent.rs +16 -5
  32. package/crates/team-agent/src/lifecycle/restart/common.rs +35 -25
  33. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +31 -25
  34. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +2 -2
  35. package/crates/team-agent/src/lifecycle/tests/core.rs +5 -5
  36. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +4 -4
  37. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +5 -3
  38. package/crates/team-agent/src/lifecycle/types.rs +4 -0
  39. package/crates/team-agent/src/lifecycle/worker_command_context.rs +361 -0
  40. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +2 -1
  41. package/crates/team-agent/src/mcp_server/tests/scoped.rs +14 -1
  42. package/crates/team-agent/src/mcp_server/tests/send.rs +15 -1
  43. package/crates/team-agent/src/mcp_server/tools.rs +65 -9
  44. package/crates/team-agent/src/mcp_server/wire.rs +2 -1
  45. package/crates/team-agent/src/message_store.rs +80 -0
  46. package/crates/team-agent/src/messaging/results.rs +76 -5
  47. package/crates/team-agent/src/messaging/send.rs +3 -1
  48. package/crates/team-agent/src/messaging/types.rs +15 -1
  49. package/crates/team-agent/src/messaging/watchers.rs +68 -30
  50. package/crates/team-agent/src/model/enums.rs +7 -1
  51. package/crates/team-agent/src/model/permissions.rs +7 -0
  52. package/crates/team-agent/src/model/spec.rs +3 -1
  53. package/crates/team-agent/src/provider/adapter.rs +472 -7
  54. package/crates/team-agent/src/provider/classify.rs +6 -2
  55. package/crates/team-agent/src/provider/faults.rs +3 -2
  56. package/crates/team-agent/src/provider/startup_prompt.rs +25 -7
  57. package/crates/team-agent/src/provider/types.rs +11 -0
  58. package/crates/team-agent/src/session_capture.rs +1 -0
  59. package/crates/team-agent/src/state/persist.rs +95 -19
  60. package/crates/team-agent/src/tmux_backend/tests.rs +8 -7
  61. package/crates/team-agent/src/tmux_backend.rs +134 -6
  62. package/crates/team-agent/src/transport.rs +32 -0
  63. package/package.json +4 -4
package/Cargo.lock CHANGED
@@ -566,7 +566,7 @@ dependencies = [
566
566
 
567
567
  [[package]]
568
568
  name = "team-agent"
569
- version = "0.3.3"
569
+ version = "0.3.5"
570
570
  dependencies = [
571
571
  "anyhow",
572
572
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent"]
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.3.3"
12
+ version = "0.3.5"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -1490,6 +1490,14 @@ pub fn cmd_doctor(args: &DoctorArgs) -> Result<CmdResult, CliError> {
1490
1490
  if args.fix && args.gate.is_none() {
1491
1491
  return Err(CliError::Runtime("--fix requires --gate".to_string()));
1492
1492
  }
1493
+ // swallow batch 3 ①: an unknown gate refuses explicitly (Python commands.py:234-235
1494
+ // `unknown doctor gate`), never an empty default-doctor green.
1495
+ if let Some(DoctorGate::Unknown(raw)) = &args.gate {
1496
+ return Ok(CmdResult::from_json(
1497
+ serde_json::json!({"ok": false, "status": "unknown_gate", "gate": raw}),
1498
+ args.json,
1499
+ ));
1500
+ }
1493
1501
  if args.comms || matches!(args.gate, Some(DoctorGate::Comms)) {
1494
1502
  let value = crate::diagnose::comms::doctor_comms_json(&args.workspace, args.team.as_deref(), Some("comms"))?;
1495
1503
  if !args.json {
@@ -115,7 +115,7 @@ pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, Cl
115
115
  let display_backend = compiled
116
116
  .as_ref()
117
117
  .and_then(|spec| yaml_path_str(spec, &["runtime", "display_backend"]))
118
- .unwrap_or("adaptive");
118
+ .unwrap_or("none");
119
119
  let ghostty_required = display_backend == "ghostty_window" || display_backend == "ghostty";
120
120
  let ghostty_path = command_path("ghostty");
121
121
  checks.push(json!({
@@ -289,21 +289,46 @@ fn copy_optional_field(from: &Value, to: &mut Value, key: &str) {
289
289
  }
290
290
 
291
291
  pub(crate) fn build_wait_ready_report(workspace: &std::path::Path, timeout: f64) -> Result<Value, CliError> {
292
- let selected = crate::state::selector::resolve_active_team(
292
+ // swallow batch 3 ③: an unreadable runtime state must never read as "ready" — the
293
+ // read error is surfaced verbatim (state_read_error) with ready=false instead of
294
+ // silently degrading to an empty/stale state.
295
+ let selected = match crate::state::selector::resolve_active_team(
293
296
  workspace,
294
297
  None,
295
298
  crate::state::selector::SelectorMode::RuntimeOnly,
296
- )
297
- .map_err(|e| CliError::Runtime(e.to_string()))?;
299
+ ) {
300
+ Ok(selected) => selected,
301
+ Err(error) => {
302
+ return Ok(json!({
303
+ "ok": false,
304
+ "status": "error",
305
+ "reason": "state_read_error",
306
+ "state_read_error": error.to_string(),
307
+ "readiness": {"ready": false},
308
+ "summary": "runtime state could not be read",
309
+ "next_actions": [json!("inspect .team/runtime/state.json (corrupt or unreadable) and retry")],
310
+ }));
311
+ }
312
+ };
298
313
  let timeout = if timeout.is_finite() && timeout > 0.0 { timeout } else { 0.0 };
299
314
  let deadline = std::time::Instant::now() + std::time::Duration::from_secs_f64(timeout);
300
315
  let mut readiness;
316
+ let mut state_read_error: Option<String> = None;
301
317
  loop {
302
- let mut state = crate::state::projection::select_runtime_state(
318
+ let mut state = match crate::state::projection::select_runtime_state(
303
319
  &selected.run_workspace,
304
320
  Some(&selected.team_key),
305
- )
306
- .unwrap_or_else(|_| selected.state.clone());
321
+ ) {
322
+ Ok(state) => {
323
+ state_read_error = None;
324
+ state
325
+ }
326
+ Err(error) => {
327
+ state_read_error = Some(error.to_string());
328
+ readiness = json!({"ready": false, "state_read_error": error.to_string()});
329
+ break;
330
+ }
331
+ };
307
332
  inject_tmux_session_present(&selected.run_workspace, &mut state);
308
333
  inject_message_counts(&selected.run_workspace, &mut state)?;
309
334
  readiness = wait_readiness(&state);
@@ -322,7 +347,15 @@ pub(crate) fn build_wait_ready_report(workspace: &std::path::Path, timeout: f64)
322
347
  .and_then(Value::as_bool)
323
348
  == Some(true);
324
349
  let ready = readiness.get("ready").and_then(Value::as_bool) == Some(true);
325
- let (ok, status, reason, summary, next_actions) = if awaiting_trust {
350
+ let (ok, status, reason, summary, next_actions) = if state_read_error.is_some() {
351
+ (
352
+ false,
353
+ "error",
354
+ "state_read_error",
355
+ "runtime state could not be read",
356
+ vec![json!("inspect .team/runtime/state.json (corrupt or unreadable) and retry")],
357
+ )
358
+ } else if awaiting_trust {
326
359
  (
327
360
  false,
328
361
  "pending",
@@ -360,7 +393,7 @@ pub(crate) fn build_wait_ready_report(workspace: &std::path::Path, timeout: f64)
360
393
  "readiness": readiness,
361
394
  }),
362
395
  )?;
363
- Ok(json!({
396
+ let mut report = json!({
364
397
  "details_log": details_log.to_string_lossy().to_string(),
365
398
  "next_actions": next_actions,
366
399
  "ok": ok,
@@ -368,7 +401,11 @@ pub(crate) fn build_wait_ready_report(workspace: &std::path::Path, timeout: f64)
368
401
  "readiness": readiness,
369
402
  "status": status,
370
403
  "summary": summary,
371
- }))
404
+ });
405
+ if let Some(error) = state_read_error {
406
+ report["state_read_error"] = json!(error);
407
+ }
408
+ Ok(report)
372
409
  }
373
410
 
374
411
  fn inject_tmux_session_present(workspace: &std::path::Path, state: &mut Value) {
@@ -392,10 +429,12 @@ pub(crate) fn wait_readiness(state: &Value) -> Value {
392
429
  let mut task_prompt_delivered = false;
393
430
  let mut awaiting_trust_prompt = false;
394
431
  let mut incomplete_sessions = Vec::new();
432
+ // A-5: a missing/unreadable leader_receiver must NOT count as attached —
433
+ // "unreadable is never ready" (doctor/wait-ready truthfulness rule).
395
434
  let all_attached_receiver = state
396
435
  .get("leader_receiver")
397
436
  .and_then(Value::as_object)
398
- .is_none_or(|receiver| {
437
+ .is_some_and(|receiver| {
399
438
  receiver
400
439
  .get("status")
401
440
  .and_then(Value::as_str)
@@ -673,6 +712,7 @@ fn provider_wire(provider: crate::provider::Provider) -> &'static str {
673
712
  crate::provider::Provider::Claude => "claude",
674
713
  crate::provider::Provider::ClaudeCode => "claude_code",
675
714
  crate::provider::Provider::Codex => "codex",
715
+ crate::provider::Provider::Copilot => "copilot",
676
716
  crate::provider::Provider::GeminiCli => "gemini_cli",
677
717
  crate::provider::Provider::Fake => "fake",
678
718
  }
@@ -682,6 +722,7 @@ fn provider_command(provider: crate::provider::Provider) -> &'static str {
682
722
  match provider {
683
723
  crate::provider::Provider::Claude | crate::provider::Provider::ClaudeCode => "claude",
684
724
  crate::provider::Provider::Codex => "codex",
725
+ crate::provider::Provider::Copilot => "copilot",
685
726
  crate::provider::Provider::GeminiCli => "gemini",
686
727
  crate::provider::Provider::Fake => "team-agent fake-worker",
687
728
  }
@@ -200,7 +200,7 @@ fn command_help(command: Option<&str>) -> String {
200
200
  )
201
201
  }
202
202
  Some("init") => "usage: team-agent init [--workspace WORKSPACE] [--force] [--json]".to_string(),
203
- Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--fresh] [--json]".to_string(),
203
+ Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--fresh] [--json]\n\ndefaults: display_backend=none; set display_backend: adaptive in TEAM.md to opt in to adaptive display windows.".to_string(),
204
204
  Some("start") => "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]".to_string(),
205
205
  Some("compile") => "usage: team-agent compile --team TEAM [--out FILE] [--json]".to_string(),
206
206
  Some("send") => "usage: team-agent send TARGET MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--targets AGENTS] [--task TASK] [--sender SENDER] [--watch-result] [--requires-ack|--no-ack] [--no-wait] [--timeout SECONDS] [--confirm-human] [--message-id ID] [--json]".to_string(),
@@ -963,7 +963,8 @@ fn doctor_gate(raw: Option<&str>) -> Option<DoctorGate> {
963
963
  match raw {
964
964
  Some("orphans") => Some(DoctorGate::Orphans),
965
965
  Some("comms") => Some(DoctorGate::Comms),
966
- _ => None,
966
+ Some(other) => Some(DoctorGate::Unknown(other.to_string())),
967
+ None => None,
967
968
  }
968
969
  }
969
970