@team-agent/installer 0.5.65 → 0.5.68

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 (208) hide show
  1. package/Cargo.lock +8 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +6 -0
  4. package/crates/team-agent/src/cli/adapters.rs +6 -1
  5. package/crates/team-agent/src/cli/diagnose.rs +82 -0
  6. package/crates/team-agent/src/cli/emit.rs +3 -3
  7. package/crates/team-agent/src/cli/grok_slot.rs +299 -0
  8. package/crates/team-agent/src/cli/leader.rs +21 -7
  9. package/crates/team-agent/src/cli/leaders.rs +125 -1
  10. package/crates/team-agent/src/cli/mod.rs +20 -0
  11. package/crates/team-agent/src/cli/send/presentation.rs +7 -1
  12. package/crates/team-agent/src/cli/spec.rs +2 -0
  13. package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
  14. package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
  15. package/crates/team-agent/src/cli/tests/base.rs +26 -3
  16. package/crates/team-agent/src/cli/tests/compile.rs +1 -1
  17. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
  18. package/crates/team-agent/src/cli/tests/mod.rs +2 -2
  19. package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
  20. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
  21. package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
  22. package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
  23. package/crates/team-agent/src/communication_mode/mod.rs +6 -4
  24. package/crates/team-agent/src/compiler/tests.rs +23 -20
  25. package/crates/team-agent/src/compiler.rs +84 -5
  26. package/crates/team-agent/src/conpty/backend.rs +16 -0
  27. package/crates/team-agent/src/coordinator/backoff.rs +55 -0
  28. package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
  29. package/crates/team-agent/src/coordinator/health.rs +173 -0
  30. package/crates/team-agent/src/coordinator/mod.rs +31 -0
  31. package/crates/team-agent/src/coordinator/orphan.rs +31 -0
  32. package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
  33. package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
  34. package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
  35. package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
  36. package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
  37. package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
  38. package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
  39. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
  40. package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
  41. package/crates/team-agent/src/coordinator/tick.rs +133 -22
  42. package/crates/team-agent/src/coordinator/types.rs +49 -0
  43. package/crates/team-agent/src/db/message_store.rs +39 -5
  44. package/crates/team-agent/src/layout/worker_env.rs +20 -3
  45. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  46. package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
  47. package/crates/team-agent/src/leader/registry.rs +65 -0
  48. package/crates/team-agent/src/leader/start.rs +1080 -58
  49. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
  50. package/crates/team-agent/src/lifecycle/display.rs +56 -0
  51. package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
  52. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
  53. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
  54. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
  55. package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
  56. package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
  57. package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
  58. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
  59. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
  60. package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
  61. package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
  62. package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
  63. package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
  64. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
  65. package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
  66. package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
  67. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
  68. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
  69. package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
  70. package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
  71. package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
  72. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
  73. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
  74. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
  75. package/crates/team-agent/src/lifecycle/launch.rs +209 -43
  76. package/crates/team-agent/src/lifecycle/lock.rs +42 -1
  77. package/crates/team-agent/src/lifecycle/mod.rs +11 -0
  78. package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
  79. package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
  80. package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
  81. package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
  82. package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
  83. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
  84. package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
  85. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
  86. package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
  87. package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
  88. package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
  89. package/crates/team-agent/src/lifecycle/restart.rs +29 -0
  90. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
  91. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
  92. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
  93. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
  94. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
  95. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
  96. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
  97. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
  98. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
  99. package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
  100. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
  101. package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
  102. package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
  103. package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
  104. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
  105. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
  106. package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
  107. package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
  108. package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
  109. package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
  110. package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
  111. package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
  112. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
  113. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
  114. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
  115. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
  116. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
  117. package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
  118. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
  119. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
  120. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
  121. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
  122. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
  123. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
  124. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
  125. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
  126. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
  127. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
  128. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
  129. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
  130. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
  131. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
  132. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
  133. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
  134. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
  135. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
  136. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
  137. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
  138. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
  139. package/crates/team-agent/src/lifecycle/tests.rs +23 -15
  140. package/crates/team-agent/src/lifecycle/types.rs +61 -2
  141. package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
  142. package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
  143. package/crates/team-agent/src/mcp_server/tests.rs +196 -2
  144. package/crates/team-agent/src/messaging/delivery.rs +405 -40
  145. package/crates/team-agent/src/messaging/helpers.rs +2 -0
  146. package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
  147. package/crates/team-agent/src/messaging/results.rs +4 -0
  148. package/crates/team-agent/src/messaging/send.rs +34 -0
  149. package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
  150. package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
  151. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
  152. package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
  153. package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
  154. package/crates/team-agent/src/messaging/types.rs +4 -1
  155. package/crates/team-agent/src/model/enums.rs +14 -5
  156. package/crates/team-agent/src/model/permissions.rs +3 -0
  157. package/crates/team-agent/src/model/spec.rs +129 -6
  158. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
  159. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
  160. package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
  161. package/crates/team-agent/src/os_probe.rs +73 -2
  162. package/crates/team-agent/src/provider/adapter.rs +231 -4
  163. package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
  164. package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
  165. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
  166. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
  167. package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
  168. package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
  169. package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
  170. package/crates/team-agent/src/provider/classify.rs +4 -2
  171. package/crates/team-agent/src/provider/faults.rs +2 -1
  172. package/crates/team-agent/src/provider/mod.rs +13 -0
  173. package/crates/team-agent/src/provider/session/capture.rs +158 -20
  174. package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
  175. package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
  176. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
  177. package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
  178. package/crates/team-agent/src/provider/session/mod.rs +18 -0
  179. package/crates/team-agent/src/provider/session/resume.rs +57 -0
  180. package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
  181. package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
  182. package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
  183. package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
  184. package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
  185. package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
  186. package/crates/team-agent/src/provider/session_scan.rs +8 -0
  187. package/crates/team-agent/src/provider/submit_now.rs +94 -0
  188. package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
  189. package/crates/team-agent/src/provider/types.rs +4 -2
  190. package/crates/team-agent/src/provider/wire.rs +23 -1
  191. package/crates/team-agent/src/state/persist.rs +301 -8
  192. package/crates/team-agent/src/state/repository.rs +5 -0
  193. package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
  194. package/crates/team-agent/src/tmux_backend.rs +1083 -75
  195. package/crates/team-agent/src/transport/test_support.rs +20 -0
  196. package/crates/team-agent/src/transport/tests/wire.rs +28 -2
  197. package/crates/team-agent/src/transport.rs +265 -1
  198. package/npm/install.mjs +129 -73
  199. package/package.json +4 -4
  200. package/skills/team-agent/SKILL.md +33 -238
  201. package/skills/team-agent/command-coverage.json +31 -0
  202. package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
  203. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
  204. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
  205. package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
  206. package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
  207. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
  208. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
