@team-agent/installer 0.5.119 → 0.5.121

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 (73) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/README.md +46 -1
  4. package/README.zh.md +46 -1
  5. package/crates/team-agent/src/cli/adapters.rs +11 -0
  6. package/crates/team-agent/src/cli/arguments.rs +401 -0
  7. package/crates/team-agent/src/cli/emit.rs +241 -142
  8. package/crates/team-agent/src/cli/intent/runtime.rs +1175 -0
  9. package/crates/team-agent/src/cli/intent/runtime_tests.rs +698 -0
  10. package/crates/team-agent/src/cli/intent/tests.rs +654 -0
  11. package/crates/team-agent/src/cli/intent.rs +642 -0
  12. package/crates/team-agent/src/cli/mod.rs +53 -6
  13. package/crates/team-agent/src/cli/named_address.rs +10 -1
  14. package/crates/team-agent/src/cli/spec.rs +22 -10
  15. package/crates/team-agent/src/cli/status_port/brief.rs +117 -16
  16. package/crates/team-agent/src/cli/tests/base.rs +6 -1
  17. package/crates/team-agent/src/cli/tests/parser_safety.rs +383 -0
  18. package/crates/team-agent/src/cli/tests/run_delegation.rs +63 -19
  19. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +56 -0
  20. package/crates/team-agent/src/cli/tests/verb_install_skill.rs +3 -2
  21. package/crates/team-agent/src/cli/tests/verb_profile.rs +3 -1
  22. package/crates/team-agent/src/contract_runtime/backend.rs +1 -1
  23. package/crates/team-agent/src/db/message_store.rs +65 -0
  24. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +8 -0
  25. package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +111 -4
  26. package/crates/team-agent/src/lifecycle/launch/cursor_mcp_iso.rs +244 -82
  27. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +166 -77
  28. package/crates/team-agent/src/lifecycle/launch.rs +29 -0
  29. package/crates/team-agent/src/lifecycle/restart/common.rs +7 -1
  30. package/crates/team-agent/src/lifecycle/restart.rs +17 -0
  31. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -3
  32. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +5 -15
  33. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -3
  34. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +2 -3
  35. package/crates/team-agent/src/lifecycle/tests/cli_argv_routing_red.rs +10 -2
  36. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +8 -1
  37. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +3 -2
  38. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +6 -9
  39. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +7 -5
  40. package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +64 -19
  41. package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +5 -2
  42. package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +34 -10
  43. package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +2 -4
  44. package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +3 -4
  45. package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +5 -4
  46. package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +2 -4
  47. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +78 -36
  48. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +8 -4
  49. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +6 -12
  50. package/crates/team-agent/src/lifecycle/tests/pi_fork_session_inheritance_red.rs +4 -0
  51. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1 -3
  52. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +7 -0
  53. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +47 -4
  54. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -3
  55. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -1
  56. package/crates/team-agent/src/lifecycle/types.rs +10 -0
  57. package/crates/team-agent/src/mcp_server/mod.rs +2 -0
  58. package/crates/team-agent/src/mcp_server/readiness.rs +432 -0
  59. package/crates/team-agent/src/mcp_server/readiness_tests.rs +242 -0
  60. package/crates/team-agent/src/mcp_server/wire.rs +96 -11
  61. package/crates/team-agent/src/messaging/delivery.rs +62 -0
  62. package/crates/team-agent/src/messaging/tests/dup_inject.rs +41 -1
  63. package/crates/team-agent/src/provider/adapter.rs +7 -0
  64. package/crates/team-agent/src/tmux_backend/tests.rs +37 -0
  65. package/crates/team-agent/src/tmux_backend.rs +8 -1
  66. package/package.json +4 -4
  67. package/skills/team-agent/SKILL.md +71 -54
  68. package/skills/team-agent/command-coverage.json +283 -13
  69. package/skills/team-agent/docs/reference/team-agent-operator.md +220 -0
  70. package/skills/team-agent/references/bug-as-artifact-flow.md +1 -1
  71. package/skills/team-agent/references/recovery-runbook.md +61 -212
  72. package/skills/team-agent/references/team-agent-operator.md +181 -48
  73. package/skills/team-agent/references/team-in-team.md +47 -80
