@team-agent/installer 0.4.10 → 0.5.0

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 (74) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +1 -0
  4. package/crates/team-agent/src/cli/diagnose.rs +2 -15
  5. package/crates/team-agent/src/cli/emit.rs +62 -4
  6. package/crates/team-agent/src/compiler.rs +11 -20
  7. package/crates/team-agent/src/coordinator/orphan.rs +2 -2
  8. package/crates/team-agent/src/coordinator/runtime_detectors.rs +5 -4
  9. package/crates/team-agent/src/coordinator/steps/abnormal.rs +1776 -2
  10. package/crates/team-agent/src/coordinator/steps/mod.rs +19 -0
  11. package/crates/team-agent/src/coordinator/tests/a0_lostupdate.rs +78 -7
  12. package/crates/team-agent/src/coordinator/tests/abnormal.rs +195 -0
  13. package/crates/team-agent/src/coordinator/tick.rs +31 -932
  14. package/crates/team-agent/src/diagnose/orphans.rs +66 -8
  15. package/crates/team-agent/src/layout/worker_window_helpers.rs +5 -7
  16. package/crates/team-agent/src/leader/provider_attribution.rs +7 -5
  17. package/crates/team-agent/src/leader/rediscover.rs +1 -11
  18. package/crates/team-agent/src/leader/start.rs +169 -25
  19. package/crates/team-agent/src/lifecycle/launch/plan.rs +19 -8
  20. package/crates/team-agent/src/lifecycle/launch.rs +135 -58
  21. package/crates/team-agent/src/lifecycle/lock.rs +302 -0
  22. package/crates/team-agent/src/lifecycle/mod.rs +1 -0
  23. package/crates/team-agent/src/lifecycle/profile_smoke.rs +1 -11
  24. package/crates/team-agent/src/lifecycle/restart/agent.rs +436 -121
  25. package/crates/team-agent/src/lifecycle/restart/common.rs +108 -17
  26. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -32
  27. package/crates/team-agent/src/lifecycle/restart/remove.rs +16 -2
  28. package/crates/team-agent/src/lifecycle/restart/selection.rs +2 -1
  29. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +193 -0
  30. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +3 -3
  31. package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +321 -0
  32. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +54 -15
  33. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +802 -0
  34. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +648 -0
  35. package/crates/team-agent/src/lifecycle/tests.rs +3 -0
  36. package/crates/team-agent/src/lifecycle/types.rs +8 -0
  37. package/crates/team-agent/src/lifecycle/worker_command_context.rs +24 -34
  38. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +24 -9
  39. package/crates/team-agent/src/mcp_server/tools.rs +157 -68
  40. package/crates/team-agent/src/messaging/activity.rs +43 -18
  41. package/crates/team-agent/src/messaging/delivery.rs +161 -97
  42. package/crates/team-agent/src/messaging/helpers.rs +1 -0
  43. package/crates/team-agent/src/messaging/leader_receiver.rs +22 -1
  44. package/crates/team-agent/src/messaging/results.rs +34 -9
  45. package/crates/team-agent/src/messaging/scheduler.rs +52 -9
  46. package/crates/team-agent/src/model/spec.rs +10 -6
  47. package/crates/team-agent/src/provider/adapter.rs +10 -1038
  48. package/crates/team-agent/src/provider/adapters/claude.rs +3 -8
  49. package/crates/team-agent/src/provider/adapters/copilot.rs +2 -5
  50. package/crates/team-agent/src/provider/classify.rs +23 -48
  51. package/crates/team-agent/src/provider/command.rs +16 -7
  52. package/crates/team-agent/src/provider/faults.rs +93 -53
  53. package/crates/team-agent/src/provider/session/capture.rs +4 -15
  54. package/crates/team-agent/src/provider/session_scan/claude.rs +309 -0
  55. package/crates/team-agent/src/provider/session_scan/codex.rs +40 -0
  56. package/crates/team-agent/src/provider/session_scan/common.rs +350 -0
  57. package/crates/team-agent/src/provider/session_scan/copilot.rs +202 -0
  58. package/crates/team-agent/src/provider/session_scan.rs +65 -27
  59. package/crates/team-agent/src/provider/tests/faults.rs +80 -0
  60. package/crates/team-agent/src/provider/types.rs +33 -1
  61. package/crates/team-agent/src/provider/wire.rs +171 -1
  62. package/crates/team-agent/src/state/identity.rs +242 -57
  63. package/crates/team-agent/src/state/identity_keys.rs +9 -12
  64. package/crates/team-agent/src/state/mod.rs +5 -1
  65. package/crates/team-agent/src/state/owner_gate.rs +59 -15
  66. package/crates/team-agent/src/state/ownership.rs +12 -11
  67. package/crates/team-agent/src/state/paths.rs +13 -6
  68. package/crates/team-agent/src/state/persist.rs +671 -128
  69. package/crates/team-agent/src/state/projection.rs +240 -49
  70. package/crates/team-agent/src/state/selector.rs +11 -5
  71. package/crates/team-agent/src/tmux_backend/tests.rs +51 -2
  72. package/crates/team-agent/src/tmux_backend.rs +42 -5
  73. package/crates/team-agent/src/transport/test_support.rs +55 -6
  74. 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.4.10"
