@team-agent/installer 0.5.100 → 0.5.102

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/diagnose.rs +29 -7
  4. package/crates/team-agent/src/cli/mod.rs +24 -8
  5. package/crates/team-agent/src/cli/tests/main_preserved.rs +0 -74
  6. package/crates/team-agent/src/compiler/tests.rs +20 -23
  7. package/crates/team-agent/src/compiler.rs +52 -65
  8. package/crates/team-agent/src/coordinator/tests/s3_bounded.rs +16 -48
  9. package/crates/team-agent/src/leader/start.rs +1 -14
  10. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +211 -16
  11. package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +1 -0
  12. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +15 -5
  13. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +89 -6
  14. package/crates/team-agent/src/lifecycle/launch/role_source.rs +168 -32
  15. package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -1
  16. package/crates/team-agent/src/lifecycle/launch.rs +11 -3
  17. package/crates/team-agent/src/lifecycle/profile_launch.rs +23 -8
  18. package/crates/team-agent/src/lifecycle/restart/agent.rs +2 -2
  19. package/crates/team-agent/src/lifecycle/restart/avatar.rs +1806 -0
  20. package/crates/team-agent/src/lifecycle/restart/common.rs +115 -2
  21. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +8 -2
  22. package/crates/team-agent/src/lifecycle/restart/selection.rs +14 -2
  23. package/crates/team-agent/src/lifecycle/restart.rs +11 -4
  24. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +0 -28
  25. package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +11 -9
  26. package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +8 -401
  27. package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +2 -2
  28. package/crates/team-agent/src/lifecycle/tests/pi_fork_session_inheritance_red.rs +1398 -0
  29. package/crates/team-agent/src/lifecycle/tests.rs +1 -0
  30. package/crates/team-agent/src/lifecycle/types.rs +10 -2
  31. package/crates/team-agent/src/lifecycle/worker_command_context.rs +22 -8
  32. package/crates/team-agent/src/mcp_server/helpers.rs +0 -16
  33. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +2 -7
  34. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -127
  35. package/crates/team-agent/src/mcp_server/mod.rs +7 -12
  36. package/crates/team-agent/src/mcp_server/tests/scoped.rs +1 -132
  37. package/crates/team-agent/src/mcp_server/tests/send.rs +1 -93
  38. package/crates/team-agent/src/mcp_server/tests/tools.rs +0 -61
  39. package/crates/team-agent/src/mcp_server/tests/wire.rs +6 -51
  40. package/crates/team-agent/src/mcp_server/tests.rs +4 -200
  41. package/crates/team-agent/src/mcp_server/tools.rs +5 -396
  42. package/crates/team-agent/src/mcp_server/types.rs +3 -36
  43. package/crates/team-agent/src/mcp_server/wire.rs +2 -223
  44. package/crates/team-agent/src/messaging/leader_receiver.rs +1 -1
  45. package/crates/team-agent/src/messaging/mod.rs +2 -2
  46. package/crates/team-agent/src/messaging/scheduler.rs +2 -205
  47. package/crates/team-agent/src/messaging/tests/basic.rs +0 -31
  48. package/crates/team-agent/src/messaging/tests/main_preserved.rs +0 -73
  49. package/crates/team-agent/src/messaging/tests/runtime.rs +0 -26
  50. package/crates/team-agent/src/messaging/types.rs +0 -20
  51. package/crates/team-agent/src/model/spec.rs +15 -18
  52. package/crates/team-agent/src/provider/types.rs +2 -0
  53. package/crates/team-agent/src/state/persist.rs +58 -2
  54. package/crates/team-agent/src/state/projection.rs +5 -1
  55. package/crates/team-agent/src/state/repository/tests.rs +1 -16
  56. package/crates/team-agent/src/state/repository.rs +126 -37
  57. package/crates/team-agent/src/transport/test_support.rs +23 -3
  58. package/package.json +4 -4
  59. package/skills/team-agent/SKILL.md +18 -47
  60. package/skills/team-agent/command-coverage.json +7 -0
  61. package/skills/team-agent/references/team-agent-operator.md +28 -118
  62. package/skills/team-agent/references/team-in-team.md +4 -19
  63. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +0 -484
@@ -397,7 +397,7 @@ pub(super) fn spawn_agent_window(
397
397
  model: command_model,
398
398
  effort: restart_effort,
399
399
  system_prompt: &system_prompt,
400
- team_mcp_tools: &["send_message", "report_result"],
400
+ team_mcp_tools: &["send_message", "report_result", "get_team_status"],
401
401
  mcp_config: &mcp_config,
402
402
  session_scope: crate::lifecycle::launch::pi_mcp::PiSessionScope::Isolated,
403
403
  };
