@team-agent/installer 0.5.61 → 0.5.63

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 (250) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +5 -2
  4. package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +1 -0
  6. package/crates/team-agent/src/cli/emit.rs +41 -7
  7. package/crates/team-agent/src/cli/helpers.rs +1 -0
  8. package/crates/team-agent/src/cli/leader.rs +6 -1
  9. package/crates/team-agent/src/cli/leaders.rs +2 -0
  10. package/crates/team-agent/src/cli/mod.rs +33 -3
  11. package/crates/team-agent/src/cli/named_address.rs +8 -1
  12. package/crates/team-agent/src/cli/profile.rs +2 -1
  13. package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
  14. package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
  15. package/crates/team-agent/src/cli/send/persist.rs +1 -0
  16. package/crates/team-agent/src/cli/send/presentation.rs +1 -0
  17. package/crates/team-agent/src/cli/send/resolve.rs +206 -1
  18. package/crates/team-agent/src/cli/send.rs +2 -1
  19. package/crates/team-agent/src/cli/spec.rs +1 -0
  20. package/crates/team-agent/src/cli/status.rs +2 -1
  21. package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
  22. package/crates/team-agent/src/cli/status_port/format.rs +1 -0
  23. package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
  24. package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
  25. package/crates/team-agent/src/cli/types.rs +3 -2
  26. package/crates/team-agent/src/communication_mode/mod.rs +1 -0
  27. package/crates/team-agent/src/compiler.rs +1 -0
  28. package/crates/team-agent/src/conpty/backend.rs +1 -0
  29. package/crates/team-agent/src/conpty/mod.rs +1 -0
  30. package/crates/team-agent/src/coordinator/backoff.rs +5 -8
  31. package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
  32. package/crates/team-agent/src/coordinator/health.rs +1 -0
  33. package/crates/team-agent/src/coordinator/mod.rs +2 -2
  34. package/crates/team-agent/src/coordinator/orphan.rs +4 -116
  35. package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
  36. package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
  37. package/crates/team-agent/src/coordinator/steps/abnormal.rs +15 -866
  38. package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
  39. package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
  40. package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
  41. package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
  42. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
  43. package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
  44. package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
  45. package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +139 -375
  46. package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
  47. package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
  48. package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -49
  49. package/crates/team-agent/src/coordinator/tick.rs +23 -40
  50. package/crates/team-agent/src/coordinator/types.rs +3 -176
  51. package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
  52. package/crates/team-agent/src/db/message_store.rs +2 -1
  53. package/crates/team-agent/src/db/migration.rs +5 -4
  54. package/crates/team-agent/src/db/mod.rs +1 -0
  55. package/crates/team-agent/src/db/schema.rs +1 -0
  56. package/crates/team-agent/src/diagnose/comms.rs +2 -1
  57. package/crates/team-agent/src/diagnose/mod.rs +1 -0
  58. package/crates/team-agent/src/diagnose/orphans.rs +1 -0
  59. package/crates/team-agent/src/layout/manager.rs +3 -7
  60. package/crates/team-agent/src/layout/mod.rs +2 -2
  61. package/crates/team-agent/src/layout/overlay.rs +6 -1
  62. package/crates/team-agent/src/layout/placement.rs +1 -0
  63. package/crates/team-agent/src/layout/recovery.rs +3 -0
  64. package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
  65. package/crates/team-agent/src/layout/sessions.rs +8 -1
  66. package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
  67. package/crates/team-agent/src/layout/worker_env.rs +3 -0
  68. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  69. package/crates/team-agent/src/leader/helpers.rs +1 -0
  70. package/crates/team-agent/src/leader/incident.rs +1 -0
  71. package/crates/team-agent/src/leader/lease.rs +27 -22
  72. package/crates/team-agent/src/leader/mod.rs +2 -3
  73. package/crates/team-agent/src/leader/owner_bind.rs +13 -3
  74. package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
  75. package/crates/team-agent/src/leader/rediscover.rs +4 -17
  76. package/crates/team-agent/src/leader/registry.rs +3 -15
  77. package/crates/team-agent/src/leader/start.rs +373 -125
  78. package/crates/team-agent/src/leader/takeover.rs +10 -232
  79. package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
  80. package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
  81. package/crates/team-agent/src/leader/tests/identity.rs +7 -2
  82. package/crates/team-agent/src/leader/tests/idle.rs +0 -193
  83. package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
  84. package/crates/team-agent/src/leader/tests/mod.rs +4 -41
  85. package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
  86. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
  87. package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
  88. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
  89. package/crates/team-agent/src/leader/types.rs +1 -28
  90. package/crates/team-agent/src/lib.rs +3 -0
  91. package/crates/team-agent/src/lifecycle/display.rs +7 -1
  92. package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
  93. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
  94. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
  95. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
  96. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
  97. package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
  98. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
  99. package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
  100. package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -27
  101. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
  102. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
  103. package/crates/team-agent/src/lifecycle/launch.rs +11 -12
  104. package/crates/team-agent/src/lifecycle/mod.rs +2 -1
  105. package/crates/team-agent/src/lifecycle/restart/agent.rs +3 -66
  106. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
  107. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
  108. package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
  109. package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
  110. package/crates/team-agent/src/lifecycle/restart.rs +13 -15
  111. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
  112. package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
  113. package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
  114. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
  115. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
  116. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
  117. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
  118. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
  119. package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
  120. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
  121. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
  122. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
  123. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
  124. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
  125. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
  126. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
  127. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
  128. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
  129. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
  130. package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
  131. package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
  132. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
  133. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
  134. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
  135. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
  136. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
  137. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
  138. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
  139. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
  140. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
  141. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
  142. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
  143. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
  144. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
  145. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
  146. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
  147. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
  148. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
  149. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
  150. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
  151. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
  152. package/crates/team-agent/src/lifecycle/tests.rs +59 -0
  153. package/crates/team-agent/src/lifecycle/types.rs +0 -9
  154. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  155. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
  156. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
  157. package/crates/team-agent/src/mcp_server/mod.rs +1 -0
  158. package/crates/team-agent/src/mcp_server/normalize.rs +4 -3
  159. package/crates/team-agent/src/mcp_server/tools.rs +1 -0
  160. package/crates/team-agent/src/mcp_server/types.rs +1 -18
  161. package/crates/team-agent/src/mcp_server/wire.rs +5 -5
  162. package/crates/team-agent/src/messaging/activity.rs +1 -0
  163. package/crates/team-agent/src/messaging/address.rs +1 -0
  164. package/crates/team-agent/src/messaging/delivery.rs +4 -3
  165. package/crates/team-agent/src/messaging/helpers.rs +2 -1
  166. package/crates/team-agent/src/messaging/leader_channel.rs +1 -0
  167. package/crates/team-agent/src/messaging/leader_receiver.rs +2 -1
  168. package/crates/team-agent/src/messaging/mod.rs +10 -14
  169. package/crates/team-agent/src/messaging/peers.rs +2 -0
  170. package/crates/team-agent/src/messaging/persist.rs +3 -1
  171. package/crates/team-agent/src/messaging/presentation.rs +2 -1
  172. package/crates/team-agent/src/messaging/results.rs +1 -0
  173. package/crates/team-agent/src/messaging/scheduler.rs +1 -0
  174. package/crates/team-agent/src/messaging/selftest.rs +1 -1
  175. package/crates/team-agent/src/messaging/send.rs +41 -8
  176. package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
  177. package/crates/team-agent/src/messaging/types.rs +2 -2
  178. package/crates/team-agent/src/messaging/watchers.rs +19 -13
  179. package/crates/team-agent/src/model/enums.rs +1 -0
  180. package/crates/team-agent/src/model/errors.rs +1 -0
  181. package/crates/team-agent/src/model/ids.rs +1 -0
  182. package/crates/team-agent/src/model/mod.rs +1 -0
  183. package/crates/team-agent/src/model/name_similarity.rs +1 -0
  184. package/crates/team-agent/src/model/pane_authority_refusal.rs +1 -0
  185. package/crates/team-agent/src/model/paths.rs +1 -0
  186. package/crates/team-agent/src/model/permissions.rs +3 -2
  187. package/crates/team-agent/src/model/routing.rs +1 -0
  188. package/crates/team-agent/src/model/spec.rs +1 -0
  189. package/crates/team-agent/src/model/task_graph.rs +1 -0
  190. package/crates/team-agent/src/model/yaml/tests.rs +1 -0
  191. package/crates/team-agent/src/model/yaml.rs +1 -0
  192. package/crates/team-agent/src/packaging/install.rs +9 -147
  193. package/crates/team-agent/src/packaging/migrate.rs +2 -0
  194. package/crates/team-agent/src/packaging/mod.rs +1 -0
  195. package/crates/team-agent/src/packaging/repair.rs +2 -0
  196. package/crates/team-agent/src/packaging/tests.rs +29 -218
  197. package/crates/team-agent/src/packaging/types.rs +8 -15
  198. package/crates/team-agent/src/platform/argv.rs +4 -0
  199. package/crates/team-agent/src/platform/errors.rs +10 -1
  200. package/crates/team-agent/src/platform/file_lock.rs +4 -157
  201. package/crates/team-agent/src/platform/mod.rs +0 -61
  202. package/crates/team-agent/src/platform/process.rs +1 -0
  203. package/crates/team-agent/src/provider/adapter.rs +1 -0
  204. package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
  205. package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
  206. package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
  207. package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
  208. package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
  209. package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
  210. package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
  211. package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
  212. package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
  213. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
  214. package/crates/team-agent/src/provider/classify.rs +1 -0
  215. package/crates/team-agent/src/provider/faults.rs +1 -26
  216. package/crates/team-agent/src/provider/helpers.rs +1 -0
  217. package/crates/team-agent/src/provider/mod.rs +1 -1
  218. package/crates/team-agent/src/provider/session/capture.rs +1 -4
  219. package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
  220. package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
  221. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
  222. package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
  223. package/crates/team-agent/src/provider/session/mod.rs +2 -4
  224. package/crates/team-agent/src/provider/session/resume.rs +2 -209
  225. package/crates/team-agent/src/provider/session_scan/claude.rs +1 -0
  226. package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
  227. package/crates/team-agent/src/provider/session_scan/common.rs +1 -0
  228. package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
  229. package/crates/team-agent/src/provider/session_scan.rs +1 -42
  230. package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
  231. package/crates/team-agent/src/provider/types.rs +1 -0
  232. package/crates/team-agent/src/provider/wire.rs +1 -0
  233. package/crates/team-agent/src/state/identity.rs +16 -5
  234. package/crates/team-agent/src/state/identity_keys.rs +1 -0
  235. package/crates/team-agent/src/state/mod.rs +2 -0
  236. package/crates/team-agent/src/state/owner_gate.rs +5 -0
  237. package/crates/team-agent/src/state/ownership.rs +10 -4
  238. package/crates/team-agent/src/state/paths.rs +12 -0
  239. package/crates/team-agent/src/state/persist.rs +9 -3
  240. package/crates/team-agent/src/state/projection.rs +15 -2
  241. package/crates/team-agent/src/state/repository/intent.rs +1 -0
  242. package/crates/team-agent/src/state/repository/tests.rs +1 -0
  243. package/crates/team-agent/src/state/repository.rs +7 -13
  244. package/crates/team-agent/src/state/selector.rs +1 -0
  245. package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
  246. package/crates/team-agent/src/tmux_backend.rs +63 -0
  247. package/crates/team-agent/src/transport.rs +15 -0
  248. package/package.json +4 -4
  249. package/crates/team-agent/src/leader/inject.rs +0 -33
  250. package/crates/team-agent/src/provider/command.rs +0 -80
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! state.json 持久化(bug-084 韧性;真相源 `state.py:save_runtime_state` + `_self_heal_runtime_state`
2
3
  //! + `runtime.py:_runtime_lock`)。
3
4
  //!
@@ -71,6 +72,7 @@ static RUNTIME_STATE_CACHE: LazyLock<Mutex<HashMap<PathBuf, Value>>> =
71
72
  LazyLock::new(|| Mutex::new(HashMap::new()));
72
73
  static TMP_SEQ: AtomicU64 = AtomicU64::new(0);
73
74
 
75
+ ///
74
76
  /// `state.py:41`。
75
77
  pub fn runtime_state_path(workspace: &Path) -> PathBuf {
76
78
  runtime_dir(workspace).join("state.json")
@@ -202,6 +204,7 @@ impl Drop for RuntimeLock {
202
204
  }
203
205
  }
204
206
 
207
+ ///
205
208
  /// `save_runtime_state`(bug-084)。`state` 是 state.json 的内存 Value(插入序保留)。
206
209
  /// 注:Python 在此还调 `_migrate_state_identity`(identity slice 落地后接入;本 slice 不改 state 内容)。
207
210
  pub fn save_runtime_state(workspace: &Path, state: &Value) -> Result<(), StateError> {
@@ -1135,10 +1138,11 @@ fn self_heal(
1135
1138
  }
1136
1139
  }
1137
1140
 
1141
+ ///
1138
1142
  /// `load_runtime_state`(本 slice 最小:读+parse+缓存;normalize/migration 待 identity slice)。
1139
1143
  /// `normalize_agent_session_state`(`state.py:45`):为每个 agent dict 的 SESSION_STATE_FIELDS
1140
1144
  /// setdefault None(缺则末尾插)。
1141
- pub fn normalize_agent_session_state(state: &mut Value) {
1145
+ fn normalize_agent_session_state(state: &mut Value) {
1142
1146
  let Some(agents) = state.get_mut("agents").and_then(Value::as_object_mut) else {
1143
1147
  return;
1144
1148
  };
@@ -1151,12 +1155,13 @@ pub fn normalize_agent_session_state(state: &mut Value) {
1151
1155
  }
1152
1156
  }
1153
1157
 
1158
+ ///
1154
1159
  /// `_migrate_active_team_key`(`state.py:73`,0.2.6 Family B C6):legacy state 缺 active_team_key
1155
1160
  /// 时 seed 一次。返回是否有改动。
1156
1161
  ///
1157
1162
  /// 注:Python `seed if seed in teams or not teams else seed` 两支均为 `seed`(死三元),
1158
1163
  /// 此处直接 `= seed`,与可观测行为一致。
1159
- pub fn migrate_active_team_key(state: &mut Value) -> bool {
1164
+ fn migrate_active_team_key(state: &mut Value) -> bool {
1160
1165
  if state
1161
1166
  .as_object()
1162
1167
  .is_some_and(|o| o.contains_key("active_team_key"))
@@ -1191,6 +1196,7 @@ pub fn migrate_active_team_key(state: &mut Value) -> bool {
1191
1196
  true
1192
1197
  }
1193
1198
 
1199
+ ///
1194
1200
  /// RM-039-STAT-001 second-round compat normalizer (architect verdict
1195
1201
  /// 2026-06-22). When `active_team_key` names an existing `teams` entry
1196
1202
  /// but root `team_key` is missing, set root `team_key = active_team_key`
@@ -1210,7 +1216,7 @@ pub fn migrate_active_team_key(state: &mut Value) -> bool {
1210
1216
  /// * Does NOT touch any other field (no generic deep merge).
1211
1217
  ///
1212
1218
  /// Returns `true` if state was mutated and the caller should persist.
1213
- pub fn migrate_team_key_to_match_active_team(state: &mut Value) -> bool {
1219
+ fn migrate_team_key_to_match_active_team(state: &mut Value) -> bool {
1214
1220
  let Some(obj) = state.as_object() else {
1215
1221
  return false;
1216
1222
  };
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! projection:team 选择 / top-level 派生视图(真相源 `state.py`,0.2.6 Family B C6-C8)。
2
3
  //!
3
4
  //! `state.teams` 是唯一候选源(`status == "alive"`);顶层 `session_name`/`team_dir`/`agents`/`tasks`
@@ -24,6 +25,7 @@ use crate::state::persist::{
24
25
 
25
26
  pub(crate) const CURRENT_TEAM_ALIAS: &str = "current";
26
27
 
28
+ ///
27
29
  /// `team_state_key`(`state.py:93`):从 team_dir(.name)/spec_path(.parent.name)派生 team key,
28
30
  /// 跳过 `.team`/`runtime`;兜底 `session_name` 或 `"current"`。
29
31
  pub fn team_state_key(state: &Value) -> String {
@@ -184,6 +186,7 @@ fn legacy_owner_team_aliases(entry: &Value) -> impl Iterator<Item = String> + '_
184
186
  .map(str::to_string)
185
187
  }
186
188
 
189
+ ///
187
190
  /// `compact_team_state`(`state.py:105`):剔除 `teams`(team entry 不嵌套全量 teams),保序。
188
191
  pub fn compact_team_state(state: &Value) -> Value {
189
192
  match state.as_object() {
@@ -208,6 +211,7 @@ pub fn state_is_managed_leader(state: &Value) -> bool {
208
211
  !state_is_external_leader(state)
209
212
  }
210
213
 
214
+ ///
211
215
  /// `merge_workspace_team_state`(`state.py:111`):把新启动的 team 并入既有 workspace state。
212
216
  pub fn merge_workspace_team_state(existing: &Value, launched: &Value) -> Value {
213
217
  let launched_key = team_state_key(launched);
@@ -239,6 +243,7 @@ pub fn merge_workspace_team_state(existing: &Value, launched: &Value) -> Value {
239
243
  Value::Object(merged)
240
244
  }
241
245
 
246
+ ///
242
247
  /// `team_state_candidates`(`state.py:131`):唯一候选源 = `state.teams` 中
243
248
  /// `status=="alive"`(大小写不敏感;缺 status/空 视为 alive,但 0.5.26 起
244
249
  /// legacy shutdown 残留「无 status + 全体 agents 处于终态」不再算 alive)。
@@ -263,6 +268,7 @@ pub fn team_state_candidates(state: &Value) -> Map<String, Value> {
263
268
  out
264
269
  }
265
270
 
271
+ ///
266
272
  /// 0.5.26 (`.team/artifacts/stale-team-saveconflict-locate.md` §7.1): 唯一 alive
267
273
  /// 谓词。规则:
268
274
  /// - 非 object → 排除;
@@ -348,8 +354,9 @@ pub(crate) fn agent_status_is_terminal(status: &str) -> bool {
348
354
  )
349
355
  }
350
356
 
357
+ ///
351
358
  /// `format_team_candidates`(`state.py:148`):候选摘要串(key 排序;agents 排序逗号连,空→`-`)。
352
- pub fn format_team_candidates(team_states: &Map<String, Value>) -> String {
359
+ fn format_team_candidates(team_states: &Map<String, Value>) -> String {
353
360
  if team_states.is_empty() {
354
361
  return "No team state was found.".to_string();
355
362
  }
@@ -412,6 +419,7 @@ fn valid_owner_pane_id(pane_id: &str) -> bool {
412
419
  pane_id.starts_with('%') || pane_id.chars().all(|ch| ch.is_ascii_digit())
413
420
  }
414
421
 
422
+ ///
415
423
  /// `_project_top_level_view`(`state.py:167`,C8):把 `teams[team_key]` 投影成扁平顶层视图。
416
424
  pub fn project_top_level_view(state: &Value, team_key: &str) -> Value {
417
425
  // entry = _team_entry_from_state(...) or {} —— 空 dict 亦走 {} 分支(同内容)。
@@ -488,6 +496,7 @@ pub struct TeamScopeResolution {
488
496
  pub state: Value,
489
497
  }
490
498
 
499
+ ///
491
500
  /// Resolve a requested team selector once, returning both the canonical map key and its
492
501
  /// projected state. Callers must carry `canonical_team_key` forward rather than deriving team
493
502
  /// identity again from the projection or the original alias.
@@ -646,6 +655,7 @@ pub fn resolve_runtime_team_scope(
646
655
  ))
647
656
  }
648
657
 
658
+ ///
649
659
  /// `select_runtime_state`(`state.py:193`):compatibility projection wrapper.
650
660
  pub fn select_runtime_state(workspace: &Path, team: Option<&str>) -> Result<Value, StateError> {
651
661
  resolve_runtime_team_scope(workspace, team).map(|resolved| resolved.state)
@@ -677,8 +687,9 @@ fn team_selector_matches(team: &str, key: &str, value: &Value) -> bool {
677
687
  .is_some_and(|name| team == name)
678
688
  }
679
689
 
690
+ ///
680
691
  /// `ambiguous_team_target_result`(`state.py:226`):无显式 team 且多候选 → 拒绝 dict;否则 None。
681
- pub fn ambiguous_team_target_result(state: &Value) -> Option<Value> {
692
+ fn ambiguous_team_target_result(state: &Value) -> Option<Value> {
682
693
  let alive = team_state_candidates(state);
683
694
  let active = state
684
695
  .get("active_team_key")
@@ -704,6 +715,7 @@ pub fn ambiguous_team_target_result(state: &Value) -> Option<Value> {
704
715
  }))
705
716
  }
706
717
 
718
+ ///
707
719
  /// `resolve_team_scoped_state`(`state.py:243`):返回 `(state, refusal)`,二者恰一为 Some。
708
720
  /// `team=None` 且歧义 → refusal;否则 select,RuntimeError → `team_target_unresolved` refusal。
709
721
  pub fn resolve_team_scoped_state(
@@ -732,6 +744,7 @@ pub fn resolve_team_scoped_state(
732
744
  }
733
745
  }
734
746
 
747
+ ///
735
748
  /// `save_team_scoped_state`(`state.py:594`):把 team-scoped(投影后)state 写回 workspace,**保全**
736
749
  /// 多 team workspace 里其他 team 的持久态。单 team(磁盘无 `teams` 且 primary key == target)退化为
737
750
  /// 纯 `save_runtime_state`(字节等价);多 team 时把本 team 落到 `teams[target_key]=compact(...)`,顶层
@@ -1,3 +1,4 @@
1
+
1
2
  use super::StateWriteIntent;
2
3
 
3
4
  impl<'a> StateWriteIntent<'a> {
@@ -1,3 +1,4 @@
1
+
1
2
  use super::{reapply_scope, ReapplyScope, StateWriteIntent};
2
3
 
3
4
  #[test]
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! StateRepository facade.
2
3
  //!
3
4
  //! Ref:
@@ -73,16 +74,19 @@ pub struct StateRepository<'a> {
73
74
  }
74
75
 
75
76
  impl<'a> StateRepository<'a> {
77
+ ///
76
78
  /// Construct a repository bound to a workspace root.
77
79
  pub fn new(workspace: &'a Path) -> Self {
78
80
  Self { workspace }
79
81
  }
80
82
 
83
+ ///
81
84
  /// Load the raw workspace state document.
82
85
  pub fn load_workspace(&self) -> Result<Value, StateError> {
83
86
  helper_load_workspace(self.workspace)
84
87
  }
85
88
 
89
+ ///
86
90
  /// Load the canonical workspace document without running read-time
87
91
  /// migrations. `None` preserves the legacy raw-reader distinction between
88
92
  /// a missing file and a present empty/default document.
@@ -96,6 +100,7 @@ impl<'a> StateRepository<'a> {
96
100
  serde_json::from_str(&text).map(Some).map_err(StateError::from)
97
101
  }
98
102
 
103
+ ///
99
104
  /// Resolve a team-scoped projection using the existing projection selector.
100
105
  /// `team_key = None` selects the ambient team the same way as pre-S1a.
101
106
  pub fn load_team(&self, team_key: Option<&str>) -> Result<Value, StateError> {
@@ -110,6 +115,7 @@ impl<'a> StateRepository<'a> {
110
115
  })
111
116
  }
112
117
 
118
+ ///
113
119
  /// Persist `state` under the supplied intent. S1a dispatch is a pure
114
120
  /// forward call to the same legacy helper family that the caller used
115
121
  /// before S1a; the intent selects the family, not the merge semantics.
@@ -117,6 +123,7 @@ impl<'a> StateRepository<'a> {
117
123
  route_direct(self.workspace, intent, state)
118
124
  }
119
125
 
126
+ ///
120
127
  /// Persist `state` and let `reapply` re-materialize the write on
121
128
  /// SaveConflict, matching the pre-S1a `_reapplying_after_conflict` helper
122
129
  /// behavior for reapply-shaped intents.
@@ -212,14 +219,6 @@ pub enum StateWriteIntent<'a> {
212
219
  CoordinatorConptyShim {
213
220
  team_key: Option<&'a str>,
214
221
  },
215
- /// 0.5.36 (`.team/artifacts/supermarket-api-error-recovery-locate.md` §7.3):
216
- /// post-save recovery step writes back the recovery intent outcome
217
- /// (attempts / status / last_error / blocked_reason). Distinct intent
218
- /// so future S1b migration can route it deliberately.
219
- CoordinatorApiErrorRecovery {
220
- team_key: Option<&'a str>,
221
- agent_id: Option<&'a str>,
222
- },
223
222
  McpAssignTask {
224
223
  team_key: Option<&'a str>,
225
224
  task_id: &'a str,
@@ -399,11 +398,6 @@ fn route_direct(
399
398
  // CoordinatorConptyShim -> root save
400
399
  // (coordinator/conpty_shim.rs:426/674).
401
400
  StateWriteIntent::CoordinatorConptyShim { .. } => helper_write_root(workspace, state),
402
- // 0.5.36 CoordinatorApiErrorRecovery -> root save. The recovery
403
- // intent lives under `coordinator.abnormal_api_error_recovery`, a
404
- // root-scoped bookkeeping namespace that must survive across team
405
- // boundaries; use the same helper family as CoordinatorConptyShim.
406
- StateWriteIntent::CoordinatorApiErrorRecovery { .. } => helper_write_root(workspace, state),
407
401
  // McpAssignTask uses the reapply variant today; direct save routes
408
402
  // to the root helper for parity.
409
403
  StateWriteIntent::McpAssignTask { .. } => helper_write_root(workspace, state),
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Active team workspace selector.
2
3
 
3
4
  use std::path::{Path, PathBuf};
@@ -146,6 +146,45 @@ fn backend_with_stdin(
146
146
  )
147
147
  }
148
148
 
149
+ #[test]
150
+ fn a21_spawn_command_too_long_is_structured_before_transport() {
151
+ let (backend, recorded) = backend_with(MockResp::Out(ok("%0\n")), Vec::new());
152
+ let session = SessionName::new("a21-session");
153
+ let window = WindowName::new("worker-long-role");
154
+ let argv = vec!["codex".to_string(), "x".repeat(20_000)];
155
+
156
+ let error = backend
157
+ .spawn_first(
158
+ &session,
159
+ &window,
160
+ &argv,
161
+ Path::new("/tmp/a21-workspace"),
162
+ &BTreeMap::new(),
163
+ )
164
+ .expect_err("oversized spawn must fail before tmux transport");
165
+ let text = error.to_string();
166
+ assert!(
167
+ text.contains("command_too_long"),
168
+ "structured error: {text}"
169
+ );
170
+ assert!(
171
+ text.contains("worker-long-role"),
172
+ "seat must be named: {text}"
173
+ );
174
+ assert!(
175
+ text.contains("actual_bytes="),
176
+ "actual length must be visible: {text}"
177
+ );
178
+ assert!(
179
+ text.contains("limit_bytes="),
180
+ "limit must be visible: {text}"
181
+ );
182
+ assert!(
183
+ recorded.lock().unwrap().is_empty(),
184
+ "tmux must not be called: {text}"
185
+ );
186
+ }
187
+
149
188
  fn svec(items: &[&str]) -> Vec<String> {
150
189
  items.iter().map(|s| (*s).to_string()).collect()
151
190
  }
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Concrete tmux `Transport` backend (SKELETON) — the real executor that runs `tmux <argv>`.
2
3
  //!
3
4
  //! step 9 shipped the [`crate::transport::Transport`] trait + the pure tmux argv-builders
@@ -77,6 +78,11 @@ const COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
77
78
  const SPAWN_IDENTITY_TIMEOUT: Duration = Duration::from_millis(500);
78
79
  const SPAWN_IDENTITY_POLL_INTERVAL: Duration = Duration::from_millis(25);
79
80
 
81
+ /// Keep the final tmux argv below the ~16 KiB command envelope observed on macOS.
82
+ /// This is checked after shell quoting and socket arguments are applied, before
83
+ /// the external tmux process is called.
84
+ pub const TMUX_SPAWN_COMMAND_LIMIT_BYTES: usize = 16_000;
85
+
80
86
  impl CommandRunner for RealCommandRunner {
81
87
  fn run(&self, argv: &[String]) -> Result<CommandOutput, std::io::Error> {
82
88
  self.run_inner(argv, None)
@@ -390,6 +396,27 @@ impl TmuxBackend {
390
396
  if self.socket.is_none() {
391
397
  return;
392
398
  }
399
+ // A server-level kill is only safe after the caller has removed every
400
+ // resource it owns. A non-empty server may carry a pre-existing team
401
+ // (or a leader owned by another launcher), so fail closed instead of
402
+ // turning an endpoint cleanup into a shared-server teardown. An
403
+ // inconclusive probe is also not proof of ownership.
404
+ let server_is_empty = match self.list_targets() {
405
+ Ok(targets) => targets.is_empty(),
406
+ Err(_) => false,
407
+ };
408
+ if !server_is_empty {
409
+ if let Some(workspace) = &self.event_workspace {
410
+ let _ = crate::event_log::EventLog::new(workspace).write(
411
+ "tmux.kill_server_skipped_nonempty_or_unknown",
412
+ serde_json::json!({
413
+ "reason": "server_ownership_not_proven",
414
+ "endpoint": self.tmux_endpoint(),
415
+ }),
416
+ );
417
+ }
418
+ return;
419
+ }
393
420
  let argv = self.tmux_argv(&["tmux".to_string(), "kill-server".to_string()]);
394
421
  let _ = self.runner.run(&argv);
395
422
  }
@@ -787,6 +814,16 @@ impl TmuxBackend {
787
814
  first: bool,
788
815
  ) -> Result<SpawnResult, TransportError> {
789
816
  let spawn_argv = tmux_spawn_argv(session, window, command, first);
817
+ self.validate_spawn_command(
818
+ &spawn_argv,
819
+ if first {
820
+ "tmux.new-session"
821
+ } else {
822
+ "tmux.new-window"
823
+ },
824
+ session,
825
+ window,
826
+ )?;
790
827
  let output = self.run_spawn(&spawn_argv)?;
791
828
  let pane = output.stdout.trim();
792
829
  if pane.is_empty() {
@@ -897,6 +934,7 @@ impl TmuxBackend {
897
934
  "-lc".to_string(),
898
935
  command,
899
936
  ];
937
+ self.validate_spawn_command(&split_argv, "tmux.split-window", session, window)?;
900
938
  let output = self.run_spawn(&split_argv)?;
901
939
  let pane = output.stdout.trim();
902
940
  if pane.is_empty() {
@@ -932,6 +970,31 @@ impl TmuxBackend {
932
970
  }
933
971
  }
934
972
 
973
+ fn validate_spawn_command(
974
+ &self,
975
+ argv: &[String],
976
+ segment: &str,
977
+ session: &SessionName,
978
+ window: &WindowName,
979
+ ) -> Result<(), TransportError> {
980
+ let final_argv = self.tmux_argv(argv);
981
+ let actual_bytes = final_argv
982
+ .iter()
983
+ .map(|arg| arg.len().saturating_add(1))
984
+ .sum::<usize>();
985
+ if actual_bytes > TMUX_SPAWN_COMMAND_LIMIT_BYTES {
986
+ return Err(TransportError::CommandTooLong {
987
+ backend: BackendKind::Tmux,
988
+ segment: segment.to_string(),
989
+ session: session.as_str().to_string(),
990
+ window: window.as_str().to_string(),
991
+ actual_bytes,
992
+ limit_bytes: TMUX_SPAWN_COMMAND_LIMIT_BYTES,
993
+ });
994
+ }
995
+ Ok(())
996
+ }
997
+
935
998
  fn run_spawn(&self, argv: &[String]) -> Result<CommandOutput, TransportError> {
936
999
  let argv = self.tmux_argv(argv);
937
1000
  let output = self
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 9 · transport — 控制面 trait + typed payload(ROUND-0 SKELETON).
2
3
  //!
3
4
  //! 设计真相源:`docs/phase0/transport-backend-design.md` §1(Rust 草图:Target /
@@ -486,6 +487,20 @@ pub enum TransportError {
486
487
  code: Option<i32>,
487
488
  stderr: String,
488
489
  },
490
+ /// The final tmux spawn argv is too large for the transport command envelope.
491
+ /// Keep this separate from `Subprocess` so callers do not have to parse a
492
+ /// provider-sized argv dump to learn the actionable limit.
493
+ #[error(
494
+ "command_too_long: backend={backend:?}; segment={segment}; session={session}; window={window}; actual_bytes={actual_bytes}; limit_bytes={limit_bytes}"
495
+ )]
496
+ CommandTooLong {
497
+ backend: BackendKind,
498
+ segment: String,
499
+ session: String,
500
+ window: String,
501
+ actual_bytes: usize,
502
+ limit_bytes: usize,
503
+ },
489
504
  /// wezterm mux 连不上 / tmux server 不在。
490
505
  #[error("mux unavailable on {backend:?}: {detail}")]
491
506
  MuxUnavailable {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.5.61",
3
+ "version": "0.5.63",
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.5.61",
24
- "@team-agent/cli-darwin-x64": "0.5.61",
25
- "@team-agent/cli-linux-x64": "0.5.61"
23
+ "@team-agent/cli-darwin-arm64": "0.5.63",
24
+ "@team-agent/cli-darwin-x64": "0.5.63",
25
+ "@team-agent/cli-linux-x64": "0.5.63"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",
@@ -1,33 +0,0 @@
1
- //! leader::inject — `push_idle_reminder` no-op shim.
2
- //!
3
- //! #236 nag_removal (N35): the framework no longer auto-pings the leader pane based on
4
- //! idle-classification inferences. `push_idle_reminder` used to emit the
5
- //! `idle_takeover.reminder` event AND route a "leader takeover may be appropriate"
6
- //! message through the N31/N32 funnel; both were nag outputs synthesized from
7
- //! time/state, not from a real delivery obligation. They are deleted by design —
8
- //! ownership / handover changes only via explicit `claim-leader` / `takeover` commands.
9
- //!
10
- //! The function signature is preserved (callers in coordinator/tick.rs and lifecycle
11
- //! tests still resolve) but the body is a strict no-op: no event, no primitive call,
12
- //! no state mutation. The `_` discards keep clippy quiet and document that the
13
- //! arguments are intentionally ignored under N35.
14
-
15
- use std::path::Path;
16
-
17
- use serde_json::Value;
18
-
19
- use super::{LeaderError, TakeoverReminderResult};
20
-
21
- /// #236 nag_removal: was the take-over reminder injector; now a true no-op. Delivery
22
- /// primitives (`report_result` / `send_to_leader` / N31/N32 funnel / `request_human` /
23
- /// broadcast-leader) continue to flow through their own callsites — only the
24
- /// time/state-inferred nag output that lived in this helper is gone.
25
- pub fn push_idle_reminder(
26
- workspace: &Path,
27
- state: &Value,
28
- event_log: &crate::event_log::EventLog,
29
- result: &TakeoverReminderResult,
30
- ) -> Result<(), LeaderError> {
31
- let _ = (workspace, state, event_log, result);
32
- Ok(())
33
- }
@@ -1,80 +0,0 @@
1
- //! unit-7 (Stage 3) — provider command-assembly boundary.
2
- //!
3
- //! The provider/adapter.rs monolith mixes three concerns:
4
- //! 1. The `ProviderAdapter` TRAIT and facade dispatch (the public API).
5
- //! 2. Per-provider COMMAND ASSEMBLY (argv plan, base/resume flags,
6
- //! --session-id injection — adapter.rs:409-508 region).
7
- //! 3. SESSION SCANNING (capture polling + transcript scanning —
8
- //! adapter.rs:511-546, 969-1078).
9
- //!
10
- //! This module CREATES the dedicated home for #2 so future work can
11
- //! migrate command-assembly fns here in small, behavior-neutral commits.
12
- //! Today it only carries the documented intent and a small typed helper —
13
- //! the bulk of the assembly fns are still in adapter.rs and reachable
14
- //! through the existing pub(crate) surface.
15
- //!
16
- //! Why incremental: every command-assembly fn touches multiple internal
17
- //! constants (`--session-id`, `--resume`, MCP-config-path conventions) and
18
- //! per-provider quirks. Moving them blindly is high-risk. The boundary
19
- //! exists so the next commit (or any contributor) has a documented target
20
- //! for relocations.
21
-
22
- use crate::provider::Provider;
23
-
24
- /// Lightweight tag for command-plan branches in the existing adapter
25
- /// (`Base` vs `Resume`). Distinct type so command-assembly code reads
26
- /// clearly even when it lives next to adapter dispatch.
27
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
28
- pub enum CommandPlanKind {
29
- /// Initial spawn — `--session-id <uuid>` predetermined.
30
- Base,
31
- /// Resume an existing session — `--resume <sid>`, no fresh
32
- /// `--session-id` injected.
33
- Resume,
34
- }
35
-
36
- /// Returns true when the provider's command plan honors `--session-id`
37
- /// for fresh spawns. Mirrors the existing adapter logic but lives next to
38
- /// where future command-assembly fns will land.
39
- pub fn honors_session_id_for_base_spawn(provider: Provider) -> bool {
40
- matches!(
41
- provider,
42
- Provider::Claude | Provider::ClaudeCode | Provider::Copilot
43
- )
44
- }
45
-
46
- #[cfg(test)]
47
- mod tests {
48
- use super::*;
49
-
50
- #[test]
51
- fn fake_provider_does_not_honor_session_id() {
52
- assert!(!honors_session_id_for_base_spawn(Provider::Fake));
53
- }
54
-
55
- #[test]
56
- fn provider_matrix_for_base_spawn_session_id_support() {
57
- let cases = [
58
- (Provider::Claude, true),
59
- (Provider::ClaudeCode, true),
60
- (Provider::Copilot, true),
61
- (Provider::Codex, false),
62
- (Provider::GeminiCli, false),
63
- (Provider::Fake, false),
64
- ];
65
- for (provider, expected) in cases {
66
- assert_eq!(
67
- honors_session_id_for_base_spawn(provider),
68
- expected,
69
- "{provider:?}"
70
- );
71
- }
72
- }
73
-
74
- #[test]
75
- fn command_plan_kind_is_copy() {
76
- let a = CommandPlanKind::Base;
77
- let b = a;
78
- assert_eq!(a, b);
79
- }
80
- }