@team-agent/installer 0.5.1 → 0.5.3

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 (55) hide show
  1. package/Cargo.lock +120 -9
  2. package/Cargo.toml +2 -2
  3. package/crates/team-agent/Cargo.toml +21 -0
  4. package/crates/team-agent/src/app_server_test_support.rs +258 -0
  5. package/crates/team-agent/src/cli/adapters.rs +32 -3
  6. package/crates/team-agent/src/cli/attach_app_server_leader.rs +16 -0
  7. package/crates/team-agent/src/cli/diagnose.rs +14 -5
  8. package/crates/team-agent/src/cli/emit.rs +79 -1
  9. package/crates/team-agent/src/cli/mod.rs +190 -80
  10. package/crates/team-agent/src/cli/named_address.rs +111 -0
  11. package/crates/team-agent/src/cli/send.rs +98 -3
  12. package/crates/team-agent/src/cli/status_port.rs +44 -6
  13. package/crates/team-agent/src/cli/tests/named_address.rs +135 -0
  14. package/crates/team-agent/src/cli/tests/run_delegation.rs +2 -0
  15. package/crates/team-agent/src/cli/types.rs +17 -0
  16. package/crates/team-agent/src/codex_app_server.rs +549 -0
  17. package/crates/team-agent/src/conpty/backend.rs +822 -0
  18. package/crates/team-agent/src/conpty/mod.rs +32 -0
  19. package/crates/team-agent/src/coordinator/backoff.rs +132 -7
  20. package/crates/team-agent/src/coordinator/conpty_shim.rs +730 -0
  21. package/crates/team-agent/src/coordinator/health.rs +97 -11
  22. package/crates/team-agent/src/coordinator/mod.rs +8 -0
  23. package/crates/team-agent/src/coordinator/tests/daemon.rs +2 -1
  24. package/crates/team-agent/src/coordinator/tests/tick_core.rs +6 -0
  25. package/crates/team-agent/src/diagnose/orphans.rs +29 -10
  26. package/crates/team-agent/src/leader/lease.rs +144 -7
  27. package/crates/team-agent/src/leader/owner_bind.rs +5 -2
  28. package/crates/team-agent/src/leader/provider_attribution.rs +19 -34
  29. package/crates/team-agent/src/leader/start.rs +18 -5
  30. package/crates/team-agent/src/leader/tests/lease_api.rs +179 -0
  31. package/crates/team-agent/src/lib.rs +36 -0
  32. package/crates/team-agent/src/lifecycle/launch.rs +207 -94
  33. package/crates/team-agent/src/lifecycle/lock.rs +40 -33
  34. package/crates/team-agent/src/lifecycle/restart/agent.rs +10 -18
  35. package/crates/team-agent/src/lifecycle/restart/common.rs +70 -0
  36. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +7 -0
  37. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +7 -0
  38. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +63 -0
  39. package/crates/team-agent/src/mcp_server/wire.rs +6 -7
  40. package/crates/team-agent/src/messaging/delivery.rs +329 -9
  41. package/crates/team-agent/src/messaging/leader_receiver.rs +17 -138
  42. package/crates/team-agent/src/messaging/mod.rs +2 -2
  43. package/crates/team-agent/src/messaging/results.rs +78 -21
  44. package/crates/team-agent/src/messaging/tests/runtime.rs +237 -0
  45. package/crates/team-agent/src/packaging/tests.rs +41 -6
  46. package/crates/team-agent/src/packaging/types.rs +31 -3
  47. package/crates/team-agent/src/platform/argv.rs +324 -0
  48. package/crates/team-agent/src/platform/errors.rs +95 -0
  49. package/crates/team-agent/src/platform/file_lock.rs +418 -0
  50. package/crates/team-agent/src/platform/mod.rs +66 -0
  51. package/crates/team-agent/src/platform/process.rs +555 -0
  52. package/crates/team-agent/src/state/persist.rs +205 -25
  53. package/crates/team-agent/src/tmux_backend.rs +94 -13
  54. package/crates/team-agent/src/transport_factory.rs +751 -0
  55. package/package.json +4 -4
