@team-agent/installer 0.5.3 → 0.5.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 (31) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +9 -1
  4. package/crates/team-agent/src/cli/diagnose.rs +191 -31
  5. package/crates/team-agent/src/cli/mod.rs +93 -34
  6. package/crates/team-agent/src/cli/send.rs +69 -2
  7. package/crates/team-agent/src/cli/status_port.rs +62 -0
  8. package/crates/team-agent/src/cli/tests/status_send.rs +12 -19
  9. package/crates/team-agent/src/coordinator/tick.rs +13 -0
  10. package/crates/team-agent/src/lifecycle/launch.rs +14 -6
  11. package/crates/team-agent/src/lifecycle/restart/agent.rs +44 -1
  12. package/crates/team-agent/src/lifecycle/restart/common.rs +53 -2
  13. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +108 -6
  14. package/crates/team-agent/src/lifecycle/restart/selection.rs +47 -18
  15. package/crates/team-agent/src/lifecycle/restart.rs +16 -6
  16. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +287 -53
  17. package/crates/team-agent/src/lifecycle/tests/restart.rs +144 -5
  18. package/crates/team-agent/src/lifecycle/types.rs +1 -0
  19. package/crates/team-agent/src/messaging/delivery.rs +209 -30
  20. package/crates/team-agent/src/messaging/leader_receiver.rs +60 -25
  21. package/crates/team-agent/src/messaging/tests/runtime.rs +446 -0
  22. package/crates/team-agent/src/messaging/watchers.rs +36 -19
  23. package/crates/team-agent/src/provider/adapter.rs +103 -41
  24. package/crates/team-agent/src/provider/session/capture.rs +328 -66
  25. package/crates/team-agent/src/provider/session/resume.rs +30 -28
  26. package/crates/team-agent/src/provider/session_scan/common.rs +117 -1
  27. package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
  28. package/crates/team-agent/src/provider/session_scan.rs +1 -0
  29. package/crates/team-agent/src/state/persist.rs +17 -0
  30. package/crates/team-agent/src/state/projection.rs +59 -4
  31. package/package.json +4 -4