569
+ version = "0.5.0"
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.4.10"
12
+ version = "0.5.0"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -993,6 +993,7 @@ fn command_version(command: &str) -> Value {
993
993
  }
994
994
 
995
995
  fn provider_command(provider: &str) -> &str {
996
+ // E2E provider command grammar, not persisted provider identity parsing.
996
997
  match provider {
997
998
  "claude" | "claude_code" => "claude",
998
999
  "gemini" | "gemini_cli" => "gemini",
@@ -1,5 +1,6 @@
1
1
  //! diagnose/preflight/wait-ready CLI helpers.
2
2
  use super::*;
3
+ use crate::provider::wire::{command_name, provider_wire};
3
4
  use crate::transport::Transport;
4
5
 
5
6
  pub(crate) fn diagnose_runtime(state: &Value, backend: &dyn Transport) -> (Value, Value) {
@@ -781,23 +782,9 @@ pub(crate) fn provider_doctor_checks() -> Value {
781
782
  Value::Object(providers)
782
783
  }
783
784
 
784
- fn provider_wire(provider: crate::provider::Provider) -> &'static str {
785
- match provider {
786
- crate::provider::Provider::Claude => "claude",
787
- crate::provider::Provider::ClaudeCode => "claude_code",
788
- crate::provider::Provider::Codex => "codex",
789
- crate::provider::Provider::Copilot => "copilot",
790
- crate::provider::Provider::GeminiCli => "gemini_cli",
791
- crate::provider::Provider::Fake => "fake",
792
- }
793
- }
794
-
795
785
  fn provider_command(provider: crate::provider::Provider) -> &'static str {
796
786
  match provider {
797
- crate::provider::Provider::Claude | crate::provider::Provider::ClaudeCode => "claude",
798
- crate::provider::Provider::Codex => "codex",
799
- crate::provider::Provider::Copilot => "copilot",
800
- crate::provider::Provider::GeminiCli => "gemini",
801
787
  crate::provider::Provider::Fake => "team-agent fake-worker",
788
+ other => command_name(other),
802
789
  }
803
790
  }
@@ -31,7 +31,7 @@ pub fn run(argv: &[String], cwd: &Path) -> ExitCode {
31
31
  let Some(command) = argv.first().map(String::as_str) else {
32
32
  return emit_missing_subcommand_usage();
33
33
  };
34
- if command == "codex" || command == "claude" || command == "copilot" {
34
+ if is_leader_passthrough_command(command) {
35
35
  return match cmd_leader_passthrough(command, &argv[1..], cwd) {
36
36
  Ok(result) => emit_result(result),
37
37
  Err(error) => emit_cli_error(command, &argv[1..], cwd, &error),
@@ -189,6 +189,13 @@ const DISPATCH_COMMANDS: &[&str] = &[
189
189
  ];
190
190
 
191
191
  const SPEC_ONLY_HELP_COMMANDS: &[&str] = &["start", "purge-agent"];
192
+ // Command grammar, not provider identity parsing: these are top-level CLI
193
+ // passthrough verbs for starting a leader under a provider executable.
194
+ const LEADER_PASSTHROUGH_COMMANDS: &[&str] = &["codex", "claude", "copilot"];
195
+
196
+ fn is_leader_passthrough_command(command: &str) -> bool {
197
+ LEADER_PASSTHROUGH_COMMANDS.contains(&command)
198
+ }
192
199
 
193
200
  fn emit_missing_subcommand_usage() -> ExitCode {
194
201
  emit_usage_error("the following arguments are required: {codex,claude,...,doctor}");
@@ -206,7 +213,7 @@ fn is_known_subcommand(command: &str) -> bool {
206
213
  fn command_help(command: Option<&str>) -> String {
207
214
  match command {
208
215
  None => {
209
- let mut commands = vec!["codex", "claude", "copilot"];
216
+ let mut commands = LEADER_PASSTHROUGH_COMMANDS.to_vec();
210
217
  commands.extend_from_slice(DISPATCH_COMMANDS);
211
218
  commands.extend_from_slice(SPEC_ONLY_HELP_COMMANDS);
212
219
  format!(
@@ -276,7 +283,7 @@ fn emit_unknown_subcommand_usage(command: &str) -> ExitCode {
276
283
 
277
284
  /// 在已知子命令里找与 `input` 最接近的一个(Levenshtein ≤ 阈值)。无足够接近者 → None。
278
285
  fn nearest_subcommand(input: &str) -> Option<&'static str> {
279
- let mut candidates: Vec<&'static str> = vec!["codex", "claude", "copilot"];
286
+ let mut candidates: Vec<&'static str> = LEADER_PASSTHROUGH_COMMANDS.to_vec();
280
287
  candidates.extend_from_slice(DISPATCH_COMMANDS);
281
288
  candidates.extend_from_slice(SPEC_ONLY_HELP_COMMANDS);
282
289
  // 阈值随长度放宽,但短词收紧,避免 'x' 误配任何东西。
@@ -868,7 +875,7 @@ fn resolve_cli_path(cwd: &Path, path: &Path) -> PathBuf {
868
875
 
869
876
  fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
870
877
  let parsed = parse_args(args);
871
- let target = if parsed.targets.is_some() {
878
+ let target = if parsed.targets.is_some() || parsed.pane.is_some() {
872
879
  None
873
880
  } else {
874
881
  parsed.positionals.first().cloned()
@@ -1747,6 +1754,57 @@ mod tests {
1747
1754
  );
1748
1755
  }
1749
1756
 
1757
+ #[test]
1758
+ fn send_pane_positionals_are_message_not_target() {
1759
+ let cwd = tmp_workspace();
1760
+ let args = send_args(&cli_argv(&["--pane", "%1596", "hello"]), &cwd).unwrap();
1761
+ assert_eq!(args.pane.as_deref(), Some("%1596"));
1762
+ assert_eq!(args.target, None);
1763
+ assert_eq!(args.targets, None);
1764
+ assert_eq!(args.message, vec!["hello".to_string()]);
1765
+
1766
+ let args = send_args(
1767
+ &cli_argv(&["--pane", "%1596", "multi", "word", "message"]),
1768
+ &cwd,
1769
+ )
1770
+ .unwrap();
1771
+ assert_eq!(args.target, None);
1772
+ assert_eq!(
1773
+ args.message,
1774
+ vec![
1775
+ "multi".to_string(),
1776
+ "word".to_string(),
1777
+ "message".to_string()
1778
+ ]
1779
+ );
1780
+
1781
+ let _ = std::fs::remove_dir_all(&cwd);
1782
+ }
1783
+
1784
+ #[test]
1785
+ fn send_pane_still_rejects_to_and_empty_message() {
1786
+ let cwd = tmp_workspace();
1787
+ let args = send_args(
1788
+ &cli_argv(&["--pane", "%1596", "--to", "worker", "hello"]),
1789
+ &cwd,
1790
+ )
1791
+ .unwrap();
1792
+ let err = cmd_send(&args).unwrap_err();
1793
+ assert!(
1794
+ matches!(err, CliError::Usage(ref message) if message.contains("--pane and TARGET/--to are mutually exclusive")),
1795
+ "expected --pane/--to mutual-exclusion usage error, got {err:?}"
1796
+ );
1797
+
1798
+ let args = send_args(&cli_argv(&["--pane", "%1596"]), &cwd).unwrap();
1799
+ let err = cmd_send(&args).unwrap_err();
1800
+ assert!(
1801
+ matches!(err, CliError::Usage(ref message) if message == "--pane requires a non-empty message"),
1802
+ "expected empty-message usage error, got {err:?}"
1803
+ );
1804
+
1805
+ let _ = std::fs::remove_dir_all(&cwd);
1806
+ }
1807
+
1750
1808
  #[test]
1751
1809
  fn ux_quick_start_workspace_resolves_relative_agents_dir_inside_workspace() {
1752
1810
  let cwd = tmp_workspace();
@@ -29,6 +29,10 @@ use std::path::Path;
29
29
  use crate::model::enums::{Provider, ProviderEffort};
30
30
  use crate::model::yaml::Value;
31
31
  use crate::model::{paths, spec, yaml, ModelError};
32
+ use crate::provider::wire::{
33
+ builtin_provider_model as wire_builtin_provider_model, is_claude_family,
34
+ parse_canonical_provider, provider_model_keys,
35
+ };
32
36
 
33
37
  pub const IGNORED_OWNER_TEAM_ID_FIELD: &str = "owner_team_id";
34
38
 
@@ -417,16 +421,8 @@ pub fn compile_role_agent(
417
421
  _ => None,
418
422
  })
419
423
  .unwrap_or("");
420
- let provider_enum = match provider_str {
421
- "claude" => Provider::Claude,
422
- "claude_code" => Provider::ClaudeCode,
423
- "codex" => Provider::Codex,
424
- "copilot" => Provider::Copilot,
425
- "gemini_cli" => Provider::GeminiCli,
426
- "fake" => Provider::Fake,
427
- _ => Provider::Codex, // unknown providers caught elsewhere
428
- };
429
- if effort.is_claude_only() && !matches!(provider_enum, Provider::Claude | Provider::ClaudeCode) {
424
+ let provider_enum = parse_canonical_provider(provider_str).unwrap_or(Provider::Codex);
425
+ if effort.is_claude_only() && !is_claude_family(provider_enum) {
430
426
  return Err(ModelError::Validation(format!(
431
427
  "{}: effort '{}' is only supported by claude/claude_code (provider: {provider_str})",
432
428
  role_path.display(),
@@ -543,19 +539,14 @@ fn resolve_model(role_meta: &Value, team_meta: &Value, provider: &str) -> Value
543
539
 
544
540
  fn provider_model(team_meta: &Value, provider: &str) -> Option<String> {
545
541
  let models = team_meta.get("provider_models")?;
546
- string_field(models, provider).or_else(|| match provider {
547
- "claude_code" => string_field(models, "claude"),
548
- "claude" => string_field(models, "claude_code"),
549
- _ => None,
550
- })
542
+ let provider = parse_canonical_provider(provider)?;
543
+ provider_model_keys(provider)
544
+ .iter()
545
+ .find_map(|key| string_field(models, key))
551
546
  }
552
547
 
553
548
  fn builtin_provider_model(provider: &str) -> Option<&'static str> {
554
- match provider {
555
- "claude" | "claude_code" => Some("claude-sonnet-4-6"),
556
- "codex" => Some("gpt-5.5"),
557
- _ => None,
558
- }
549
+ parse_canonical_provider(provider).and_then(wire_builtin_provider_model)
559
550
  }
560
551
 
561
552
  fn non_empty_trimmed(text: &str) -> Option<String> {
@@ -31,8 +31,8 @@ pub fn should_orphan_self_terminate(
31
31
  /// #236 dedicated `worker.abnormal_exit` surface.
32
32
  ///
33
33
  /// This is intentionally separate from `process_abnormal_records`: the generic
34
- /// abnormal path is transcript-only, while `worker.abnormal_exit` requires the
35
- /// provider-process-dead AND latest-explicit-error gate before leader notification.
34
+ /// abnormal path is transcript-only, while `worker.abnormal_exit` requires a fresh
35
+ /// latest explicit provider error before leader notification.
36
36
  #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
37
37
  #[serde(tag = "event")]
38
38
  pub enum WorkerAbnormalExitSurface {
@@ -64,7 +64,7 @@ fn detect_compaction(
64
64
  "coordinator.compaction_observed",
65
65
  json!({
66
66
  "agent_id": fact.agent_id.as_str(),
67
- "provider": provider.map(provider_name),
67
+ "provider": provider.map(runtime_event_provider_name),
68
68
  "team": team,
69
69
  "compaction_count": current,
70
70
  "stuck_loop": false,
@@ -82,7 +82,7 @@ fn detect_compaction(
82
82
  "compaction_threshold_crossed.recommend_reset",
83
83
  json!({
84
84
  "agent_id": fact.agent_id.as_str(),
85
- "provider": provider.map(provider_name),
85
+ "provider": provider.map(runtime_event_provider_name),
86
86
  "team": team,
87
87
  "compaction_count": current,
88
88
  "threshold": threshold,
@@ -239,7 +239,7 @@ fn detect_leader_api_error(
239
239
  json!({
240
240
  "leader_session_uuid": leader_session_uuid,
241
241
  "error_class": error_class,
242
- "provider": provider.map(provider_name),
242
+ "provider": provider.map(runtime_event_provider_name),
243
243
  "partial_response_streamed": partial_response_streamed,
244
244
  "worker_dispatch_just_before": [],
245
245
  "retry_count": 0,
@@ -545,7 +545,8 @@ fn leader_receiver_provider(receiver: Option<&Value>) -> Option<Provider> {
545
545
  serde_json::from_value(Value::String(raw.to_string())).ok()
546
546
  }
547
547
 
548
- fn provider_name(provider: Provider) -> &'static str {
548
+ // Event-field grammar, not provider identity parsing.
549
+ fn runtime_event_provider_name(provider: Provider) -> &'static str {
549
550
  match provider {
550
551
  Provider::Claude => "claude",
551
552
  Provider::ClaudeCode => "claude_code",