@@ -954,7 +954,7 @@ pub(crate) fn lifecycle_worker_transport_for_selected_state(
954
954
  /// returns: 后端与 endpoint 来源
955
955
  /// errors: state 声明后端是 conpty 时返回 TeamSelect
956
956
  /// ---
957
- pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
957
+ pub(crate) fn lifecycle_worker_tmux_backend_selection_for_state(
958
958
  run_workspace: &Path,
959
959
  state: &serde_json::Value,
960
960
  ) -> Result<crate::tmux_backend::RuntimeTmuxBackendSelection, LifecycleError> {
@@ -1344,6 +1344,119 @@ pub(crate) fn session_identity_probe_for_agent(
1344
1344
  }
1345
1345
  }
1346
1346
 
1347
+ /// ---
1348
+ /// purpose: 将合法 Pi fork 血统纳入共享会话身份判定
1349
+ /// params:
1350
+ /// workspace/state/agent: 当前被验证席位及同队来源记录
1351
+ /// rollout_path: 当前席位的精确 backing
1352
+ /// returns: 字面身份一致,或经独立 Pi backing 与 fork 血统验证后合法继承时为 true
1353
+ /// ---
1354
+ pub(crate) fn session_identity_probe_for_agent_with_fork_ancestry(
1355
+ workspace: &Path,
1356
+ state: &serde_json::Value,
1357
+ agent_id: &AgentId,
1358
+ agent: &serde_json::Value,
1359
+ provider: Provider,
1360
+ rollout_path: Option<&RolloutPath>,
1361
+ ) -> SessionIdentityProbeResult {
1362
+ let mut probe = session_identity_probe_for_agent(agent_id, provider, rollout_path);
1363
+ if probe.identity_ok != Some(false)
1364
+ || !pi_fork_ancestor_identity_is_valid(
1365
+ workspace,
1366
+ state,
1367
+ agent_id,
1368
+ agent,
1369
+ provider,
1370
+ rollout_path,
1371
+ probe.embedded_agent_id.as_deref(),
1372
+ )
1373
+ {
1374
+ return probe;
1375
+ }
1376
+ probe.identity_ok = Some(true);
1377
+ probe
1378
+ }
1379
+
1380
+ fn pi_fork_ancestor_identity_is_valid(
1381
+ workspace: &Path,
1382
+ state: &serde_json::Value,
1383
+ agent_id: &AgentId,
1384
+ agent: &serde_json::Value,
1385
+ provider: Provider,
1386
+ rollout_path: Option<&RolloutPath>,
1387
+ embedded_agent_id: Option<&str>,
1388
+ ) -> bool {
1389
+ if provider != Provider::Pi
1390
+ || agent.get("attribution_ambiguous").and_then(serde_json::Value::as_bool) == Some(true)
1391
+ || agent.get("capture_state").and_then(serde_json::Value::as_str)
1392
+ == Some("attribution_ambiguous")
1393
+ || !matches!(
1394
+ agent
1395
+ .get("attribution_confidence")
1396
+ .and_then(serde_json::Value::as_str),
1397
+ Some("high" | "medium")
1398
+ )
1399
+ {
1400
+ return false;
1401
+ }
1402
+ let (Some(embedded_agent_id), Some(forked_from), Some(target_path)) = (
1403
+ embedded_agent_id,
1404
+ agent
1405
+ .get("forked_from")
1406
+ .and_then(serde_json::Value::as_str)
1407
+ .filter(|value| !value.is_empty()),
1408
+ rollout_path.map(RolloutPath::as_path),
1409
+ ) else {
1410
+ return false;
1411
+ };
1412
+ if forked_from != embedded_agent_id || forked_from == agent_id.as_str() {
1413
+ return false;
1414
+ }
1415
+ let Some(source_agent) = state
1416
+ .get("agents")
1417
+ .and_then(|agents| agents.get(forked_from))
1418
+ .filter(|source| agent_provider(source) == Provider::Pi)
1419
+ else {
1420
+ return false;
1421
+ };
1422
+ let Some(source_path) = agent_rollout_path(source_agent) else {
1423
+ return false;
1424
+ };
1425
+ if source_path.as_path() == target_path {
1426
+ return false;
1427
+ }
1428
+ let (Some(session_id), Ok(target_path), Some(target_root)) = (
1429
+ agent_session_id(agent),
1430
+ std::fs::canonicalize(target_path),
1431
+ agent
1432
+ .get("claude_projects_root")
1433
+ .and_then(serde_json::Value::as_str)
1434
+ .filter(|path| !path.is_empty())
1435
+ .map(std::path::PathBuf::from),
1436
+ ) else {
1437
+ return false;
1438
+ };
1439
+ let Ok(target_root) = std::fs::canonicalize(target_root) else {
1440
+ return false;
1441
+ };
1442
+ if !target_path.starts_with(target_root)
1443
+ || std::fs::canonicalize(source_path.as_path())
1444
+ .is_ok_and(|source_path| source_path == target_path)
1445
+ || !resume_backing_probe_for_agent(
1446
+ workspace,
1447
+ agent_id,
1448
+ agent,
1449
+ provider,
1450
+ &session_id,
1451
+ rollout_path,
1452
+ )
1453
+ .exists
1454
+ {
1455
+ return false;
1456
+ }
1457
+ true
1458
+ }
1459
+
1347
1460
  /// ---
1348
1461
  /// purpose: 按 provider 分支探测 resume 依据,并记录所有查过的路径
1349
1462
  /// returns: 存在位与查过的路径列表;持久化的 rollout 路径即使不存在也记进列表
@@ -1133,8 +1133,11 @@ fn restart_with_selected_team_and_transport(
1133
1133
  &session_id,
1134
1134
  agent_rollout_path(&agent).as_ref(),
1135
1135
  );
1136
- let identity_probe = session_identity_probe_for_agent(
1136
+ let identity_probe = session_identity_probe_for_agent_with_fork_ancestry(
1137
+ &selected.run_workspace,
1138
+ &state,
1137
1139
  &decision.agent_id,
1140
+ &agent,
1138
1141
  provider,
1139
1142
  agent_rollout_path(&agent).as_ref(),
1140
1143
  );
@@ -1221,8 +1224,11 @@ fn restart_with_selected_team_and_transport(
1221
1224
  &session_id,
1222
1225
  agent_rollout_path(&agent_after).as_ref(),
1223
1226
  );
1224
- let identity_probe_after = session_identity_probe_for_agent(
1227
+ let identity_probe_after = session_identity_probe_for_agent_with_fork_ancestry(
1228
+ &selected.run_workspace,
1229
+ &state,
1225
1230
  &decision.agent_id,
1231
+ &agent_after,
1226
1232
  provider,
1227
1233
  agent_rollout_path(&agent_after).as_ref(),
1228
1234
  );
@@ -349,8 +349,20 @@ pub(crate) fn classify_restart_plan_with_resume_validation(
349
349
  }
350
350
  _ => (true, Vec::new()),
351
351
  };
352
- let identity_probe =
353
- session_identity_probe_for_agent(&agent_id, provider, rollout_path.as_ref());
352
+ let identity_probe = workspace
353
+ .map(|workspace| {
354
+ session_identity_probe_for_agent_with_fork_ancestry(
355
+ workspace,
356
+ state,
357
+ &agent_id,
358
+ agent,
359
+ provider,
360
+ rollout_path.as_ref(),
361
+ )
362
+ })
363
+ .unwrap_or_else(|| {
364
+ session_identity_probe_for_agent(&agent_id, provider, rollout_path.as_ref())
365
+ });
354
366
  let session_identity_mismatch = session_id.is_some()
355
367
  && provider_can_resume
356
368
  && resume_backing_exists
@@ -66,6 +66,8 @@ struct LifecyclePathRefs<'a> {
66
66
  }
67
67
 
68
68
  mod agent;
69
+ mod avatar;
70
+ pub(crate) use avatar::fork_pi_new_seat_locked;
69
71
  mod common;
70
72
  mod orchestrator;
71
73
  pub mod preflight;
@@ -82,11 +84,16 @@ pub(crate) use agent::{
82
84
  };
83
85
  pub(crate) use common::refresh_missing_provider_sessions;
84
86
  pub(crate) use common::restart_required_missing_session_agent_ids;
85
- pub(crate) use common::session_identity_probe_for_agent;
87
+ pub(crate) use common::{
88
+ session_identity_probe_for_agent, session_identity_probe_for_agent_with_fork_ancestry,
89
+ };
86
90
  // 0.3.24 add-agent socket drift fix: state-aware tmux resolver shared with
87
91
  // `lifecycle::launch::add_agent` / `fork_agent` so all three (restart / add / fork)
88
92
  // route to the SAME tmux socket the live team uses.
89
- pub(crate) use common::lifecycle_worker_tmux_backend_for_selected_state;
93
+ pub(crate) use common::{
94
+ lifecycle_worker_tmux_backend_for_selected_state,
95
+ lifecycle_worker_tmux_backend_selection_for_state,
96
+ };
90
97
  pub(crate) use orchestrator::{halt_plan, plan_status};
91
98
  pub(crate) use rebuild::restart_with_transport_with_session_convergence_deadline;
92
99
  // 0.5.38 (`.team/artifacts/startup-latency-locate.md` §5): expose the phase
@@ -100,10 +107,10 @@ pub(crate) use rebuild::{
100
107
  restart_candidates, restart_with_transport, restart_with_transport_with_readiness_deadline,
101
108
  select_restart_state,
102
109
  };
103
- pub use remove::{remove_agent, remove_agent_flag_requirements};
104
110
  pub(crate) use remove::remove_agent_with_transport;
105
- pub(crate) use selection::{classify_first_send_at, classify_restart_plan, python_type_name};
111
+ pub use remove::{remove_agent, remove_agent_flag_requirements};
106
112
  pub use selection::decide_start_mode;
113
+ pub(crate) use selection::{classify_first_send_at, classify_restart_plan, python_type_name};
107
114
  // Layer 2 (leader follow-up 2026-06-22): test-visible workspace-aware
108
115
  // classification so lifecycle/tests/restart.rs can exercise the
109
116
  // SessionBackingStoreMissing + checked_paths + RecoveryHint path
@@ -532,22 +532,6 @@ fn reset_stopped_true_still_rechecks_same_role_residue_before_start() {
532
532
  );
533
533
  }
