@team-agent/installer 0.5.77 → 0.5.80

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 (95) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +161 -29
  4. package/crates/team-agent/src/cli/diagnose.rs +333 -68
  5. package/crates/team-agent/src/cli/emit.rs +24 -8
  6. package/crates/team-agent/src/cli/mod.rs +543 -98
  7. package/crates/team-agent/src/cli/models.rs +393 -32
  8. package/crates/team-agent/src/cli/named_address.rs +19 -5
  9. package/crates/team-agent/src/cli/profile.rs +35 -4
  10. package/crates/team-agent/src/cli/spec.rs +7 -2
  11. package/crates/team-agent/src/cli/status_port/compact.rs +12 -8
  12. package/crates/team-agent/src/cli/status_port/runtime.rs +3 -1
  13. package/crates/team-agent/src/cli/status_port/snapshot.rs +15 -12
  14. package/crates/team-agent/src/cli/status_port/store.rs +1 -1
  15. package/crates/team-agent/src/cli/status_port/tests.rs +37 -1
  16. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +62 -8
  17. package/crates/team-agent/src/cli/tests/named_address.rs +70 -0
  18. package/crates/team-agent/src/cli/tests/verb_profile.rs +81 -6
  19. package/crates/team-agent/src/cli/types.rs +1 -0
  20. package/crates/team-agent/src/coordinator/steps/abnormal.rs +122 -2
  21. package/crates/team-agent/src/leader/lease.rs +492 -33
  22. package/crates/team-agent/src/leader/owner_bind.rs +40 -12
  23. package/crates/team-agent/src/leader/rediscover/tests.rs +65 -0
  24. package/crates/team-agent/src/leader/rediscover.rs +36 -6
  25. package/crates/team-agent/src/leader/start.rs +197 -88
  26. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +15 -9
  27. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +28 -227
  28. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +0 -18
  29. package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +94 -227
  30. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +59 -10
  31. package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +86 -23
  32. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +1449 -282
  33. package/crates/team-agent/src/lifecycle/launch/readiness.rs +349 -4
  34. package/crates/team-agent/src/lifecycle/launch/spawn.rs +11 -11
  35. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +35 -3
  36. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +16 -0
  37. package/crates/team-agent/src/lifecycle/launch.rs +30 -7
  38. package/crates/team-agent/src/lifecycle/profile_launch.rs +37 -3
  39. package/crates/team-agent/src/lifecycle/restart/agent.rs +112 -100
  40. package/crates/team-agent/src/lifecycle/restart/common.rs +82 -52
  41. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +996 -66
  42. package/crates/team-agent/src/lifecycle/restart.rs +2 -2
  43. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +3 -0
  44. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +15 -0
  45. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +5 -0
  46. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +11 -13
  47. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +119 -4
  48. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +12 -0
  49. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +22 -0
  50. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +12 -0
  51. package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +265 -198
  52. package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +14 -30
  53. package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +3 -0
  54. package/crates/team-agent/src/lifecycle/tests/fixtures/pi_list_models_spaced_ids.stdout.txt +9 -0
  55. package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +2 -5
  56. package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +10 -0
  57. package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +58 -35
  58. package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +10 -0
  59. package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +9 -0
  60. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -2
  61. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +0 -168
  62. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +145 -212
  63. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +16 -2
  64. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +54 -9
  65. package/crates/team-agent/src/lifecycle/tests/pi_catalog_red.rs +71 -0
  66. package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +78 -0
  67. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +3 -0
  68. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +51 -10
  69. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +50 -7
  70. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +137 -0
  71. package/crates/team-agent/src/lifecycle/types.rs +8 -0
  72. package/crates/team-agent/src/mcp_server/wire.rs +140 -11
  73. package/crates/team-agent/src/messaging/delivery.rs +221 -80
  74. package/crates/team-agent/src/messaging/leader_channel.rs +56 -0
  75. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +127 -0
  76. package/crates/team-agent/src/messaging/tests/runtime.rs +10 -2
  77. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +2 -5
  78. package/crates/team-agent/src/provider/session_scan/common/tests.rs +204 -0
  79. package/crates/team-agent/src/provider/session_scan/common.rs +24 -2
  80. package/crates/team-agent/src/state/abnormal_watch/tests/mod.rs +248 -0
  81. package/crates/team-agent/src/state/abnormal_watch.rs +144 -0
  82. package/crates/team-agent/src/state/mod.rs +1 -0
  83. package/crates/team-agent/src/state/persist.rs +102 -4
  84. package/crates/team-agent/src/state/projection.rs +7 -1
  85. package/crates/team-agent/src/state/repository.rs +29 -0
  86. package/crates/team-agent/src/test_support/models_timeout_fixture.rs +43 -21
  87. package/crates/team-agent/src/tmux_backend/tests.rs +149 -4
  88. package/crates/team-agent/src/tmux_backend.rs +239 -127
  89. package/crates/team-agent/src/transport.rs +78 -0
  90. package/crates/team-agent/src/transport_factory.rs +53 -0
  91. package/package.json +4 -5
  92. package/skills/team-agent/SKILL.md +45 -17
  93. package/skills/team-agent/command-coverage.json +4 -29
  94. package/npm/check-skill-links.mjs +0 -88
  95. package/skills/team-agent/references/team-agent-operator.md +0 -672