@@ -1,3 +1,41 @@
1
+ //! ---
2
+ //! purpose: restart 与 start 共用的底座,含 spawn 执行、transport 解析、state 标记与各类读取判定
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: spawn_agent_window
6
+ //! what: 为一个席位拼命令并在目标 session 里开窗或分格起进程
7
+ //! - name: lifecycle_worker_tmux_backend_for_selected_state
8
+ //! what: 按团队已持久化的 endpoint 解析 tmux 后端,非 tmux 状态直接拒绝
9
+ //! - name: save_restart_projected_state
10
+ //! what: 同步团队投影后经 repository 带写意图落盘
11
+ //! - name: resume_backing_probe_for_agent
12
+ //! what: 探测该席位的 resume 依据是否在盘上,并记下所有查过的路径
13
+ //! - name: converge_missing_provider_sessions
14
+ //! what: 有界等待会话捕获收敛,并逐轮写进度事件
15
+ //! - name: mark_agent_stopped
16
+ //! what: 把席位标记为已停并清掉 window 与 pane_id 字段
17
+ //! depends:
18
+ //! - crate::transport::Transport
19
+ //! - crate::tmux_backend
20
+ //! - crate::transport_factory
21
+ //! - crate::provider
22
+ //! - crate::session_capture
23
+ //! - crate::state::projection
24
+ //! - crate::state::repository
25
+ //! - crate::state::persist
26
+ //! - crate::coordinator::health
27
+ //! - crate::event_log::EventLog
28
+ //! - crate::lifecycle::launch
29
+ //! - crate::lifecycle::profile_launch
30
+ //! - crate::lifecycle::worker_command_context
31
+ //! - crate::lifecycle::restart::selection
32
+ //! boundary:
33
+ //! - 状态是 conpty 时 tmux 专用解析器直接报错,不静默降级到 tmux
34
+ //! - 探测类判定失败一律倒向保守值,不把不确定当成存活
35
+ //! - 清活动观测只删观测字段,不动生命周期与拓扑字段
36
+ //! - 收敛与排空都是有界等待,不无限阻塞
37
+ //! maturity: wired
38
+ //! ---
1
39
  use super::*;
2
40
 