534
534
 
535
- #[test]
536
- fn mcp_reset_output_exposes_capture_outcome_and_weak_reset_proof() {
537
- let src = include_str!(concat!(
538
- env!("CARGO_MANIFEST_DIR"),
539
- "/src/mcp_server/lifecycle_tools/agent_ops.rs"
540
- ));
541
- let reset = source_section(src, "pub(crate) fn reset_agent", "pub(crate) fn fork_agent");
542
- let json_object = reset_success_json_object(reset);
543
- let missing = ["\"capture_state\":", "\"reset_proof\":", "\"transcript_missing\"", "\"attribution_ambiguous\"", "\"weak\""]
544
- .into_iter().filter(|needle| !json_object.contains(needle)).collect::<Vec<_>>();
545
- assert!(
546
- missing.is_empty(),
547
- "P0 MCP reset: ok=true/status=reset must expose additive capture_state and reset_proof=weak inside the ResetAgentOutcome::Reset serde_json::json! object; missing={missing:?}; reset_json={json_object}"
548
- );
549
- }
550
-
551
535
  #[test]
552
536
  #[serial(env)]
553
537
  fn abnormal_shell_pane_with_live_delivery_is_not_dead_and_dedupe_ignores_size() {
@@ -972,15 +956,3 @@ impl ProviderRegistry for RealAdapterRegistry {
972
956
  get_adapter(provider)
973
957
  }
974
958
  }