@@ -0,0 +1,32 @@
1
+ //! 0.5.x Windows-native transport Phase 1: ConPTY backend.
2
+ //!
3
+ //! Wire protocol + portable shim state machine live in the
4
+ //! `conpty-transport` crate (portable, no team-agent runtime deps —
5
+ //! consumed directly by the Windows `windows-shim` binary). This
6
+ //! module wires that portable machinery into team-agent's `Transport`
7
+ //! trait via `ConPtyBackend`.
8
+ //!
9
+ //! Truth sources (READ-ONLY):
10
+ //! - Design: `.team/artifacts/0.5.x-windows-native-transport-design.md`
11
+ //! - CR verdict: `.team/artifacts/0.5.x-windows-transport-cr-verdict.md`
12
+ //!
13
+ //! CR anchors:
14
+ //! - **C-1 pipe_token**: only lives in `ConPtyBackend.pipe_token`
15
+ //! (Mutex) and the shim's in-memory state; never persisted. Grep
16
+ //! guard test (`conpty_pipe_token_no_persist_guard.rs`) locks this.
17
+ //! - **C-2 kill_server**: forwards to `Op::Shutdown` per-team, not
18
+ //! global. See CR pre-check in the gate report.
19
+ //! - **C-3 stale event**: emitted by the backend when `state.shim_pid`
20
+ //! disagrees with the shim's `hello` response.
21
+ //! - **C-5 pipe_token_mismatch**: `ProtocolError::PipeTokenMismatch`
22
+ //! maps to `TransportError::MuxUnavailable`, NEVER silent retry.
23
+
24
+ pub mod backend;
25
+
26
+ pub use backend::{ConPtyBackend, PipeClientAdapter, PipeClientTrait};
27
+ // Re-export the portable protocol + shim types from the standalone
28
+ // crate so downstream code can `use team_agent::conpty::{Shim,
29
+ // FakePaneRuntime, ...}` without a second dependency.
30
+ pub use conpty_transport::{
31
+ protocol, shim, FakePaneRuntime, LocalShimClient, PaneRuntime, PipeClient, Shim,
32
+ };
@@ -27,6 +27,14 @@ pub struct DaemonArgs {
27
27
  pub once: bool,
28
28
  /// `--tick-interval`(`__main__.py:29`)。`None` → 读 spec `runtime.tick_interval_sec`。
29
29
  pub tick_interval_sec: Option<f64>,
30
+ /// 0.5.x Windows portability Batch 9 F8 (leader msg_2a4cc1fa54c0):
31
+ /// team_key passed via `--team` CLI so the coordinator daemon
32
+ /// doesn't have to derive from state at boot time. That derivation
33
+ /// (Batch 7 F5) got trapped by Batch 8's seed-state pattern
34
+ /// (F8 root cause). Preserving the derivation as a fallback when
35
+ /// `team_key` is None keeps Unix daemons and pre-existing test
36
+ /// harnesses byte-preserving.
37
+ pub team_key: Option<String>,
30
38
  }
31
39
 
32
40
  /// daemon 主循环(`main`,`__main__.py:25-98`)。写 pid/meta(source=boot)、装信号→STOP、孤儿自检、
@@ -36,19 +44,82 @@ pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
36
44
  // CP-1: the daemon's whole tick surface (has_session / capture / inject / list_windows / kill)
37
45
  // runs through this backend. Prefer the persisted runtime endpoint so attached explicit-socket
38
46
  // teams are checked on the same socket as lifecycle worker operations.
47
+ //
48
+ // 0.5.x Phase 1d Batch 3: coordinator boot now routes through
49
+ // `transport_factory::resolve_transport` so `state.transport.kind =
50
+ // conpty` boots the ConPTY backend, not a tmux backend that would
51
+ // fake `has_session()`. Tmux behavior is byte-equivalent (Layer 3
52
+ // legacy `tmux_endpoint` → same `tmux_backend_for_runtime_state_or_workspace`
53
+ // shape inside the factory's `build_tmux`).
39
54
  let state = crate::state::persist::load_runtime_state(args.workspace.as_path()).ok();