@@ -67,7 +67,11 @@ pub fn classify_first_send_at(raw: &serde_json::Value) -> FirstSendAtState {
67
67
  /// * lifecycle/restart/common.rs::restart_required_missing_session_agent_ids
68
68
  pub(crate) fn restart_agent_has_context_to_preserve(agent: &serde_json::Value) -> bool {
69
69
  let has_valid_first_send_at = matches!(
70
- classify_first_send_at(agent.get("first_send_at").unwrap_or(&serde_json::Value::Null)),
70
+ classify_first_send_at(
71
+ agent
72
+ .get("first_send_at")
73
+ .unwrap_or(&serde_json::Value::Null)
74
+ ),
71
75
  FirstSendAtState::Valid
72
76
  );
73
77
  if has_valid_first_send_at {
@@ -242,6 +246,7 @@ pub(crate) fn classify_restart_plan_with_resume_validation(
242
246
  let provider = agent_provider(agent);
243
247
  let provider_wire = provider_wire(provider);
244
248
  let provider_can_resume = provider_supports_resume(provider);
249
+ let rollout_path = agent_rollout_path(agent);
245
250
  // Layer 2 self-healing (leader follow-up 2026-06-22): use the
246
251
  // structured probe so we can carry the list of paths the runtime
247
252
  // actually checked into the refusal — operators need to see WHICH
@@ -256,16 +261,15 @@ pub(crate) fn classify_restart_plan_with_resume_validation(
256
261
  agent,
257
262
  provider,
258
263
  session,
259
- agent_rollout_path(agent).as_ref(),
264
+ rollout_path.as_ref(),
260
265
  );
261
266
  (probe.exists, probe.checked_paths)
262
267
  }
263
268
  (None, Some(_), true) if resumable_provider_requires_backing(provider_wire) => {
264
- let path_opt = agent_rollout_path(agent);
265
- let exists = path_opt
269
+ let exists = rollout_path
266
270
  .as_ref()
267
271
  .is_some_and(|path| path.as_path().exists());
268
- let checked = path_opt
272
+ let checked = rollout_path
269
273
  .as_ref()
270
274
  .map(|p| vec![p.as_path().to_path_buf()])
271
275
  .unwrap_or_default();
@@ -273,6 +277,12 @@ pub(crate) fn classify_restart_plan_with_resume_validation(
273
277
  }
274
278
  _ => (true, Vec::new()),
275
279
  };
280
+ let identity_probe =
281
+ session_identity_probe_for_agent(&agent_id, provider, rollout_path.as_ref());
282
+ let session_identity_mismatch = session_id.is_some()
283
+ && provider_can_resume
284
+ && resume_backing_exists
285
+ && identity_probe.identity_ok == Some(false);
276
286
  // 0.4.7 partial-resume + RESTART-RESUME-001 (0.4.8): when a worker
277
287
  // has NEVER been captured (no session_id AND no context-bearing
278
288
  // signal at all), it is structurally non-resumable — there is no
@@ -292,7 +302,11 @@ pub(crate) fn classify_restart_plan_with_resume_validation(
292
302
  let _ = first_send_at_state; // retained for the Corrupt branch above
293
303
  let never_captured =
294
304
  restart_agent_never_captured(agent, session_id.as_ref().map(|s| s.as_str()));
295
- let decision = if session_id.is_some() && provider_can_resume && resume_backing_exists {
305
+ let decision = if session_id.is_some()
306
+ && provider_can_resume
307
+ && resume_backing_exists
308
+ && !session_identity_mismatch
309
+ {
296
310
  ResumeDecision::Resume
297
311
  } else if session_id.is_some() && allow_fresh {
298
312
  ResumeDecision::FreshStart
@@ -313,7 +327,24 @@ pub(crate) fn classify_restart_plan_with_resume_validation(
313
327
  // (round-tripped through ResumeRefusalReason::wire) so the
314
328
  // CLI/JSON contract does not change.
315
329
  let (reason_str, structured) = if session_id.is_some() {
316
- if !provider_can_resume {
330
+ if session_identity_mismatch {
331
+ let session = session_id
332
+ .as_ref()
333
+ .map(|session| session.as_str().to_string())
334
+ .unwrap_or_default();
335
+ (
336
+ "session_identity_mismatch".to_string(),
337
+ crate::provider::session::ResumeRefusalReason::SessionIdentityMismatch {
338
+ expected_agent_id: agent_id.as_str().to_string(),
339
+ embedded_agent_id: identity_probe
340
+ .embedded_agent_id
341
+ .clone()
342
+ .unwrap_or_default(),
343
+ session_id: session,
344
+ rollout_path: identity_probe.rollout_path.clone(),
345
+ },
346
+ )
347
+ } else if !provider_can_resume {
317
348
  (
318
349
  "session_unresumable".to_string(),
319
350
  crate::provider::session::ResumeRefusalReason::ProviderResumeUnsupported {
@@ -330,17 +361,15 @@ pub(crate) fn classify_restart_plan_with_resume_validation(
330
361
  // a recovery hint pointing at the agent_id (used as
331
362
  // launch-time `--name`) and spawn_cwd. Operator-facing
332
363
  // diagnostic only — no auto-resume off the hint.
333
- let recovery_hint = Some(
334
- crate::provider::session::RecoveryHint {
335
- provider_session_name_hint: Some(agent_id.as_str().to_string()),
336
- spawn_cwd: agent
337
- .get("spawn_cwd")
338
- .and_then(|v| v.as_str())
339
- .filter(|s| !s.is_empty())
340
- .map(std::path::PathBuf::from),
341
- provider: provider_wire.to_string(),
342
- },
343
- );
364
+ let recovery_hint = Some(crate::provider::session::RecoveryHint {
365
+ provider_session_name_hint: Some(agent_id.as_str().to_string()),
366
+ spawn_cwd: agent
367
+ .get("spawn_cwd")
368
+ .and_then(|v| v.as_str())
369
+ .filter(|s| !s.is_empty())
370
+ .map(std::path::PathBuf::from),
371
+ provider: provider_wire.to_string(),
372
+ });
344
373
  (
345
374
  "session_unresumable".to_string(),
346
375
  crate::provider::session::ResumeRefusalReason::SessionBackingStoreMissing {
@@ -1,7 +1,7 @@
1
1
  //! lifecycle::restart —— 单 worker 起/停/重置/删 + 整队 Route B 重建 + plan halt/status。
2
2
 
3
- use std::path::Path;
4
3
  use std::collections::BTreeMap;
4
+ use std::path::Path;
5
5
 
6
6
  use crate::model::enums::{AuthMode, Provider};
7
7
  use crate::model::ids::AgentId;
@@ -32,22 +32,28 @@ mod remove;
32
32
  mod selection;
33
33
  mod team_state;
34
34
 
35
- pub use agent::{reset_agent, reset_agent_with_transport, start_agent, start_agent_with_transport, stop_agent, stop_agent_with_transport};
36
35
  pub(crate) use agent::start_agent_at_paths;
36
+ pub use agent::{
37
+ reset_agent, reset_agent_with_transport, start_agent, start_agent_with_transport, stop_agent,
38
+ stop_agent_with_transport,
39
+ };
37
40
  pub(crate) use common::refresh_missing_provider_sessions;
38
41
  pub(crate) use common::restart_required_missing_session_agent_ids;
42
+ pub(crate) use common::session_identity_probe_for_agent;
39
43
  // 0.3.24 add-agent socket drift fix: state-aware tmux resolver shared with
40
44
  // `lifecycle::launch::add_agent` / `fork_agent` so all three (restart / add / fork)
41
45
  // route to the SAME tmux socket the live team uses.
42
46
  pub(crate) use common::lifecycle_worker_tmux_backend_for_selected_state;
43
47
  pub use orchestrator::{halt_plan, plan_status};
48
+ pub(crate) use rebuild::restart_with_transport_with_session_convergence_deadline;
44
49
  pub use rebuild::{
45
50
  restart, restart_candidates, restart_with_session_convergence_deadline, restart_with_transport,
46
51
  restart_with_transport_with_readiness_deadline, select_restart_state,
47
52
  };
48
- pub(crate) use rebuild::restart_with_transport_with_session_convergence_deadline;
49
53
  pub use remove::{remove_agent, remove_agent_with_transport};
50
- pub use selection::{classify_first_send_at, classify_restart_plan, decide_start_mode, python_type_name};
54
+ pub use selection::{
55
+ classify_first_send_at, classify_restart_plan, decide_start_mode, python_type_name,
56
+ };
51
57
  // Layer 2 (leader follow-up 2026-06-22): test-visible workspace-aware
52
58
  // classification so lifecycle/tests/restart.rs can exercise the
53
59
  // SessionBackingStoreMissing + checked_paths + RecoveryHint path
@@ -55,7 +61,9 @@ pub use selection::{classify_first_send_at, classify_restart_plan, decide_start_
55
61
  pub(crate) use selection::classify_restart_plan_with_resume_validation;
56
62
  pub(crate) use team_state::write_team_state;
57
63
 
58
- pub(crate) fn lifecycle_run_workspace(workspace: &Path) -> Result<std::path::PathBuf, LifecycleError> {
64
+ pub(crate) fn lifecycle_run_workspace(
65
+ workspace: &Path,
66
+ ) -> Result<std::path::PathBuf, LifecycleError> {
59
67
  crate::model::paths::canonical_run_workspace(workspace)
60
68
  .map_err(|e| LifecycleError::StatePersist(e.to_string()))
61
69
  }
@@ -93,7 +101,9 @@ fn lifecycle_paths(workspace: &Path, team: Option<&str>) -> Result<LifecyclePath
93
101
  .spec_workspace
94
102
  .clone()
95
103
  .or_else(|| selected_state_spec_workspace(&selected.state))
96
- .ok_or_else(|| LifecycleError::TeamSelect("active team spec workspace not found".to_string()))?;
104
+ .ok_or_else(|| {
105
+ LifecycleError::TeamSelect("active team spec workspace not found".to_string())
106
+ })?;
97
107
  Ok(LifecyclePaths {
98
108
  run_workspace: selected.run_workspace,
99
109
  spec_workspace,