975
-
976
- fn source_section<'a>(src: &'a str, start: &str, end: &str) -> &'a str { let start = src.find(start).unwrap_or(0); let rest = &src[start..]; &rest[..rest.find(end).unwrap_or(rest.len())] }
977
-
978
- fn reset_success_json_object(reset_src: &str) -> &str {
979
- let success = source_section(reset_src, "ResetAgentOutcome::Reset", "ResetAgentOutcome::Refused");
980
- let macro_start = success.find("serde_json::json!({").expect("reset success json object"); let object_start = macro_start + success[macro_start..].find('{').unwrap(); let mut depth = 0_i32;
981
- for (offset, ch) in success[object_start..].char_indices() {
982
- if ch == '{' { depth += 1; } if ch == '}' { depth -= 1; }
983
- if depth == 0 { return &success[object_start..object_start + offset + 1]; }
984
- }
985
- panic!("unterminated reset success json object")
986
- }
@@ -108,12 +108,13 @@ fn pi_role_ignores_tools_and_preserves_provider_defaults() {
108
108
  .expect("Pi model and effort may use provider defaults");
109
109
  assert_eq!(
110
110
  compiled.agent.get("model"),
111
- Some(&Value::Null),
112
- "omitted Pi model must not inherit a synthesized framework default"
111
+ Some(&Value::Str("openai-codex/gpt-6-luna".to_string())),
112
+ "omitted Pi model uses the Luna default rather than team metadata"
113
113
  );
114
- assert!(
115
- compiled.agent.get("effort").is_none(),
116
- "omitted Pi effort must not inherit a synthesized framework default"
114
+ assert_eq!(
115
+ compiled.agent.get("effort"),
116
+ Some(&Value::Str("max".to_string())),
117
+ "omitted Pi effort uses the global max default rather than team metadata"
117
118
  );
118
119
 
119
120
  let models = parse_pi_list_models_table(CATALOG).expect("catalog fixture");
@@ -160,10 +161,11 @@ fn pi_role_uses_standard_bypass_field_without_fabricating_argv() {
160
161
  );
161
162
 
162
163
  let missing_ack = valid_role_with("").replace("dangerously_skip_permissions: true\n", "");
163
- let text = compile_error(compile_pi_role(&missing_ack)).to_string();
164
- assert!(
165
- text.contains("missing front matter field dangerously_skip_permissions"),
166
- "got {text}"
164
+ let compiled = compile_pi_role(&missing_ack)
165
+ .expect("omitted bypass metadata defaults to true");
166
+ assert_eq!(
167
+ compiled.agent.get("dangerously_skip_permissions"),
168
+ Some(&Value::Bool(true))
167
169
  );
168
170
  }
169
171