40
- let tmux_selection = crate::tmux_backend::tmux_backend_for_runtime_state_or_workspace(
55
+ // 0.5.x Windows portability Batch 9 F8 (leader msg_2a4cc1fa54c0):
56
+ // team_key priority is now `args.team_key` (from `--team` CLI)
57
+ // > `state.active_team_key` fallback. The Batch 7 F5 derivation
58
+ // stays as the fallback so pre-Batch-9 tests + Unix daemons
59
+ // continue byte-preserving. Callers that CAN pass `--team`
60
+ // (Batch 9 quick-start on Windows) do — that avoids the F8
61
+ // seed-state trap where writing active_team_key to state ahead
62
+ // of coord spawn made downstream launch code think "existing
63
+ // runtime, use restart" and skip spec compile.
64
+ let team_key_from_state = args
65
+ .team_key
66
+ .clone()
67
+ .filter(|s| !s.is_empty())
68
+ .or_else(|| {
69
+ state
70
+ .as_ref()
71
+ .and_then(|s| s.get("active_team_key"))
72
+ .and_then(|v| v.as_str())
73
+ .filter(|s| !s.is_empty())
74
+ .map(str::to_string)
75
+ });
76
+ let mut factory_input = crate::transport_factory::TransportFactoryInput::new(
41
77
  args.workspace.as_path(),
42
- state.as_ref(),
43
- );
78
+ crate::transport_factory::TransportPurpose::Coordinator,
79
+ )
80
+ .with_state(state.as_ref());
81
+ if let Some(ref team_key) = team_key_from_state {
82
+ factory_input = factory_input.with_team_key(Some(team_key));
83
+ }
84
+ let resolved = match crate::transport_factory::resolve_transport(factory_input) {
85
+ Ok(resolved) => resolved,
86
+ Err(e) => {
87
+ // Fail-closed honest degradation: coordinator boots with a
88
+ // tmux-workspace backend so it doesn't crash, but records
89
+ // the assembly refusal in the boot metadata source so the
90
+ // event log shows the reason. This preserves the daemon
91
+ // liveness path while making the failure explicit.
92
+ eprintln!(
93
+ "coordinator: transport_factory refused ({e}); falling back to tmux workspace for daemon liveness"
94
+ );
95
+ let sel = crate::tmux_backend::tmux_backend_for_runtime_state_or_workspace(
96
+ args.workspace.as_path(),
97
+ state.as_ref(),
98
+ );
99
+ let metadata = DaemonTmuxEndpointMetadata {
100
+ tmux_endpoint_used: sel.tmux_endpoint_used.clone(),
101
+ tmux_endpoint_source: "factory_refused_fallback",
102
+ };
103
+ let coord = Coordinator::new(
104
+ args.workspace.clone(),
105
+ Box::new(RealProviderRegistry),
106
+ Box::new(sel.backend),
107
+ );
108
+ return run_daemon_with_coordinator_and_boot_tmux(&args, &coord, Some(metadata));
109
+ }
110
+ };
111
+ // Preserve tmux boot metadata byte-equivalent for tmux teams.
112
+ // ConPTY teams get their source string but no tmux endpoint (design
113
+ // §Behavior Equivalence: same `tmux_endpoint_used` for tmux; None
114
+ // + honest source for conpty).
44
115
  let tmux_metadata = DaemonTmuxEndpointMetadata {
45
- tmux_endpoint_used: tmux_selection.tmux_endpoint_used.clone(),
46
- tmux_endpoint_source: tmux_selection.tmux_endpoint_source.as_str(),
116
+ tmux_endpoint_used: resolved.tmux_endpoint_used.clone(),
117
+ tmux_endpoint_source: resolved.source,
47
118
  };