@@ -32,7 +32,7 @@ fn enter_hermetic(tag: &str) -> hermetic_guard::HermeticTestEnv {
32
32
  #[test]
33
33
  #[serial_test::serial(env)]
34
34
  fn concurrent_reset_discard_session_serializes() {
35
- let _hermetic = enter_hermetic("phase-b-concurrent-reset");
35
+ let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("phase-b-concurrent-reset");
36
36
  let ids = (1..=6).map(|n| format!("w{n}")).collect::<Vec<_>>();
37
37
  let role_docs = ids
38
38
  .iter()
@@ -139,7 +139,7 @@ fn concurrent_reset_discard_session_serializes() {
139
139
  #[test]
140
140
  #[serial_test::serial(env)]
141
141
  fn add_fork_remove_share_lifecycle_lock_behavior() {
142
- let _hermetic = enter_hermetic("phase-b-fork");
142
+ let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("phase-b-fork");
143
143
  let _home = LaneHomeGuard::enter("phase-b-fork");
144
144
  let (add_team, add_workspace, add_role, add_transport) = add_fixture();
145
145
  let add_agent = aid("w2");
@@ -324,7 +324,7 @@ fn add_fork_remove_share_lifecycle_lock_behavior() {
324
324
  #[test]
325
325
  #[serial_test::serial(env)]
326
326
  fn breal_spawn_ownership_mismatch_fails_without_state_pollution() {
327
- let _hermetic = enter_hermetic("phase-b-spawn-ownership");
327
+ let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("phase-b-spawn-ownership");
328
328
  let (workspace, _team) = breal_workspace();
329
329
  let transport = BRealTransport::misowned();
330
330
 
@@ -366,7 +366,9 @@ fn breal_spawn_ownership_mismatch_fails_without_state_pollution() {
366
366
  }
367
367
 
368
368
  #[test]
369
+ #[serial_test::serial(env)]
369
370
  fn breal_provider_missing_window_disappeared_fails_closed() {
371
+ let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("phase-b-missing-window");
370
372
  let (workspace, _team) = breal_workspace();
371
373
  let transport = BRealTransport::dead_after_spawn();
372
374
 
@@ -404,7 +406,9 @@ fn breal_provider_missing_window_disappeared_fails_closed() {
404
406
  }
405
407
 
406
408
  #[test]
409
+ #[serial_test::serial(env)]
407
410
  fn breal_reset_rehydrates_role_context_from_compiled_spec() {
411
+ let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("phase-b-reset-context");
408
412
  let (workspace, _team) = breal_workspace();
409
413
  strip_runtime_command_context(&workspace);
410
414
  let transport = BRealTransport::owned();
@@ -438,7 +442,7 @@ fn breal_reset_rehydrates_role_context_from_compiled_spec() {
438
442
  #[test]
439
443
  #[serial_test::serial(env)]
440
444
  fn breal_restart_rehydrates_role_context_from_compiled_spec() {
441
- let _hermetic = enter_hermetic("phase-b-restart-role-context");
445
+ let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("phase-b-restart-role-context");
442
446
  let (workspace, _team) = breal_one_worker_workspace();
443
447
  strip_runtime_command_context(&workspace);
444
448
  let transport = BRealTransport::owned();
@@ -94,8 +94,8 @@ fn hermetic_caller_envs() -> &'static [&'static str] {
94
94
 
95
95
  #[test]
96
96
  #[serial_test::serial(env)]
97
- fn phase_golden_unbound_not_ready_requires_claim_leader() {
98
- let diagnose = run_diagnose_after_quick_start();
97
+ fn phase_golden_missing_registry_requires_index_publication_not_claim() {
98
+ let diagnose = run_diagnose_after_unbound_state_fixture();
99
99
  let issues = diagnose
100
100
  .get("issues")
101
101
  .and_then(Value::as_array)
@@ -177,22 +177,16 @@ fn cmd_value(result: Result<CmdResult, crate::cli::CliError>) -> Value {
177
177
  }
178
178
  }
179
179
 
180
- fn run_diagnose_after_quick_start() -> Value {
180
+ fn run_diagnose_after_unbound_state_fixture() -> Value {
181
181
  let hermetic = HermeticTestEnv::enter("tombstone-claim-leader");
182
182
  let _permission_mode = EnvVarGuard::set(ANCESTRY_ENV, "[]");
183
183
  let team = two_worker_team_dir(&hermetic);
184
184
  let workspace = team.parent().expect("workspace").to_path_buf();
185
185
  seed_healthy_coordinator(&workspace);
186
186
  let launch_transport = codex_ready_transport();
187
- quick_start_with_transport_in_workspace(
188
- &workspace,
189
- &team,
190
- None,
191
- true,
192
- None,
193
- &launch_transport,
194
- )
195
- .expect("tombstone fixture must quick-start");
187
+ crate::lifecycle::launch::materialize_unbound_state_for_test(
188
+ &workspace, &team, "teamdir", &launch_transport,
189
+ ).expect("materialize the recording-only tombstone fixture, not a callerless public start");
196
190
  let mut state = crate::state::persist::load_runtime_state(&workspace)
197
191
  .expect("tombstone fixture must persist runtime state");
198
192
  crate::lifecycle::launch::seed_launched_owner_from_caller_with_provider_lookup(
@@ -90,6 +90,7 @@ impl Drop for PathEnvGuard {
90
90
  struct Fixture {
91
91
  #[cfg(unix)]
92
92
  _path_guard: PathEnvGuard,
93
+ _caller: hermetic_guard::HermeticTestEnv,
93
94
  root: PathBuf,
94
95
  team_dir: PathBuf,
95
96
  run_workspace: PathBuf,
@@ -103,6 +104,7 @@ struct Fixture {
103
104
 
104
105
  impl Fixture {
105
106
  fn new() -> Self {
107
+ let caller = hermetic_guard::HermeticTestEnv::enter_with_leader("pi-fork-owned-caller");
106
108
  let root = super::temp_ws();
107
109
  #[cfg(unix)]
108
110
  let path_guard = PathEnvGuard::with_fake_pi(&root);
@@ -122,6 +124,7 @@ impl Fixture {
122
124
  )
123
125
  .expect("write referenced profile");
124
126
 
127
+ super::launch_spawn::seed_healthy_coordinator(&root);
125
128
  let setup = team_agent::lifecycle::launch::quick_start_with_transport(
126
129
  &team_dir,
127
130
  None,
@@ -198,6 +201,7 @@ impl Fixture {
198
201
  let fixture = Self {
199
202
  #[cfg(unix)]
200
203
  _path_guard: path_guard,
204
+ _caller: caller,
201
205
  root,
202
206
  team_dir,
203
207
  run_workspace,
@@ -468,9 +468,7 @@ struct LaunchLatencyCase {
468
468
 
469
469
  impl LaunchLatencyCase {
470
470
  fn new(tag: &str, workers: usize) -> Self {
471
- let env = hermetic_guard::HermeticTestEnv::enter(tag);
472
- env.scrub_tmux();
473
- env.assert_no_real_tmux();
471
+ let env = hermetic_guard::HermeticTestEnv::enter_with_leader(tag);
474
472
  let workspace = env.workspace(tag);
475
473
  let team_dir = workspace.join("teamdir");
476
474
  fs::create_dir_all(&team_dir).expect("create launch team dir");
@@ -355,6 +355,13 @@ fn red6_cli_restart_reports_and_top_level_error_logs_are_redacted() {
355
355
  combined.push_str(&String::from_utf8_lossy(&output.stdout));
356
356
  combined.push_str(&String::from_utf8_lossy(&output.stderr));
357
357
  }
358
+ assert!(read_cli_error_logs(&workspace).is_empty(),
359
+ "invalid profile syntax must not create a runtime diagnostic log");
360
+ // Keep the original physical-log redaction assertion on the same actual
361
+ // runtime-error emitter, separately from the new zero-log Usage boundary.
362
+ assert_eq!(crate::cli::emit::__test_runtime_error_emitter(
363
+ "restart", &workspace, &crate::cli::CliError::Runtime(diagnostic.clone())
364
+ ), crate::cli::ExitCode::Error);
358
365
  let logs = read_cli_error_logs(&workspace);
359
366
  let output_safe = !combined.contains(&marker) && combined.contains(REDACTED);
360
367
  let logs_safe = !logs.contains(&marker) && logs.contains(REDACTED);
@@ -720,8 +720,12 @@ fn contract_b_step2_retry_delivers_queued_message_after_startup_trust_is_handled
720
720
  let ws = tmp_dir("b-step2-trust-retry");
721
721
  let mcp_config = ws.join(".team").join("runtime").join("mcp").join("w1.json");
722
722
  std::fs::create_dir_all(mcp_config.parent().unwrap()).unwrap();
723
- std::fs::write(&mcp_config, "{}").unwrap();
724
- let state = json!({
723
+ // This worker genuinely depends on MCP. Bind the controlled receipt to the
724
+ // live test carrier rather than bypassing readiness with an empty config.
725
+ let owner = "subprep-trust-retry";
726
+ let mut state = json!({
727
+ "team_key": owner,
728
+ "active_team_key": owner,
725
729
  "session_name": "team-ta-subroot-SUB-PROBE-2-send-turn-idle",
726
730
  "team_dir": ws.join("teamdir").to_string_lossy().to_string(),
727
731
  "agents": {
@@ -736,6 +740,21 @@ fn contract_b_step2_retry_delivers_queued_message_after_startup_trust_is_handled
736
740
  }
737
741
  }
738
742
  });
743
+ #[cfg(unix)]
744
+ let carrier = {
745
+ let carrier = crate::mcp_server::readiness::test_carrier_pid().unwrap();
746
+ state["agents"]["w1"]["pane_pid"] = json!(carrier);
747
+ state["agents"]["w1"]["spawned_at"] =
748
+ json!((chrono::Utc::now() - chrono::Duration::seconds(10)).to_rfc3339());
749
+ carrier
750
+ };
751
+ state["teams"] = json!({owner: state.clone()});
752
+ std::fs::write(&mcp_config, serde_json::to_vec(&json!({
753
+ "mcpServers": {"team_orchestrator": {
754
+ "command": std::env::current_exe().unwrap().canonicalize().unwrap(),
755
+ "env": {"TEAM_AGENT_WORKSPACE": ws.to_path_buf(), "TEAM_AGENT_ID": "w1", "TEAM_AGENT_OWNER_TEAM_ID": owner}
756
+ }}
757
+ })).unwrap()).unwrap();
739
758
  team_agent::state::persist::save_runtime_state(&ws, &state).unwrap();
740
759
 
741
760
  let store = MessageStore::open(&ws).unwrap();
@@ -748,7 +767,7 @@ fn contract_b_step2_retry_delivers_queued_message_after_startup_trust_is_handled
748
767
  "SUB step2 probe: reply with one sentence and call report_result exactly once.",
749
768
  None,
750
769
  true,
751
- None,
770
+ Some(owner),
752
771
  )
753
772
  .unwrap();
754
773
  let transport = RecordingTransport::new(vec![
@@ -759,6 +778,10 @@ fn contract_b_step2_retry_delivers_queued_message_after_startup_trust_is_handled
759
778
  ])
760
779
  .with_session_present(true)
761
780
  .with_windows(vec![WindowName::new("w1")]);
781
+ #[cfg(unix)]
782
+ {
783
+ transport.state.lock().unwrap().carrier = Some(carrier);
784
+ }
762
785
  let event_log = EventLog::new(&ws);
763
786
 
764
787
  let deferred =
@@ -787,6 +810,13 @@ fn contract_b_step2_retry_delivers_queued_message_after_startup_trust_is_handled
787
810
  "fixture sanity: delivery defer must be observable as delivery.deferred_startup_prompt"
788
811
  );
789
812
 
813
+ // No receipt existed during the actionable trust prompt. Once startup
814
+ // control can progress, model the successful live helper handshake before
815
+ // the coordinator retries this SAME queued row.
816
+ #[cfg(unix)]
817
+ {
818
+ crate::mcp_server::readiness::publish(&ws, owner, "w1", chrono::Utc::now()).unwrap();
819
+ }
790
820
  let coord = Coordinator::new(
791
821
  WorkspacePath::new(ws.to_path_buf()),
792
822
  Box::new(RealAdapterRegistry),
@@ -1045,6 +1075,7 @@ struct TransportState {
1045
1075
  session_present: bool,
1046
1076
  windows: Vec<WindowName>,
1047
1077
  spawn_argv: Vec<Vec<String>>,
1078
+ carrier: Option<u32>,
1048
1079
  }
1049
1080
 
1050
1081
  #[derive(Debug, Clone)]
@@ -1189,7 +1220,19 @@ impl Transport for RecordingTransport {
1189
1220
  }
1190
1221
 
1191
1222
  fn list_targets(&self) -> Result<Vec<PaneInfo>, TransportError> {
1192
- Ok(Vec::new())
1223
+ Ok(self.state.lock().unwrap().carrier.map(|carrier| PaneInfo {
1224
+ pane_id: PaneId::new("%7"),
1225
+ session: SessionName::new("team-ta-subroot-SUB-PROBE-2-send-turn-idle"),
1226
+ window_index: None,
1227
+ window_name: Some(WindowName::new("w1")),
1228
+ pane_index: None,
1229
+ tty: None,
1230
+ current_command: None,
1231
+ current_path: None,
1232
+ active: true,
1233
+ pane_pid: Some(carrier),
1234
+ leader_env: BTreeMap::new(),
1235
+ }).into_iter().collect())
1193
1236
  }
1194
1237
 
1195
1238
  fn has_session(&self, _session: &SessionName) -> Result<bool, TransportError> {
@@ -35,10 +35,8 @@ use team_agent::transport::{
35
35
  #[test]
36
36
  #[serial(env)]
37
37
  fn quick_start_team_id_persists_requested_team_key_for_lifecycle_selection() {
38
- let hermetic = hermetic_guard::HermeticTestEnv::enter("team-in-team-requested-key");
39
- hermetic.scrub_tmux();
40
- hermetic.assert_no_real_tmux();
41
38
  let _env = EnvGuard::unset();
39
+ let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("team-in-team-requested-key");
42
40
  let root = tmp_dir("team-id-key");
43
41
  seed_healthy_coordinator(&root);
44
42
  let team_dir = write_team_dir(&root, "teamdir", "w1");
@@ -46,8 +46,9 @@ const CLAUDE_ROLE: &str = "---\nname: clauder\nrole: Claude Worker\nprovider: cl
46
46
  #[test]
47
47
  #[serial(env)]
48
48
  fn worker_mcp_owner_team_scope_must_match_runtime_team_key_not_spec_name() {
49
- let hermetic = HermeticTestEnv::enter("rs031-mcp-owner-scope");
49
+ let hermetic = HermeticTestEnv::enter_with_leader("rs031-mcp-owner-scope");
50
50
  let team = mixed_provider_team_dir(&hermetic, "mcp-owner-scope");
51
+ super::launch_spawn::seed_healthy_coordinator(team.parent().unwrap());
51
52
  let transport = RecordingTransport::new()
52
53
  .with_windows(vec![WindowName::new("clauder"), WindowName::new("codexer")]);
53
54
  let launch = ready_launch(
@@ -512,6 +512,16 @@ pub enum QuickStartReport {
512
512
  worker_readiness: QuickStartReadiness,
513
513
  team: String,
514
514
  },
515
+ /// Workers may exist, but post-spawn admission/coordinator completion failed.
516
+ /// Preserve confirmed effects; never disguise this as a preflight refusal.
517
+ Partial {
518
+ workspace: PathBuf,
519
+ session_name: SessionName,
520
+ launch: Box<LaunchReport>,
521
+ team: String,
522
+ error: String,
523
+ coordinator_state: String,
524
+ },
515
525
  /// 已有 runtime state → quick-start 拒绝并引导用 restart。
516
526
  ExistingRuntime {
517
527
  team: Option<String>,
@@ -89,6 +89,8 @@ use crate::messaging::{self, DeliveryOutcome, MessageTarget, SendOptions, Truste
89
89
  pub mod helpers;
90
90
  pub(crate) mod lifecycle_tools;
91
91
  pub mod normalize;
92
+ #[cfg(unix)]
93
+ pub(crate) mod readiness;
92
94
  pub mod tools;
93
95
  pub mod types;
94
96
  pub mod wire;