@team-agent/installer 0.4.3 → 0.4.4

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.
package/Cargo.lock CHANGED
@@ -566,7 +566,7 @@ dependencies = [
566
566
 
567
567
  [[package]]
568
568
  name = "team-agent"
569
- version = "0.4.3"
569
+ version = "0.4.4"
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.3"
12
+ version = "0.4.4"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -654,6 +654,13 @@ pub fn restart_with_transport_with_session_convergence_deadline(
654
654
  &failed_agents,
655
655
  "partial",
656
656
  )?;
657
+ // 0.3.30 Bug 1: auto-attach on partial restart too — workers that did
658
+ // come up still need a leader_receiver pane to deliver report_result.
659
+ try_autobind_leader_after_restart(
660
+ &selected.run_workspace,
661
+ Some(selected.team_key.as_str()),
662
+ &state,
663
+ );
657
664
  return Ok(RestartReport::Partial {
658
665
  session_name,
659
666
  agents: successful_agents,
@@ -669,6 +676,16 @@ pub fn restart_with_transport_with_session_convergence_deadline(
669
676
  &failed_agents,
670
677
  "ok",
671
678
  )?;
679
+ // 0.3.30 Bug 1: auto-attach leader from caller's TMUX_PANE if available.
680
+ // Mirrors quick-start's seed_launched_owner_from_env behaviour: a restart
681
+ // invoked from a tmux pane should bind that pane as leader_receiver,
682
+ // restoring the worker→leader delivery path. Failure is non-fatal — the
683
+ // user can still run `team-agent attach-leader` manually.
684
+ try_autobind_leader_after_restart(
685
+ &selected.run_workspace,
686
+ Some(&selected.team_key),
687
+ &state,
688
+ );
672
689
  // 0.3.28 Step 1: topology invariant guard (warn-only). Same pattern as
673
690
  // `lifecycle::launch::launch_with_transport_in_workspace` — logs to stderr,
674
691
  // never panics. Hard error path is deferred to Step 10.
@@ -1063,6 +1080,68 @@ fn verify_spawned_agent_live(
1063
1080
  Ok(())
1064
1081
  }
1065
1082
 
1083
+ /// 0.3.30 Bug 1: restart success path auto-attach.
1084
+ /// When `TMUX_PANE` is present in the caller's env, treat the restart as if
1085
+ /// the user had also run `attach-leader` from that pane. Mirrors quick-start's
1086
+ /// `seed_launched_owner_from_env` semantics.
1087
+ ///
1088
+ /// Failure modes are intentionally non-fatal — `attach_leader` returns Err if
1089
+ /// the pane validation rejects (e.g. caller pane is a registered worker pane,
1090
+ /// E51 guard). In that case the user must still run `attach-leader` manually,
1091
+ /// matching pre-fix behaviour. We log to stderr so the operator sees why
1092
+ /// auto-attach didn't take.
1093
+ fn try_autobind_leader_after_restart(
1094
+ workspace: &std::path::Path,
1095
+ team: Option<&str>,
1096
+ state: &serde_json::Value,
1097
+ ) {
1098
+ if std::env::var_os("TMUX_PANE").is_none() {
1099
+ return;
1100
+ }
1101
+ // Provider: prefer the existing team_owner.provider (if rebind),
1102
+ // else leader_receiver.provider (stale, but still informative),
1103
+ // else default to ClaudeCode (matches the most common deployment).
1104
+ let provider = state
1105
+ .pointer("/team_owner/provider")
1106
+ .and_then(serde_json::Value::as_str)
1107
+ .or_else(|| {
1108
+ state
1109
+ .pointer("/leader_receiver/provider")
1110
+ .and_then(serde_json::Value::as_str)
1111
+ })
1112
+ .and_then(|s| match s {
1113
+ "codex" => Some(crate::model::enums::Provider::Codex),
1114
+ "claude" | "claude_code" | "claude-code" => {
1115
+ Some(crate::model::enums::Provider::ClaudeCode)
1116
+ }
1117
+ "copilot" => Some(crate::model::enums::Provider::Copilot),
1118
+ _ => None,
1119
+ })
1120
+ .unwrap_or(crate::model::enums::Provider::ClaudeCode);
1121
+ let team_str = team;
1122
+ match crate::leader::attach_leader(workspace, team_str, None, provider) {
1123
+ Ok(result) if result.ok => {
1124
+ eprintln!(
1125
+ "team_agent::restart auto_attach_leader ok pane={:?} team={:?}",
1126
+ result.bound_pane_id.as_ref().map(|p| p.as_str()),
1127
+ team_str,
1128
+ );
1129
+ }
1130
+ Ok(result) => {
1131
+ eprintln!(
1132
+ "team_agent::restart auto_attach_leader skipped reason={:?} team={:?}",
1133
+ result.reason, team_str,
1134
+ );
1135
+ }
1136
+ Err(error) => {
1137
+ eprintln!(
1138
+ "team_agent::restart auto_attach_leader failed error={error} team={team_str:?} \
1139
+ (run `team-agent attach-leader` from your tmux pane to bind manually)",
1140
+ );
1141
+ }
1142
+ }
1143
+ }
1144
+
1066
1145
  fn mark_leader_receiver_rebind_required(state: &mut serde_json::Value, session_name: &SessionName) {
1067
1146
  let Some(receiver) = state
1068
1147
  .get_mut("leader_receiver")
@@ -2648,7 +2648,7 @@ fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
2648
2648
  "spawned_at",
2649
2649
  "pane_id",
2650
2650
  ],
2651
- "0.3.28 running agent state: no adaptive layout bookkeeping; raw={raw}"
2651
+ "0.3.31 Codex capture correction: no framework _pending_session_id for Codex (CLI doesn't honor --session-id); raw={raw}"
2652
2652
  );
2653
2653
  assert_eq!(agent["status"], json!("running"));
2654
2654
  assert_eq!(agent["provider"], json!("codex"));
@@ -476,6 +476,14 @@ impl ProviderAdapter for BasicProviderAdapter {
476
476
  }
477
477
  // codex.py:105-118 — the profile command overrides (codex_profile / codex_config)
478
478
  // ride on `agent["_provider_profile"]`, which only the plan path carries.
479
+ //
480
+ // 0.3.31 Codex capture correction (reverts ad518f8): Codex CLI does
481
+ // NOT accept `--session-id`, so a framework-generated UUID is never
482
+ // matched against Codex's own session_meta.payload.id. Setting
483
+ // expected_session_id caused the apply-time Stage 1 guard to
484
+ // permanently reject the real Codex transcript. Codex capture must
485
+ // anchor on (cwd, spawned_at) instead — handled in
486
+ // `scan_session_candidates_once` below.
479
487
  Provider::Codex => Ok(CommandPlan::argv_only(codex_base_command(
480
488
  None,
481
489
  ctx.auth_mode,
@@ -1049,6 +1057,38 @@ fn scan_session_candidates_once(
1049
1057
  agent_path_match,
1050
1058
  });
1051
1059
  }
1060
+ // 0.3.31 Codex capture correction: HARD (cwd, spawned_at) filter.
1061
+ // Codex CLI does NOT honor `--session-id` so we cannot use
1062
+ // expected_session_id semantics. The only safe identity boundary is:
1063
+ // * session_meta.payload.cwd == spawn_cwd (already filtered above via
1064
+ // requires_cwd_match), AND
1065
+ // * session_meta.payload.timestamp >= spawned_at - small_grace, OR file
1066
+ // mtime >= spawned_at - small_grace (the candidate must be POST-spawn).
1067
+ // Candidates older than the current spawn are pre-reset / pre-restart
1068
+ // remnants and MUST be dropped, not merely de-prioritized — otherwise the
1069
+ // single-candidate allocator path picks them, and the Stage 1 mismatch
1070
+ // guard later rejects them, producing the 0.4.4 attribution_ambiguous loop.
1071
+ if matches!(provider, Provider::Codex) {
1072
+ if let Some(spawned_at) = context.spawned_at.as_deref().and_then(parse_spawned_at) {
1073
+ // 5-second grace: allow for clock skew between Codex's session
1074
+ // timestamp and our spawned_at (Codex records LOCAL time before
1075
+ // RFC3339-encoding, framework records UTC; small skew possible
1076
+ // across midnight or DST boundary).
1077
+ let grace = std::time::Duration::from_secs(5);
1078
+ let cutoff = spawned_at.checked_sub(grace).unwrap_or(spawned_at);
1079
+ out.retain(|candidate| {
1080
+ let path = match candidate.captured.rollout_path.as_ref() {
1081
+ Some(p) => p.as_path(),
1082
+ None => return false,
1083
+ };
1084
+ std::fs::metadata(path)
1085
+ .and_then(|meta| meta.modified())
1086
+ .map(|mtime| mtime >= cutoff)
1087
+ .unwrap_or(false)
1088
+ });
1089
+ }
1090
+ }
1091
+
1052
1092
  // E6 层1·C(机会性兜底):若盘上真有 expected_session_id 命名的 transcript(claude 哪天
1053
1093
  // 真采用 --session-id,或别的 provider 本就采用),直接唯一命中,省去时间窗扫描。
1054
1094
  // 命不中(交互式 claude 现实:不落 <expected>.jsonl)→ 回落 B。
@@ -428,11 +428,22 @@ where
428
428
  .and_then(Value::as_str)
429
429
  .filter(|value| !value.is_empty())
430
430
  .map(str::to_string),
431
- expected_session_id: agent
432
- .get("_pending_session_id")
433
- .and_then(Value::as_str)
434
- .filter(|value| !value.is_empty())
435
- .map(SessionId::new),
431
+ // 0.3.31 Codex capture correction: Codex does NOT honor
432
+ // `--session-id`, so any `_pending_session_id` stored for a Codex
433
+ // agent (stale 0.3.30 state, or the framework's pre-spawn token)
434
+ // is a local-only token and must NOT be used as expected_session_id
435
+ // — that would trigger the Stage 1 mismatch guard against Codex's
436
+ // real session_meta.payload.id and permanently reject the correct
437
+ // transcript. Codex capture anchors purely on (cwd, spawned_at).
438
+ expected_session_id: if matches!(provider, Provider::Codex) {
439
+ None
440
+ } else {
441
+ agent
442
+ .get("_pending_session_id")
443
+ .and_then(Value::as_str)
444
+ .filter(|value| !value.is_empty())
445
+ .map(SessionId::new)
446
+ },
436
447
  provider_projects_root: agent
437
448
  .get("claude_projects_root")
438
449
  .and_then(Value::as_str)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "npx installer for Team Agent",
5
5
  "keywords": [
6
6
  "codex",
@@ -20,9 +20,9 @@
20
20
  "team-agent-installer": "npm/install.mjs"
21
21
  },
22
22
  "optionalDependencies": {
23
- "@team-agent/cli-darwin-arm64": "0.4.3",
24
- "@team-agent/cli-darwin-x64": "0.4.3",
25
- "@team-agent/cli-linux-x64": "0.4.3"
23
+ "@team-agent/cli-darwin-arm64": "0.4.4",
24
+ "@team-agent/cli-darwin-x64": "0.4.4",
25
+ "@team-agent/cli-linux-x64": "0.4.4"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",