3
41
  pub(super) struct SpawnedAgentWindow {
@@ -23,6 +61,12 @@ pub(super) struct SameRoleCohortTarget {
23
61
  }
24
62
 
25
63
  impl SameRoleCohortTarget {
64
+ /// ---
65
+ /// purpose: 构造一个同角色同批目标
66
+ /// params:
67
+ /// window: 该席位的窗口名
68
+ /// returns: 未带期望 pane 的目标
69
+ /// ---
26
70
  pub(super) fn new(agent_id: &AgentId, window: &str) -> Self {
27
71
  Self {
28
72
  agent_id: agent_id.as_str().to_string(),
@@ -31,16 +75,28 @@ impl SameRoleCohortTarget {
31
75
  }
32
76
  }
33
77
 
78
+ /// ---
79
+ /// purpose: 给目标补上期望的旧 pane
80
+ /// returns: 带期望 pane 的目标
81
+ /// ---
34
82
  pub(super) fn with_expected_pane_id(mut self, pane_id: Option<&str>) -> Self {
35
83
  self.expected_pane_id = pane_id.map(ToString::to_string);
36
84
  self
37
85
  }
38
86
  }
39
87
 
88
+ /// ---
89
+ /// purpose: 判断该窗口是不是按席位命名的独立窗口
90
+ /// returns: 窗口名等于席位名且不是规范布局窗口时为 true
91
+ /// ---
40
92
  pub(super) fn is_per_agent_cohort_window(window: &str, agent_id: &AgentId) -> bool {
41
93
  window == agent_id.as_str() && !crate::lifecycle::launch::is_adaptive_layout_window_pub(window)
42
94
  }
43
95
 
96
+ /// ---
97
+ /// purpose: spawn 之前检查同角色同批是否已有残留
98
+ /// returns: 期望基数为 0;不满足时给出可读的拒绝说明,满足则 None
99
+ /// ---
44
100
  pub(super) fn same_role_cohort_pre_spawn_error(
45
101
  transport: &dyn crate::transport::Transport,
46
102
  session_name: &SessionName,
@@ -50,6 +106,10 @@ pub(super) fn same_role_cohort_pre_spawn_error(
50
106
  same_role_cohort_error(transport, session_name, operation, targets, 0, true)
51
107
  }
52
108
 
109
+ /// ---
110
+ /// purpose: spawn 之后检查同角色同批是否恰好只剩一个
111
+ /// returns: 期望基数为 1;不满足时给出可读的拒绝说明,满足则 None
112
+ /// ---
53
113
  pub(super) fn same_role_cohort_exactly_one_error(
54
114
  transport: &dyn crate::transport::Transport,
55
115
  session_name: &SessionName,
@@ -59,6 +119,11 @@ pub(super) fn same_role_cohort_exactly_one_error(
59
119
  same_role_cohort_error(transport, session_name, operation, targets, 1, false)
60
120
  }
61
121
 
122
+ /// ---
123
+ /// purpose: 杀掉这些目标记录的旧 pane
124
+ /// returns: 全部处理完返回空值;目标没有期望 pane 或该 pane 明确不存在时跳过
125
+ /// errors: 杀 pane 失败时返回带席位、窗口与 pane 的错误串
126
+ /// ---
62
127
  pub(super) fn retire_expected_same_role_cohorts(
63
128
  transport: &dyn crate::transport::Transport,
64
129
  operation: &str,
@@ -175,6 +240,17 @@ fn same_role_cohort_error(
175
240
  None
176
241
  }
177
242
 
243
+ /// ---
244
+ /// purpose: 为一个席位拼出命令并在目标 session 里起进程
245
+ /// params:
246
+ /// resume_session_id: 给出且该 provider 支持 resume 时按 resume 起,否则丢弃它全新起
247
+ /// into_existing_session: 目标 session 已存在时开新窗口,否则新建 session
248
+ /// layout_placement: 有布局位置时按布局开窗或分格
249
+ /// spawn_cwd_override: 覆盖工作目录
250
+ /// owner_team_id_override: 显式指定写进 worker 环境的 owner team,缺省时退回席位行与顶层活跃键
251
+ /// returns: spawn 结果、时间戳、命令计划、profile 启动参数、布局位置与实际使用的 owner team
252
+ /// errors: 命令拼装、profile 准备或 transport spawn 失败时返回 LifecycleError
253
+ /// ---
178
254
  #[allow(clippy::too_many_arguments)]
179
255
  pub(super) fn spawn_agent_window(
180
256
  workspace: &Path,
@@ -213,7 +289,9 @@ pub(super) fn spawn_agent_window(
213
289
  let safety = if let Some(safety) = safety {
214
290
  safety
215
291
  } else {
216
- detected_safety = crate::lifecycle::launch::effective_runtime_config_for_worker_spawn()?;
292
+ detected_safety = crate::lifecycle::launch::effective_runtime_config_for_worker_spawn_json(
293
+ agent, provider,
294
+ )?;
217
295
  &detected_safety
218
296
  };
219
297
  let command_agent = crate::lifecycle::worker_command_context::WorkerCommandAgent::from_json(
@@ -226,7 +304,6 @@ pub(super) fn spawn_agent_window(
226
304
  let tools = crate::lifecycle::worker_command_context::resolved_tool_strings_for_command(
227
305
  &command_agent,
228
306
  provider,
229
- safety,
230
307
  )?;
231
308
  let resolved_tool_refs: Vec<&str> = tools.iter().map(String::as_str).collect();
232
309
  // owner_team_id resolution priority (Issue 2 fix):
@@ -325,6 +402,7 @@ pub(super) fn spawn_agent_window(
325
402
  workspace,
326
403
  agent_id.as_str(),
327
404
  team_id.as_deref(),
405
+ Some(crate::lifecycle::launch::auth_mode_env_value(auth_mode)),
328
406
  );
329
407
  crate::lifecycle::launch::apply_profile_launch_env(&mut env, &profile_launch);
330
408
  crate::lifecycle::launch::apply_mcp_auto_approval_env(&mut env, safety);
@@ -336,6 +414,27 @@ pub(super) fn spawn_agent_window(
336
414
  &mut env,
337
415
  )?;
338
416
  }
417
+ // 0.5.67 Cursor 方案 1 变体: role 经 workspace rules 文件注入 (不 argv)。
418
+ if provider == crate::provider::Provider::CursorAgent {
419
+ crate::lifecycle::launch::refuse_second_cursor_occupant(
420
+ workspace,
421
+ agent_id.as_str(),
422
+ None,
423
+ )?;
424
+ crate::lifecycle::launch::apply_cursor_agent_rules_overlay(
425
+ workspace,
426
+ agent_id.as_str(),
427
+ &system_prompt,
428
+ )?;
429
+ crate::lifecycle::launch::apply_cursor_mcp_overlay(workspace, &mcp_config)?;
430
+ crate::lifecycle::launch::enable_cursor_workspace_mcp(workspace)?;
431
+ crate::lifecycle::launch::apply_cursor_workspace_physical_path(&mut plan.argv, workspace);
432
+ crate::lifecycle::launch::apply_cursor_subscription_proxy_env(&mut env);
433
+ }
434
+ if provider == crate::provider::Provider::Grok {
435
+ crate::lifecycle::launch::ensure_grok_login_and_folder_trust(workspace)?;
436
+ crate::lifecycle::launch::apply_grok_mcp_overlay(workspace, &mcp_config)?;
437
+ }
339
438
  // 0.3.28 Step 3: per Python parity, worker spawn cwd is ALWAYS `workspace`.
340
439
  // The persisted-state `agent.spawn_cwd` override is ignored (it was a
341
440
  // Rust-only extension that drifted to `.team/runtime/<team_key>/` after
@@ -558,6 +657,10 @@ fn is_structural_startup_prompt_error(error: &str) -> bool {
558
657
  .any(|needle| lower.contains(needle))
559
658
  }
560
659
 
660
+ /// ---
661
+ /// purpose: 用 spec 里的同名 agent 补回 state 席位行缺的命令上下文字段
662
+ /// returns: 合并后的席位行;spec 读不到或找不到该 agent 时原样返回
663
+ /// ---
561
664
  pub(super) fn rehydrate_agent_command_context_from_spec(
562
665
  spec_workspace: &Path,
563
666
  agent_id: &AgentId,
@@ -591,6 +694,8 @@ fn merge_command_context_fields(
591
694
  "effort",
592
695
  "profile",
593
696
  "permission_mode",
697
+ // 0.5.66 bypass 单源:rehydrate 同步合入,restart 的 safety 构造读到新值。
698
+ "dangerously_skip_permissions",
594
699
  ] {
595
700
  if let Some(value) = spec_agent.get(field).and_then(yaml_value_to_json) {
596
701
  obj.insert(field.to_string(), value);
@@ -648,6 +753,13 @@ fn window_present_in_live(
648
753
  false
649
754
  }
650
755
 
756
+ /// ---
757
+ /// purpose: 起该 workspace 的 coordinator
758
+ /// params:
759
+ /// team_key: 传给 coordinator 的团队键
760
+ /// returns: 启动摘要
761
+ /// errors: 启动失败时返回 StatePersist
762
+ /// ---
651
763
  pub(super) fn start_coordinator_for_workspace(
652
764
  workspace: &Path,
653
765
  team_key: Option<&str>,
@@ -658,6 +770,13 @@ pub(super) fn start_coordinator_for_workspace(
658
770
  .map_err(|e| LifecycleError::StatePersist(e.to_string()))
659
771
  }
660
772
 
773
+ /// ---
774
+ /// purpose: 按团队持久化的 tmux endpoint 解析出后端,让 restart、add 与 fork 落在同一 socket
775
+ /// params:
776
+ /// team: 目标团队,None 时按唯一性选
777
+ /// returns: 绑定到该 endpoint 的 tmux 后端;冷 workspace 无持久 endpoint 时退到按 workspace 派生
778
+ /// errors: 团队目标歧义或未解析返回 TeamSelect;state 声明后端是 conpty 时也返回 TeamSelect 拒绝,不降级
779
+ /// ---
661
780
  /// State-aware tmux backend resolver. Reads the team's persisted
662
781
  /// `tmux_endpoint` (set at `team-agent launch` time and shared across
663
782
  /// restart/add-agent/fork-agent) and constructs a TmuxBackend on THAT socket,
@@ -732,6 +851,11 @@ pub(crate) fn lifecycle_worker_tmux_backend_for_selected_state(
732
851
  .unwrap_or_else(|| crate::tmux_backend::TmuxBackend::for_workspace(run_workspace)))
733
852
  }
734
853
 
854
+ /// ---
855
+ /// purpose: 与上面同样的团队选择语义,但返回工厂解析出的通用 transport
856
+ /// returns: 已解析的 transport,含后端种类、来源与提示
857
+ /// errors: 团队选择失败或工厂拒绝时返回 TeamSelect,读 state 失败返回 StatePersist
858
+ /// ---
735
859
  /// 0.5.x Phase 1d Batch 1: new generic-typed lifecycle resolver.
736
860
  ///
737
861
  /// Same team-selection semantics as the legacy tmux-typed variant, but
@@ -770,6 +894,11 @@ pub(crate) fn lifecycle_worker_transport_for_selected_state(
770
894
  .map_err(|e| LifecycleError::TeamSelect(e.to_string()))
771
895
  }
772
896
 
897
+ /// ---
898
+ /// purpose: 由已取到的 state 解析 tmux 后端与它的来源
899
+ /// returns: 后端与 endpoint 来源
900
+ /// errors: state 声明后端是 conpty 时返回 TeamSelect
901
+ /// ---
773
902
  pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
774
903
  run_workspace: &Path,
775
904
  state: &serde_json::Value,
@@ -791,6 +920,10 @@ pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
791
920
  )
792
921
  }
793
922
 
923
+ /// ---
924
+ /// purpose: 由已取到的 state 直接给出 tmux 后端
925
+ /// returns: 绑定到该 state endpoint 的后端,缺失时按 workspace 派生
926
+ /// ---
794
927
  pub(super) fn lifecycle_worker_tmux_backend_for_state(
795
928
  run_workspace: &Path,
796
929
  state: &serde_json::Value,
@@ -799,13 +932,14 @@ pub(super) fn lifecycle_worker_tmux_backend_for_state(
799
932
  .backend
800
933
  }
801
934
 
802
- pub(super) fn persist_effective_approval_policy_for_restart(
803
- agent: &mut serde_json::Map<String, serde_json::Value>,
804
- safety: &DangerousApproval,
805
- ) {
806
- crate::lifecycle::launch::persist_effective_approval_policy(agent, safety);
807
- }
808
-
935
+ /// ---
936
+ /// purpose: 同步团队投影后落盘 restart 结果
937
+ /// params:
938
+ /// topology_authority_agent_ids: 本次以内存值为拓扑权威的席位
939
+ /// returns: 成功返回空值
940
+ /// errors: 落盘失败时返回 StatePersist
941
+ /// contract_id: lifecycle.common.save_restart_projected_state
942
+ /// ---
809
943
  pub(super) fn save_restart_projected_state(
810
944
  workspace: &Path,
811
945
  state: &mut serde_json::Value,
@@ -821,6 +955,14 @@ pub(super) fn save_restart_projected_state(
821
955
  )
822
956
  }
823
957
 
958
+ /// ---
959
+ /// purpose: 同上,并可指定哪些席位跳过会话捕获回填
960
+ /// params:
961
+ /// skip_capture_backfill_agent_ids: 跳过回填的席位
962
+ /// returns: 成功返回空值
963
+ /// errors: 落盘失败时返回 StatePersist
964
+ /// contract_id: lifecycle.common.save_restart_projected_state
965
+ /// ---
824
966
  pub(super) fn save_restart_projected_state_with_capture_backfill_skip(
825
967
  workspace: &Path,
826
968
  state: &mut serde_json::Value,
@@ -829,17 +971,22 @@ pub(super) fn save_restart_projected_state_with_capture_backfill_skip(
829
971
  topology_authority_agent_ids: &[&str],
830
972
  ) -> Result<(), LifecycleError> {
831
973
  sync_restart_team_projections(state, team_key);
832
- crate::state::repository::StateRepository::new(workspace).save(
833
- crate::state::repository::StateWriteIntent::RestartTeam {
834
- team_key,
835
- topology_authority_agent_ids,
836
- skip_capture_backfill_agent_ids,
837
- },
838
- state,
839
- )
840
- .map_err(|e| LifecycleError::StatePersist(e.to_string()))
974
+ crate::state::repository::StateRepository::new(workspace)
975
+ .save(
976
+ crate::state::repository::StateWriteIntent::RestartTeam {
977
+ team_key,
978
+ topology_authority_agent_ids,
979
+ skip_capture_backfill_agent_ids,
980
+ },
981
+ state,
982
+ )
983
+ .map_err(|e| LifecycleError::StatePersist(e.to_string()))
841
984
  }
842
985
 
986
+ /// ---
987
+ /// purpose: 定出本次投影使用的团队键
988
+ /// returns: 显式 team 优先,其次 state 里的活跃键,最后由 state 推算
989
+ /// ---
843
990
  pub(super) fn restart_projection_team_key(state: &serde_json::Value, team: Option<&str>) -> String {
844
991
  team.filter(|key| !key.is_empty())
845
992
  .map(str::to_string)
@@ -853,6 +1000,12 @@ pub(super) fn restart_projection_team_key(state: &serde_json::Value, team: Optio
853
1000
  .unwrap_or_else(|| crate::state::projection::team_state_key(state))
854
1001
  }
855
1002
 
1003
+ /// ---
1004
+ /// purpose: 把当前顶层状态压实后写回 teams 表
1005
+ /// params:
1006
+ /// state: 就地改写;显式团队键允许覆盖或新建,别名键只在盘上已有且身份不冲突时才写
1007
+ /// returns: teams 表缺失或为空时不动
1008
+ /// ---
856
1009
  pub(super) fn sync_restart_team_projections(state: &mut serde_json::Value, team_key: &str) {
857
1010
  let Some(teams) = state.get("teams").and_then(serde_json::Value::as_object) else {
858
1011
  return;
@@ -933,6 +1086,10 @@ fn json_team_identity_matches(existing: &serde_json::Value, compact: &serde_json
933
1086
  true
934
1087
  }
935
1088
 
1089
+ /// ---
1090
+ /// purpose: 取 state 里的 session 名
1091
+ /// returns: 非空的 session_name,缺失时退到默认名
1092
+ /// ---
936
1093
  pub(super) fn state_session_name(state: &serde_json::Value) -> SessionName {
937
1094
  state
938
1095
  .get("session_name")
@@ -942,6 +1099,10 @@ pub(super) fn state_session_name(state: &serde_json::Value) -> SessionName {
942
1099
  .unwrap_or_else(|| SessionName::new("team-agent"))
943
1100
  }
944
1101
 
1102
+ /// ---
1103
+ /// purpose: 判断 state 里是否记了非空 session 名
1104
+ /// returns: 记了则 true
1105
+ /// ---
945
1106
  pub(super) fn session_name_present(state: &serde_json::Value) -> bool {
946
1107
  state
947
1108
  .get("session_name")
@@ -950,6 +1111,12 @@ pub(super) fn session_name_present(state: &serde_json::Value) -> bool {
950
1111
  .unwrap_or(false)
951
1112
  }
952
1113
 
1114
+ /// ---
1115
+ /// purpose: 探测 session 是否存活
1116
+ /// params:
1117
+ /// default: 探测本身 panic 时采用的兜底判定
1118
+ /// returns: transport 明确回答时用它;返回错误时判为不存活;探测 panic 时用兜底值
1119
+ /// ---
953
1120
  pub(super) fn session_live_or_default(
954
1121
  transport: &dyn crate::transport::Transport,
955
1122
  session_name: &SessionName,
@@ -964,6 +1131,10 @@ pub(super) fn session_live_or_default(
964
1131
  }
965
1132
  }
966
1133
 
1134
+ /// ---
1135
+ /// purpose: 取席位行的 provider
1136
+ /// returns: 解析成功用它,缺失或不认识时退到 codex
1137
+ /// ---
967
1138
  pub(super) fn agent_provider(agent: &serde_json::Value) -> Provider {
968
1139
  agent
969
1140
  .get("provider")
@@ -972,6 +1143,10 @@ pub(super) fn agent_provider(agent: &serde_json::Value) -> Provider {
972
1143
  .unwrap_or(Provider::Codex)
973
1144
  }
974
1145
 
1146
+ /// ---
1147
+ /// purpose: 取席位行的 auth_mode
1148
+ /// returns: 解析成功用它,缺失或不认识时退到 subscription
1149
+ /// ---
975
1150
  pub(super) fn agent_auth_mode(agent: &serde_json::Value) -> AuthMode {
976
1151
  agent
977
1152
  .get("auth_mode")
@@ -980,6 +1155,10 @@ pub(super) fn agent_auth_mode(agent: &serde_json::Value) -> AuthMode {
980
1155
  .unwrap_or(AuthMode::Subscription)
981
1156
  }
982
1157
 
1158
+ /// ---
1159
+ /// purpose: 取席位行记录的会话 id
1160
+ /// returns: 非空时返回,否则 None
1161
+ /// ---
983
1162
  pub(super) fn agent_session_id(agent: &serde_json::Value) -> Option<SessionId> {
984
1163
  agent
985
1164
  .get("session_id")
@@ -988,6 +1167,10 @@ pub(super) fn agent_session_id(agent: &serde_json::Value) -> Option<SessionId> {
988
1167
  .map(SessionId::new)
989
1168
  }
990
1169
 
1170
+ /// ---
1171
+ /// purpose: 取席位行记录的 rollout 路径
1172
+ /// returns: 非空时返回,否则 None
1173
+ /// ---
991
1174
  pub(super) fn agent_rollout_path(agent: &serde_json::Value) -> Option<RolloutPath> {
992
1175
  agent
993
1176
  .get("rollout_path")
@@ -996,6 +1179,10 @@ pub(super) fn agent_rollout_path(agent: &serde_json::Value) -> Option<RolloutPat
996
1179
  .map(RolloutPath::new)
997
1180
  }
998
1181
 
1182
+ /// ---
1183
+ /// purpose: 判断该席位的 resume 依据是否存在
1184
+ /// returns: 探测结果里的存在位
1185
+ /// ---
999
1186
  pub(super) fn resume_backing_exists_for_agent(
1000
1187
  workspace: &Path,
1001
1188
  agent_id: &AgentId,
@@ -1035,6 +1222,13 @@ pub(crate) struct SessionIdentityProbeResult {
1035
1222
  pub rollout_path: Option<PathBuf>,
1036
1223
  }
1037
1224
 
1225
+ /// ---
1226
+ /// purpose: 探测 rollout 文件里嵌的席位身份是否与本席位一致
1227
+ /// params:
1228
+ /// _provider: 未参与判定
1229
+ /// rollout_path: 没有路径时三项都返回未知
1230
+ /// returns: 一致性判定、读到的嵌入席位名与实际探测路径;读不出嵌入身份时一致性为未知
1231
+ /// ---
1038
1232
  pub(crate) fn session_identity_probe_for_agent(
1039
1233
  agent_id: &AgentId,
1040
1234
  _provider: Provider,
@@ -1059,6 +1253,10 @@ pub(crate) fn session_identity_probe_for_agent(
1059
1253
  }
1060
1254
  }
1061
1255
 
1256
+ /// ---
1257
+ /// purpose: 按 provider 分支探测 resume 依据,并记录所有查过的路径
1258
+ /// returns: 存在位与查过的路径列表;持久化的 rollout 路径即使不存在也记进列表
1259
+ /// ---
1062
1260
  pub(super) fn resume_backing_probe_for_agent(
1063
1261
  workspace: &Path,
1064
1262
  agent_id: &AgentId,
@@ -1110,6 +1308,47 @@ pub(super) fn resume_backing_probe_for_agent(
1110
1308
  }
1111
1309
  copilot_session_store_has_session(session_id.as_str())
1112
1310
  }
1311
+ Provider::Grok => {
1312
+ let spawn_cwd = agent
1313
+ .get("spawn_cwd")
1314
+ .and_then(serde_json::Value::as_str)
1315
+ .map(PathBuf::from)
1316
+ .unwrap_or_else(|| workspace.to_path_buf());
1317
+ match crate::provider::session_scan::grok::grok_session_dir(
1318
+ &spawn_cwd,
1319
+ session_id.as_str(),
1320
+ ) {
1321
+ Some(dir) => {
1322
+ checked_paths.push(dir.clone());
1323
+ crate::provider::session_scan::grok::grok_session_archive_present(&dir)
1324
+ }
1325
+ None => false,
1326
+ }
1327
+ }
1328
+ Provider::CursorAgent => {
1329
+ let spawn_cwd = agent
1330
+ .get("spawn_cwd")
1331
+ .and_then(serde_json::Value::as_str)
1332
+ .map(PathBuf::from)
1333
+ .unwrap_or_else(|| workspace.to_path_buf());
1334
+ let rollout_ok = rollout_path.is_some_and(|path| {
1335
+ crate::provider::session_scan::cursor::cursor_session_archive_present(path.as_path())
1336
+ });
1337
+ let discovered = crate::provider::session_scan::cursor::cursor_session_dir_for_cwd(
1338
+ session_id.as_str(),
1339
+ &spawn_cwd,
1340
+ );
1341
+ if let Some(dir) = discovered.as_ref() {
1342
+ checked_paths.push(dir.clone());
1343
+ }
1344
+ if let Some(home) = std::env::var_os("HOME").map(PathBuf::from) {
1345
+ checked_paths.push(home.join(".cursor").join("chats"));
1346
+ }
1347
+ rollout_ok
1348
+ || discovered.as_ref().is_some_and(|dir| {
1349
+ crate::provider::session_scan::cursor::cursor_session_archive_present(dir)
1350
+ })
1351
+ }
1113
1352
  Provider::GeminiCli | Provider::Fake => false,
1114
1353
  };
1115
1354
 
@@ -1124,10 +1363,20 @@ pub(super) fn resume_backing_probe_for_agent(
1124
1363
  }
1125
1364
  }
1126
1365
 
1366
+ /// ---
1367
+ /// purpose: 判断该 provider 是否支持 resume
1368
+ /// returns: 由 provider 适配器的能力位给出
1369
+ /// contract_id: lifecycle.common.provider_supports_resume
1370
+ /// ---
1127
1371
  pub(super) fn provider_supports_resume(provider: Provider) -> bool {
1128
1372
  crate::provider::get_adapter(provider).caps().resume
1129
1373
  }
1130
1374
 
1375
+ /// ---
1376
+ /// purpose: 由 provider wire 名判断是否支持 resume
1377
+ /// returns: 名字不认识时为 false
1378
+ /// contract_id: lifecycle.common.provider_supports_resume
1379
+ /// ---
1131
1380
  pub(super) fn provider_wire_supports_resume(provider: &str) -> bool {
1132
1381
  parse_provider(provider)
1133
1382
  .map(provider_supports_resume)
@@ -1303,6 +1552,13 @@ fn copilot_session_store_has_session(session_id: &str) -> bool {
1303
1552
  .is_ok()
1304
1553
  }
1305
1554
 
1555
+ /// ---
1556
+ /// purpose: 对缺会话的席位做一次捕获尝试
1557
+ /// params:
1558
+ /// state: 就地改写
1559
+ /// returns: 本次是否改动了 state
1560
+ /// errors: 捕获过程出错时返回 Provider
1561
+ /// ---
1306
1562
  pub(crate) fn refresh_missing_provider_sessions(
1307
1563
  state: &mut serde_json::Value,
1308
1564
  ) -> Result<bool, LifecycleError> {
@@ -1316,6 +1572,15 @@ pub(crate) fn refresh_missing_provider_sessions(
1316
1572
  .map_err(|e| LifecycleError::Provider(e.to_string()))
1317
1573
  }
1318
1574
 
1575
+ /// ---
1576
+ /// purpose: 有界等待缺会话席位收敛,并逐轮写进度事件
1577
+ /// params:
1578
+ /// deadline: 等待上限
1579
+ /// poll_interval: 轮询间隔
1580
+ /// allow_fresh: 只写进事件载荷,不改变收敛判定
1581
+ /// returns: 收敛结论
1582
+ /// errors: 收敛过程出错时返回 StatePersist
1583
+ /// ---
1319
1584
  pub(crate) fn converge_missing_provider_sessions(
1320
1585
  state: &mut serde_json::Value,
1321
1586
  deadline: std::time::Duration,
@@ -1361,6 +1626,10 @@ fn write_session_convergence_progress_event(
1361
1626
  .map_err(|e| e.to_string())
1362
1627
  }
1363
1628
 
1629
+ /// ---
1630
+ /// purpose: 列出必须等到会话收敛才能 restart 的席位
1631
+ /// returns: 排序后的席位 id;只保留无会话、状态为 running 且确有待保留上下文的席位,从未捕获过的席位不进入
1632
+ /// ---
1364
1633
  pub(crate) fn restart_required_missing_session_agent_ids(state: &serde_json::Value) -> Vec<String> {
1365
1634
  let mut missing = crate::session_capture::incomplete_resumable_agent_ids(state)
1366
1635
  .into_iter()
@@ -1400,6 +1669,10 @@ pub(crate) fn restart_required_missing_session_agent_ids(state: &serde_json::Val
1400
1669
  missing.sort();
1401
1670
  missing
1402
1671
  }
1672
+ /// ---
1673
+ /// purpose: 取该席位的窗口名
1674
+ /// returns: 席位行里的非空 window,缺失时用席位 id
1675
+ /// ---
1403
1676
  pub(super) fn agent_window(agent: &serde_json::Value, agent_id: &AgentId) -> String {
1404
1677
  agent
1405
1678
  .get("window")
@@ -1411,6 +1684,10 @@ pub(super) fn agent_window(agent: &serde_json::Value, agent_id: &AgentId) -> Str
1411
1684
 
1412
1685
  pub(super) use crate::provider::wire::{parse_provider, provider_wire};
1413
1686
 
1687
+ /// ---
1688
+ /// purpose: 把 auth_mode 字符串解析成枚举
1689
+ /// returns: 只认三种取值,未知返回 None
1690
+ /// ---
1414
1691
  pub(super) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
1415
1692
  match raw {
1416
1693
  "subscription" => Some(AuthMode::Subscription),
@@ -1420,6 +1697,11 @@ pub(super) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
1420
1697
  }
1421
1698
  }
1422
1699
 
1700
+ /// ---
1701
+ /// purpose: 从给定目录读出 team spec
1702
+ /// returns: 解析后的 YAML
1703
+ /// errors: 文件不存在返回 TeamSelect,读文件或解析失败返回 Compile
1704
+ /// ---
1423
1705
  pub(super) fn load_team_spec(workspace: &Path) -> Result<YamlValue, LifecycleError> {
1424
1706
  let spec_path = workspace.join("team.spec.yaml");
1425
1707
  if !spec_path.exists() {
@@ -1433,6 +1715,10 @@ pub(super) fn load_team_spec(workspace: &Path) -> Result<YamlValue, LifecycleErr
1433
1715
  yaml::loads(&text).map_err(|e| LifecycleError::Compile(e.to_string()))
1434
1716
  }
1435
1717
 
1718
+ /// ---
1719
+ /// purpose: 在 spec 的 agents 列表里找该席位
1720
+ /// returns: 命中的节点;该 id 其实是 leader 时返回 None
1721
+ /// ---
1436
1722
  pub(super) fn find_spec_agent<'a>(
1437
1723
  spec: &'a YamlValue,
1438
1724
  agent_id: &AgentId,
@@ -1455,10 +1741,18 @@ pub(super) fn find_spec_agent<'a>(
1455
1741
  })
1456
1742
  }
1457
1743
 
1744
+ /// ---
1745
+ /// purpose: 构造未知席位的错误
1746
+ /// returns: 带席位 id 的 RequirementUnmet
1747
+ /// ---
1458
1748
  pub(super) fn unknown_worker(agent_id: &AgentId) -> LifecycleError {
1459
1749
  LifecycleError::RequirementUnmet(format!("unknown worker agent id: {agent_id}"))
1460
1750
  }
1461
1751
 
1752
+ /// ---
1753
+ /// purpose: 定出 session 名,state 缺失时回落到 spec
1754
+ /// returns: 依次取 state 的 session_name、spec 的 runtime.session_name、由 team 名派生,最后用默认名
1755
+ /// ---
1462
1756
  pub(super) fn state_session_name_from_spec(
1463
1757
  state: &serde_json::Value,
1464
1758
  spec: &YamlValue,
@@ -1483,6 +1777,14 @@ pub(super) fn state_session_name_from_spec(
1483
1777
  .unwrap_or_else(|| SessionName::new("team-agent"))
1484
1778
  }
1485
1779
 
1780
+ /// ---
1781
+ /// purpose: 把席位标记为已停并清掉 window 与 pane_id 字段(pane_pid 等其余字段保留)
1782
+ /// params:
1783
+ /// state: 就地改写,非对象时先重置成空对象
1784
+ /// spec_agent: 提供 provider 等最小投影
1785
+ /// returns: 成功返回空值
1786
+ /// errors: state 结构不是对象时返回 StatePersist
1787
+ /// ---
1486
1788
  pub(super) fn mark_agent_stopped(
1487
1789
  state: &mut serde_json::Value,
1488
1790
  agent_id: &AgentId,
@@ -1532,6 +1834,13 @@ pub(super) fn mark_agent_stopped(
1532
1834
  Ok(())
1533
1835
  }
1534
1836
 
1837
+ /// ---
1838
+ /// purpose: 窗口本就存在而未新起进程时,把席位标记为运行中
1839
+ /// params:
1840
+ /// pane: 探到的现有 pane,用于写 pane id 与进程号
1841
+ /// returns: 成功返回空值
1842
+ /// errors: state 结构不是对象时返回 StatePersist
1843
+ /// ---
1535
1844
  pub(super) fn mark_agent_running_noop(
1536
1845
  state: &mut serde_json::Value,
1537
1846
  agent_id: &AgentId,
@@ -1590,6 +1899,11 @@ pub(super) fn mark_agent_running_noop(
1590
1899
  Ok(())
1591
1900
  }
1592
1901
 
1902
+ /// ---
1903
+ /// purpose: 写一条起席无操作事件
1904
+ /// returns: 成功返回空值
1905
+ /// errors: 事件写入失败时返回 StatePersist
1906
+ /// ---
1593
1907
  pub(super) fn write_start_agent_noop_event(
1594
1908
  workspace: &Path,
1595
1909
  agent_id: &AgentId,
@@ -1609,6 +1923,10 @@ pub(super) fn write_start_agent_noop_event(
1609
1923
  Ok(())
1610
1924
  }
1611
1925
 
1926
+ /// ---
1927
+ /// purpose: 判断某 session 里是否存在该窗口
1928
+ /// returns: 明确列出该窗口才为 true;列窗口出错或 panic 时为 false
1929
+ /// ---
1612
1930
  pub(super) fn window_exists(
1613
1931
  transport: &dyn crate::transport::Transport,
1614
1932
  session_name: &SessionName,
@@ -1622,6 +1940,11 @@ pub(super) fn window_exists(
1622
1940
  }
1623
1941
  }
1624
1942
 
1943
+ /// ---
1944
+ /// purpose: 在 state 里把该席位的显示标记为已关
1945
+ /// params:
1946
+ /// state: 就地改写;只有 ghostty 工作区后端会被改写状态与标题,其余后端不动
1947
+ /// ---
1625
1948
  pub(super) fn close_agent_display(state: &mut serde_json::Value, agent_id: &AgentId) {
1626
1949
  let Some(display) = state
1627
1950
  .get_mut("agents")
@@ -1649,6 +1972,13 @@ pub(super) fn close_agent_display(state: &mut serde_json::Value, agent_id: &Agen
1649
1972
  }
1650
1973
  }
1651
1974
 
1975
+ /// ---
1976
+ /// purpose: 丢弃该席位的会话捕获字段并标记为已停
1977
+ /// params:
1978
+ /// state: 就地改写;只删会话捕获相关字段,工作目录等状态字段保留
1979
+ /// returns: 成功返回空值
1980
+ /// errors: 席位不存在返回 RequirementUnmet,席位行不是对象返回 StatePersist
1981
+ /// ---
1652
1982
  pub(super) fn discard_agent_session_fields(
1653
1983
  state: &mut serde_json::Value,
1654
1984
  agent_id: &AgentId,
@@ -1696,6 +2026,10 @@ pub(super) fn discard_agent_session_fields(
1696
2026
  Ok(())
1697
2027
  }
1698
2028
 
2029
+ /// ---
2030
+ /// purpose: 判断该席位是否在运行
2031
+ /// returns: 状态为 running 或 busy 直接为真;其余状态都退到按 session 与窗口存在性判定
2032
+ /// ---
1699
2033
  pub(super) fn agent_is_running(
1700
2034
  state: &serde_json::Value,
1701
2035
  agent_id: &AgentId,
@@ -1727,6 +2061,10 @@ pub(super) fn agent_is_running(
1727
2061
  window_exists(transport, &session_name, window)
1728
2062
  }
1729
2063
 
2064
+ /// ---
2065
+ /// purpose: 判断该席位是不是动态生成的
2066
+ /// returns: state 里记了动态角色文件,或 spec 里标了来源席位时为 true
2067
+ /// ---
1730
2068
  pub(super) fn is_dynamic_agent(
1731
2069
  state: &serde_json::Value,
1732
2070
  spec_agent: &YamlValue,
@@ -1745,6 +2083,13 @@ pub(super) fn is_dynamic_agent(
1745
2083
  .is_some_and(|s| !s.is_empty())
1746
2084
  }
1747
2085
 
2086
+ /// ---
2087
+ /// purpose: 在 spawn 之前预判 tmux 的起法,供审计事件记录
2088
+ /// params:
2089
+ /// layout_placement: 有布局位置时按是否起新窗口区分
2090
+ /// into_existing_session: 目标 session 已存在
2091
+ /// returns: new-session、new-window 或 split-window
2092
+ /// ---
1748
2093
  /// 0.4.6 Stage 2: predict the tmux start mode BEFORE the spawn call, so
1749
2094
  /// the `provider.worker.spawn_argv` event can record what the spawn will
1750
2095
  /// actually do. Same logic as `tmux_start_mode_for_spawn` in agent.rs
@@ -1771,6 +2116,10 @@ pub(super) fn predict_tmux_start_mode(
1771
2116
  }
1772
2117
  }
1773
2118
 
2119
+ /// ---
2120
+ /// purpose: 从盘上读该席位当前的 spawn 世代号
2121
+ /// returns: 读到的值;读不出 state 或字段缺失时为 0
2122
+ /// ---
1774
2123
  /// 0.4.6 Stage 2: read state.agents[agent_id].spawn_epoch from disk to
1775
2124
  /// stamp the spawn_argv event with the current cohort identifier. Returns
1776
2125
  /// 0 if the agent row / field is missing.
@@ -1786,6 +2135,12 @@ pub(super) fn state_spawn_epoch_for_agent(workspace: &Path, agent_id: &AgentId)
1786
2135
  .unwrap_or(0)
1787
2136
  }
1788
2137
 
2138
+ /// ---
2139
+ /// purpose: 新起进程后清掉该席位的活动观测字段
2140
+ /// params:
2141
+ /// agent: 就地删除观测字段;生命周期与拓扑字段一概不动
2142
+ /// returns: 清完之后观测缺失表示未知,不等于空闲
2143
+ /// ---
1789
2144
  /// 0.5.32 (`.team/artifacts/restart-resumed-stale-activity-locate.md` §5):
1790
2145
  /// clear the per-agent turn/activity observation set on a successful new
1791
2146
  /// worker process cohort. Called from `mark_agent_started` /