package/Cargo.lock CHANGED
@@ -581,7 +581,7 @@ dependencies = [
581
581
 
582
582
  [[package]]
583
583
  name = "team-agent"
584
- version = "0.5.77"
584
+ version = "0.5.80"
585
585
  dependencies = [
586
586
  "anyhow",
587
587
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-trans
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.5.77"
12
+ version = "0.5.80"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -183,10 +183,18 @@ fn quickstart_human(value: &Value) -> String {
183
183
  append_reminder(out, crate::cli::QUICK_START_REMINDER)
184
184
  }
185
185
 
186
- fn append_send_guidance(value: &mut Value, workspace: &Path, team: Option<&str>) {
187
- if value.get("ok").and_then(Value::as_bool) != Some(true) {
186
+ pub(crate) fn append_send_guidance(value: &mut Value, workspace: &Path, team: Option<&str>) {
187
+ let existing = value.get("summary").and_then(Value::as_str) == Some("existing runtime");
188
+ if value.get("ok").and_then(Value::as_bool) != Some(true) && !existing {
188
189
  return;
189
190
  }
191
+ let team = value
192
+ .get("team")
193
+ .and_then(Value::as_str)
194
+ .filter(|team| !team.is_empty())
195
+ .map(str::to_string)
196
+ .or_else(|| team.map(str::to_string));
197
+ let team = team.as_deref();
190
198
  let agents = value
191
199
  .get("agent_ids")
192
200
  .and_then(Value::as_array)
@@ -204,11 +212,36 @@ fn append_send_guidance(value: &mut Value, workspace: &Path, team: Option<&str>)
204
212
  }
205
213
  }
206
214
 
215
+ #[cfg(test)]
216
+ pub(crate) fn split_shell_argv(command: &str) -> Vec<String> {
217
+ let mut argv = Vec::new();
218
+ let mut current = String::new();
219
+ let mut chars = command.chars().peekable();
220
+ let mut quote: Option<char> = None;
221
+ while let Some(ch) = chars.next() {
222
+ match (quote, ch) {
223
+ (None, '\'') | (None, '"') => quote = Some(ch),
224
+ (Some(q), c) if c == q => quote = None,
225
+ (None, c) if c.is_whitespace() => {
226
+ if !current.is_empty() {
227
+ argv.push(std::mem::take(&mut current));
228
+ }
229
+ }
230
+ (_, c) => current.push(c),
231
+ }
232
+ }
233
+ if !current.is_empty() {
234
+ argv.push(current);
235
+ }
236
+ argv
237
+ }
238
+
207
239
  pub(crate) fn send_command(agent: &str, workspace: &Path, team: Option<&str>) -> Option<String> {
208
240
  let workspace = workspace.to_str()?;
209
241
  let mut command = format!(
210
- "team-agent send {} \\\"MESSAGE\\\" --workspace {}",
242
+ "team-agent send {} {} --workspace {}",
211
243
  shell_quote(agent),
244
+ shell_quote("MESSAGE"),
212
245
  shell_quote(workspace)
213
246
  );
214
247
  if let Some(team) = team {
@@ -528,6 +561,42 @@ pub fn cmd_allow_peer_talk(args: &AllowPeerTalkArgs) -> Result<CmdResult, CliErr
528
561
 
529
562
  /// `cmd_diagnose`(`parser.py:298`)。
530
563
  pub fn cmd_diagnose(args: &DiagnoseArgs) -> Result<CmdResult, CliError> {
564
+ let team_hint = args
565
+ .team
566
+ .as_deref()
567
+ .filter(|team| !team.is_empty())
568
+ .unwrap_or("current");
569
+ // Check the caller-supplied workspace before RuntimeOnly synthesis or
570
+ // transport setup, which can create `.team` paths and make an empty
571
+ // directory look like an unbound team.
572
+ if !crate::cli::diagnose::workspace_has_existing_team_runtime(&args.workspace, team_hint) {
573
+ let (issues, suggested_repairs) =
574
+ crate::cli::diagnose::missing_team_runtime_issues_and_repairs(team_hint);
575
+ let event_log = args
576
+ .workspace
577
+ .join(".team")
578
+ .join("logs")
579
+ .join("events.jsonl");
580
+ return Ok(CmdResult::from_json(
581
+ json!({
582
+ "event_log": event_log.to_string_lossy().to_string(),
583
+ "issues": issues,
584
+ "ok": false,
585
+ "providers": provider_doctor_checks(),
586
+ "runtime": {
587
+ "workspace": args.workspace.to_string_lossy().to_string(),
588
+ "team_key": team_hint,
589
+ "session_name": Value::Null,
590
+ "leader_receiver": Value::Null,
591
+ "agent_count": 0,
592
+ "message_count": 0,
593
+ "result_count": 0,
594
+ },
595
+ "suggested_repairs": suggested_repairs,
596
+ }),
597
+ args.json,
598
+ ));
599
+ }
531
600
  let selected = crate::state::selector::resolve_active_team(
532
601
  &args.workspace,
533
602
  args.team.as_deref(),
@@ -556,7 +625,12 @@ pub fn cmd_diagnose(args: &DiagnoseArgs) -> Result<CmdResult, CliError> {
556
625
  )),
557
626
  };
558
627
  let (issues, suggested_repairs) =
559
- diagnose_runtime_for_workspace(&selected.run_workspace, &state, backend.as_ref());
628
+ diagnose_runtime_for_workspace(
629
+ &selected.run_workspace,
630
+ &state,
631
+ backend.as_ref(),
632
+ Some(selected.team_key.as_str()),
633
+ );
560
634
  let ok = issues.as_array().is_some_and(Vec::is_empty);
561
635
  Ok(CmdResult::from_json(
562
636
  json!({
@@ -1347,17 +1421,24 @@ pub fn cmd_restart(args: &RestartArgs) -> Result<CmdResult, CliError> {
1347
1421
 
1348
1422
  /// `cmd_start_agent`(`commands.py:348`)。
1349
1423
  pub fn cmd_start_agent(args: &StartAgentArgs) -> Result<CmdResult, CliError> {
1350
- Ok(CmdResult::from_json(
1351
- lifecycle_port::start_agent(
1352
- &args.workspace,
1353
- &args.agent,
1354
- args.force,
1355
- !args.no_display,
1356
- args.allow_fresh,
1357
- args.team.as_deref(),
1358
- )?,
1359
- args.json,
1360
- ))
1424
+ let mut value = lifecycle_port::start_agent(
1425
+ &args.workspace,
1426
+ &args.agent,
1427
+ args.force,
1428
+ !args.no_display,
1429
+ args.allow_fresh,
1430
+ args.team.as_deref(),
1431
+ )?;
1432
+ if value.get("agent_ids").is_none() {
1433
+ if let Some(object) = value.as_object_mut() {
1434
+ object.insert(
1435
+ "agent_ids".to_string(),
1436
+ json!([args.agent.as_str()]),
1437
+ );
1438
+ }
1439
+ }
1440
+ append_send_guidance(&mut value, &args.workspace, args.team.as_deref());
1441
+ Ok(CmdResult::from_json(value, args.json))
1361
1442
  }
1362
1443
 
1363
1444
  /// `cmd_stop_agent`(`commands.py:359`)。
@@ -1384,17 +1465,24 @@ pub fn cmd_reset_agent(args: &ResetAgentArgs) -> Result<CmdResult, CliError> {
1384
1465
 
1385
1466
  /// `cmd_add_agent`(`commands.py:373`)。
1386
1467
  pub fn cmd_add_agent(args: &AddAgentArgs) -> Result<CmdResult, CliError> {
1387
- Ok(CmdResult::from_json(
1388
- lifecycle_port::add_agent(
1389
- &args.workspace,
1390
- &args.agent,
1391
- &args.role_file,
1392
- !args.no_display,
1393
- args.team.as_deref(),
1394
- args.force,
1395
- )?,
1396
- args.json,
1397
- ))
1468
+ let mut value = lifecycle_port::add_agent(
1469
+ &args.workspace,
1470
+ &args.agent,
1471
+ &args.role_file,
1472
+ !args.no_display,
1473
+ args.team.as_deref(),
1474
+ args.force,
1475
+ )?;
1476
+ if value.get("agent_ids").is_none() {
1477
+ if let Some(object) = value.as_object_mut() {
1478
+ object.insert(
1479
+ "agent_ids".to_string(),
1480
+ json!([args.agent.as_str()]),
1481
+ );
1482
+ }
1483
+ }
1484
+ append_send_guidance(&mut value, &args.workspace, args.team.as_deref());
1485
+ Ok(CmdResult::from_json(value, args.json))
1398
1486
  }
1399
1487
 
1400
1488
  /// `cmd_fork_agent`(`commands.py:383`)。
@@ -1540,8 +1628,10 @@ pub fn cmd_doctor(args: &DoctorArgs) -> Result<CmdResult, CliError> {
1540
1628
  mod tests {
1541
1629
  #![allow(clippy::unwrap_used)]
1542
1630
 
1543
- use super::{agent_pane_id, append_send_guidance, quickstart_human, send_command};
1544
- use serde_json::json;
1631
+ use super::{
1632
+ agent_pane_id, append_send_guidance, quickstart_human, send_command, split_shell_argv,
1633
+ };
1634
+ use serde_json::{json, Value};
1545
1635
  use std::path::{Path, PathBuf};
1546
1636
 
1547
1637
  // E13:happy 人类输出必须带 attach 块(此前 else 分支只打 summary 丢 attach_commands)。
@@ -1585,13 +1675,37 @@ mod tests {
1585
1675
  );
1586
1676
  }
1587
1677
 
1678
+ #[test]
1679
+ fn send_command_message_is_one_argv_token() {
1680
+ let command = send_command(
1681
+ "worker name; echo unsafe",
1682
+ Path::new("/tmp/my workspace"),
1683
+ Some("team-a"),
1684
+ )
1685
+ .unwrap();
1686
+ let argv = split_shell_argv(&command);
1687
+ assert_eq!(
1688
+ argv,
1689
+ [
1690
+ "team-agent",
1691
+ "send",
1692
+ "worker name; echo unsafe",
1693
+ "MESSAGE",
1694
+ "--workspace",
1695
+ "/tmp/my workspace",
1696
+ "--team",
1697
+ "team-a"
1698
+ ]
1699
+ );
1700
+ }
1701
+
1588
1702
  #[test]
1589
1703
  fn send_guidance_is_copyable_and_preserves_explicit_scope() {
1590
1704
  let command = send_command("worker name; echo unsafe", Path::new("/tmp/my workspace"), Some("team-a"))
1591
1705
  .unwrap();
1592
1706
  assert_eq!(
1593
1707
  command,
1594
- "team-agent send 'worker name; echo unsafe' \\\"MESSAGE\\\" --workspace '/tmp/my workspace' --team team-a"
1708
+ "team-agent send 'worker name; echo unsafe' MESSAGE --workspace '/tmp/my workspace' --team team-a"
1595
1709
  );
1596
1710
  }
1597
1711
 
@@ -1625,6 +1739,24 @@ mod tests {
1625
1739
  assert!(value.get("send_commands").is_none());
1626
1740
  }
1627
1741
 
1742
+ #[test]
1743
+ fn existing_runtime_gets_canonical_send_guidance() {
1744
+ let mut value = json!({
1745
+ "ok": false,
1746
+ "summary": "existing runtime",
1747
+ "agent_ids": ["worker"]
1748
+ });
1749
+ append_send_guidance(&mut value, Path::new("/tmp/ws"), Some("team-a"));
1750
+ let command = value
1751
+ .pointer("/send_commands/0")
1752
+ .and_then(Value::as_str)
1753
+ .unwrap();
1754
+ assert!(command.contains("send worker"));
1755
+ assert!(command.contains("--team team-a"));
1756
+ let parts: Vec<&str> = command.split_whitespace().collect();
1757
+ assert_eq!(parts[3], "MESSAGE");
1758
+ }
1759
+
1628
1760
  #[test]
1629
1761
  fn agent_pane_id_resolves_session_window_even_with_recorded_pane_id() {
1630
1762
  let state = json!({