48
119
  let coordinator = Coordinator::new(
49
120
  args.workspace.clone(),
50
121
  Box::new(RealProviderRegistry),
51
- Box::new(tmux_selection.backend),
122
+ resolved.backend,
52
123
  );
53
124
  run_daemon_with_coordinator_and_boot_tmux(&args, &coordinator, Some(tmux_metadata))
54
125
  }
@@ -98,6 +169,57 @@ fn run_daemon_with_coordinator_and_boot_tmux(
98
169
  }
99
170
  }
100
171
  event_log.write("coordinator.boot", boot_event)?;
172
+
173
+ // 0.5.x Windows portability Batch 8 F7 (leader msg_590b4dce0f68):
174
+ // coordinator takes ownership of shim lifecycle. Idempotent:
175
+ // spawns a fresh shim if none is recorded, reconnects if
176
+ // `state.transport.shim.pid` names a living shim (post-crash
177
+ // restart path). Failures are non-fatal for the coord daemon —
178
+ // the tick loop's transport calls will surface honest
179
+ // MuxUnavailable errors and `mark_transport_unavailable`
180
+ // emits the C-3 stale-family event.
181
+ //
182
+ // On non-Windows this call is cfg'd out (there's no shim
183
+ // concept on Unix).
184
+ #[cfg(windows)]
185
+ {
186
+ let state_for_team_key =
187
+ crate::state::persist::load_runtime_state(args.workspace.as_path()).ok();
188
+ let team_key_opt = state_for_team_key
189
+ .as_ref()
190
+ .and_then(|s| s.get("active_team_key"))
191
+ .and_then(|v| v.as_str())
192
+ .filter(|s| !s.is_empty())
193
+ .map(str::to_string);
194
+ if let Some(team_key) = team_key_opt {
195
+ let workspace_hash =
196
+ crate::tmux_backend::workspace_short_hash_pub(args.workspace.as_path());
197
+ match crate::coordinator::conpty_shim::ensure_shim_running(
198
+ args.workspace.as_path(),
199
+ &team_key,
200
+ &workspace_hash,
201
+ ) {
202
+ Ok(handle) => {
203
+ // Detach so the shim survives beyond this
204
+ // coordinator instance (F7 core invariant).
205
+ let _shim_pid = handle.detach();
206
+ eprintln!("coordinator: shim ensured (pid={_shim_pid})");
207
+ }
208
+ Err(e) => {
209
+ // Honest degradation: emit the stale-family
210
+ // event, coord continues without a shim. Tick
211
+ // loop will surface `mux_unavailable` on any
212
+ // transport call.
213
+ eprintln!("coordinator: shim ensure failed: {e}");
214
+ let _ = crate::coordinator::conpty_shim::mark_transport_unavailable(
215
+ args.workspace.as_path(),
216
+ &format!("boot_ensure_failed: {e}"),
217
+ );
218
+ }
219
+ }
220
+ }
221
+ }
222
+
101
223
  let tick_interval = match args.tick_interval_sec {
102
224
  Some(v) if v > 0.0 => v,
103
225
  _ => resolve_tick_interval(&args.workspace)?,
@@ -215,8 +337,11 @@ fn panic_payload_message(payload: &(dyn std::any::Any + Send)) -> String {
215
337
  }
216
338
 
217
339
  /// 当前 ppid(`os.getppid()`,孤儿自检输入)。
340
+ ///
341
+ /// 0.5.x Windows portability Batch 3: uses `platform::process::current_parent_pid`
342
+ /// so Windows sees a real Toolhelp32-derived ppid instead of `0`.
218
343
  fn current_ppid() -> u32 {
219
- u32::try_from(unsafe { libc::getppid() }).unwrap_or(0)
344
+ crate::platform::process::current_parent_pid().unwrap_or(0)
220
345
  }
221
346
 
222
347
  /// 计算 tick 间隔(`_tick_interval`,`__main__.py:104-115`)。读 spec `runtime.tick_interval_sec`,