@team-agent/installer 0.5.66 → 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 (164) hide show
  1. package/Cargo.lock +8 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +1 -0
  4. package/crates/team-agent/src/cli/adapters.rs +5 -0
  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/missing_subcommands.rs +129 -19
  17. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
  18. package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
  19. package/crates/team-agent/src/communication_mode/mod.rs +6 -4
  20. package/crates/team-agent/src/compiler.rs +59 -0
  21. package/crates/team-agent/src/coordinator/backoff.rs +55 -0
  22. package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
  23. package/crates/team-agent/src/coordinator/health.rs +173 -0
  24. package/crates/team-agent/src/coordinator/mod.rs +31 -0
  25. package/crates/team-agent/src/coordinator/orphan.rs +31 -0
  26. package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
  27. package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
  28. package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
  29. package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
  30. package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
  31. package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
  32. package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
  33. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
  34. package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
  35. package/crates/team-agent/src/coordinator/tick.rs +133 -22
  36. package/crates/team-agent/src/coordinator/types.rs +49 -0
  37. package/crates/team-agent/src/db/message_store.rs +39 -5
  38. package/crates/team-agent/src/layout/worker_env.rs +20 -3
  39. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  40. package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
  41. package/crates/team-agent/src/leader/registry.rs +65 -0
  42. package/crates/team-agent/src/leader/start.rs +920 -63
  43. package/crates/team-agent/src/lifecycle/display.rs +56 -0
  44. package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
  45. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -12
  46. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +62 -0
  47. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +59 -0
  48. package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
  49. package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
  50. package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
  51. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -456
  52. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
  53. package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
  54. package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
  55. package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
  56. package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
  57. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
  58. package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
  59. package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
  60. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
  61. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
  62. package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
  63. package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
  64. package/crates/team-agent/src/lifecycle/launch/spawn.rs +62 -2
  65. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +139 -0
  66. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -0
  67. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
  68. package/crates/team-agent/src/lifecycle/launch.rs +99 -29
  69. package/crates/team-agent/src/lifecycle/lock.rs +42 -1
  70. package/crates/team-agent/src/lifecycle/mod.rs +11 -0
  71. package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
  72. package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
  73. package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
  74. package/crates/team-agent/src/lifecycle/restart/agent.rs +124 -0
  75. package/crates/team-agent/src/lifecycle/restart/common.rs +369 -11
  76. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
  77. package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
  78. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +106 -0
  79. package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
  80. package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
  81. package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
  82. package/crates/team-agent/src/lifecycle/restart.rs +29 -0
  83. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +2 -3
  84. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
  85. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +150 -157
  86. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +2 -2
  87. package/crates/team-agent/src/lifecycle/tests/core.rs +4 -0
  88. package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
  89. package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
  90. package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
  91. package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
  92. package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
  93. package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
  94. package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
  95. package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
  96. package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
  97. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +63 -38
  98. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +3 -2
  99. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +16 -8
  100. package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
  101. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +20 -19
  102. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +97 -61
  103. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +2 -0
  104. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +40 -7
  105. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +59 -1
  106. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
  107. package/crates/team-agent/src/lifecycle/tests.rs +23 -13
  108. package/crates/team-agent/src/lifecycle/types.rs +61 -0
  109. package/crates/team-agent/src/lifecycle/worker_command_context.rs +85 -11
  110. package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
  111. package/crates/team-agent/src/mcp_server/tests.rs +196 -2
  112. package/crates/team-agent/src/messaging/delivery.rs +405 -40
  113. package/crates/team-agent/src/messaging/helpers.rs +2 -0
  114. package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
  115. package/crates/team-agent/src/messaging/results.rs +4 -0
  116. package/crates/team-agent/src/messaging/send.rs +34 -0
  117. package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
  118. package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
  119. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
  120. package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
  121. package/crates/team-agent/src/messaging/types.rs +4 -1
  122. package/crates/team-agent/src/model/enums.rs +14 -5
  123. package/crates/team-agent/src/model/permissions.rs +3 -0
  124. package/crates/team-agent/src/os_probe.rs +73 -2
  125. package/crates/team-agent/src/provider/adapter.rs +231 -4
  126. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
  127. package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
  128. package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
  129. package/crates/team-agent/src/provider/bypass_flags.rs +46 -2
  130. package/crates/team-agent/src/provider/classify.rs +4 -2
  131. package/crates/team-agent/src/provider/faults.rs +2 -1
  132. package/crates/team-agent/src/provider/mod.rs +11 -0
  133. package/crates/team-agent/src/provider/session/capture.rs +158 -20
  134. package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
  135. package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
  136. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
  137. package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
  138. package/crates/team-agent/src/provider/session/mod.rs +18 -0
  139. package/crates/team-agent/src/provider/session/resume.rs +57 -0
  140. package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
  141. package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
  142. package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
  143. package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
  144. package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
  145. package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
  146. package/crates/team-agent/src/provider/session_scan.rs +8 -0
  147. package/crates/team-agent/src/provider/submit_now.rs +94 -0
  148. package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
  149. package/crates/team-agent/src/provider/types.rs +4 -2
  150. package/crates/team-agent/src/provider/wire.rs +23 -1
  151. package/crates/team-agent/src/state/persist.rs +301 -8
  152. package/crates/team-agent/src/state/repository.rs +5 -0
  153. package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
  154. package/crates/team-agent/src/tmux_backend.rs +1064 -75
  155. package/crates/team-agent/src/transport/tests/wire.rs +28 -2
  156. package/crates/team-agent/src/transport.rs +248 -1
  157. package/npm/install.mjs +129 -73
  158. package/package.json +4 -4
  159. package/skills/team-agent/SKILL.md +33 -238
  160. package/skills/team-agent/command-coverage.json +31 -0
  161. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
  162. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -488
  163. package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
  164. package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
@@ -1,3 +1,19 @@
1
+ //! ---
2
+ //! purpose: 两个纯判定——daemon 自己是不是该自杀的孤儿,以及整个队是不是已经全没了
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: should_orphan_self_terminate
6
+ //! what: ppid 变更 + 被 init 收养 + workspace 已消失,三者同时成立才判孤儿
7
+ //! - name: detect_whole_team_gone
8
+ //! what: 由存活快照判整队消失,并区分 clean/restart(静默)与 unexpected(写 marker + 延迟升报)
9
+ //! depends:
10
+ //! - super::types
11
+ //! boundary:
12
+ //! - 不杀任何进程,也不发信号;只给判定结果,动作归调用方
13
+ //! - 不读屏、不命名 provider;只消费结构化存活快照
14
+ //! - 不直接升报用户,unexpected 只落 durable marker,等下一条 leader 命令再升
15
+ //! maturity: wired
16
+ //! ---
1
17
  //!
2
18
  //! 孤儿自终止判定(Gap 37b)+ provider-neutral 整队消失检测。
3
19
 
@@ -9,6 +25,14 @@ use super::types::{
9
25
  /// 孤儿自终止判定(`__main__.py:51-59`,Gap 37b)。仅当
10
26
  /// `current_ppid != initial_ppid ∧ current_ppid == 1 ∧ !workspace.exists()` 三者**同时**成立 → true。
11
27
  /// 少一个条件都不能误杀正常 daemon(card §91)。
28
+ /// ---
29
+ /// purpose: 判断本 daemon 是否已成为应当自我终止的孤儿
30
+ /// params:
31
+ /// initial_ppid: 进程启动那一刻记下的父 pid
32
+ /// current_ppid: 当前父 pid;被 init/launchd 收养后为 1
33
+ /// workspace: 本 daemon 服务的 workspace 根,用其存在性作第三个条件
34
+ /// returns: 三个条件同时成立才为 true;少一个都返回 false,宁可不自杀
35
+ /// ---
12
36
  pub fn should_orphan_self_terminate(
13
37
  initial_ppid: u32,
14
38
  current_ppid: u32,
@@ -25,6 +49,13 @@ pub fn should_orphan_self_terminate(
25
49
  /// `detect_whole_team_gone`(`abnormal_track.py:91`,C10/C13)。coordinator-independent 整队消失检测:
26
50
  /// 全死(coordinator + leader + 所有 worker + 所有 session)且非 clean_shutdown/restart →
27
51
  /// 写 durable marker + 延迟到下条 leader 命令再 escalate。clean/restart 静默。
52
+ /// ---
53
+ /// purpose: 由整队存活快照判定「整队消失」并给出分类与后续处置标志
54
+ /// params:
55
+ /// snapshot: coordinator/leader/各 worker 进程/transport 会话的存活位,以及 clean_shutdown、restart_in_progress 两个意图位
56
+ /// marker_store: durable marker 写入口;仅 unexpected_exit 分支会被调用
57
+ /// returns: 任一成员存活则 Alive 且全部标志为 false;全死时按 clean_shutdown、restart_in_progress、unexpected_exit 依次分类,只有 unexpected 才置 notify/escalate 并写 marker
58
+ /// ---
28
59
  pub fn detect_whole_team_gone(
29
60
  snapshot: &TeamPresenceSnapshot,
30
61
  marker_store: &mut dyn MarkerStore,
@@ -1,3 +1,22 @@
1
+ //! ---
2
+ //! purpose: 三个只读运行时探测——上下文压缩、Codex session 漂移、leader 端 API 错误——全部值变才发事件
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: observe_runtime
6
+ //! what: 对本 tick 的每条捕获事实跑三个探测,收集结果并写变化驱动的事件
7
+ //! depends:
8
+ //! - super::runtime_observation
9
+ //! - super::types
10
+ //! - crate::event_log
11
+ //! - crate::state::projection
12
+ //! - crate::state::ownership
13
+ //! - crate::model::enums
14
+ //! boundary:
15
+ //! - 只分类不动作:不重启 agent、不清 session、不投递任何消息,只给 recommendation 文案
16
+ //! - 事件是 change-driven:计数/指纹不变的那一 tick 不重发,去重键存在 state 里
17
+ //! - 只读 scrollback 尾与已存 session id,不与 provider 进程交互
18
+ //! maturity: wired
19
+ //! ---
1
20
  //!
2
21
  use std::collections::BTreeMap;
3
22
  use std::path::Path;
@@ -15,6 +34,15 @@ use super::types::{CompactionResult, LeaderApiError, SessionDriftResult};
15
34
 
16
35
  const COMPACTION_RESET_THRESHOLD_DEFAULT: i64 = 3;
17
36
 
37
+ /// ---
38
+ /// purpose: 对本 tick 的全部捕获事实依次跑 compaction / session-drift / leader-api-error 三个探测
39
+ /// params:
40
+ /// workspace: workspace 根,用于打开事件日志
41
+ /// state: 可变运行时状态;探测在其中读写压缩计数、漂移标记与 api-error 指纹这些跨 tick 去重键
42
+ /// captures_by_agent: 本 tick 的 per-agent 捕获事实,按 agent_id 有序
43
+ /// leader_capture: leader 侧捕获;为 None 时 api-error 结果为空,且不清除既有指纹
44
+ /// returns: 捕获事实原样带回,附三组探测结果;未命中的探测对应空 Vec
45
+ /// ---
18
46
  pub fn observe_runtime(
19
47
  workspace: &Path,
20
48
  state: &mut Value,
@@ -554,6 +582,8 @@ fn runtime_event_provider_name(provider: Provider) -> &'static str {
554
582
  Provider::Codex => "codex",
555
583
  Provider::Copilot => "copilot",
556
584
  Provider::GeminiCli => "gemini_cli",
585
+ Provider::Grok => "grok",
586
+ Provider::CursorAgent => "cursor_agent",
557
587
  Provider::Fake => "fake",
558
588
  }
559
589
  }
@@ -1,3 +1,19 @@
1
+ //! ---
2
+ //! purpose: 运行时观测的类型缝——定义「一次 tick 捕到的事实」与「探测结果集合」两个形状,并把探测转交 runtime_detectors
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: observe
6
+ //! what: 把本 tick 的 per-agent 捕获事实与 leader 捕获事实交给探测器,返回结果集合
7
+ //! depends:
8
+ //! - super::runtime_detectors
9
+ //! - super::types
10
+ //! - crate::provider
11
+ //! - crate::transport
12
+ //! boundary:
13
+ //! - 不做捕获本身(scrollback/pane 快照由 tick 侧采集后传入)
14
+ //! - 不做任何判定,判定全在 runtime_detectors
15
+ //! maturity: wired
16
+ //! ---
1
17
  //!
2
18
  //! Shared coordinator runtime observation seam.
3
19
  //!
@@ -49,6 +65,15 @@ pub struct RuntimeObservationResults {
49
65
  pub api_errors: Vec<LeaderApiError>,
50
66
  }
51
67
 
68
+ /// ---
69
+ /// purpose: 本 tick 观测的统一入口,把捕获事实转交 runtime_detectors 并原样带回结果
70
+ /// params:
71
+ /// workspace: workspace 根,探测器据此写事件日志
72
+ /// state: 可变运行时状态;探测器在其中保存跨 tick 的去重/计数键
73
+ /// captures_by_agent: 本 tick 每个 agent 的捕获事实(scrollback 尾、pane 信息、已存 session id 等)
74
+ /// leader_capture: leader 侧捕获事实;缺失表示本 tick 没拿到 leader 屏幕,api-error 探测随之为空
75
+ /// returns: 捕获事实原样回传,外加 compaction / session_drift / leader api-error 三组探测结果
76
+ /// ---
52
77
  pub fn observe(
53
78
  workspace: &Path,
54
79
  state: &mut Value,
@@ -1,3 +1,31 @@
1
+ //! ---
2
+ //! purpose: tick 的异常退出检测步骤——按 agent 读有界 rollout 尾,判「最新一条显式 provider 错误是否属于当前 worker 世代」,据此决定通知还是只留审计
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: detect_abnormal_exits
6
+ //! what: 遍历受监视 agent,产出 worker.abnormal_exit 通知或 dead_only 审计事件,并把本轮观测写回 state 的 abnormal_exit_watch
7
+ //! - name: explicit_process_liveness
8
+ //! what: 从 agent JSON 的显式存活字段解析进程存活态,不做任何探测
9
+ //! - name: read_tail_text
10
+ //! what: 有界读文件尾部文本,超长时从尾部 max_bytes 处起读
11
+ //! - name: metadata_mtime_ns
12
+ //! what: 把文件 metadata 的 mtime 归一成纳秒整数,供跨 tick 变化比较
13
+ //! depends:
14
+ //! - super::super::health
15
+ //! - super::super::tick
16
+ //! - super::super::types
17
+ //! - crate::event_log
18
+ //! - crate::messaging
19
+ //! - crate::provider
20
+ //! - crate::transport
21
+ //! - crate::state::projection
22
+ //! boundary:
23
+ //! - 不重启、不清理、不做物理注入:写事件与 state 观测记录之外,异常通知会经 crate::messaging 的 leader funnel 入库为 accepted 消息(物理注入由后续 deliver_pending 完成)
24
+ //! - 进程已死但没有显式错误时不通知,只落 dead_only 审计
25
+ //! - 通知事件里的 queued 只表示消息入库,不表示已送达
26
+ //! - rollout 尾读是有界的(首行可能被截断,消费方只解析最后一条完整 JSONL 记录)
27
+ //! maturity: wired
28
+ //! ---
1
29
  //!
2
30
  //! unit-11 (Stage 4) — coordinator tick `abnormal` step group.
3
31
  //!
@@ -23,6 +51,17 @@ use super::super::types::Pid;
23
51
  /// provider error that is fresh for the current worker cohort. A dead process
24
52
  /// without an explicit error remains a suppressed `dead_only` audit event; process
25
53
  /// liveness is otherwise diagnostic data for this path.
54
+ /// ---
55
+ /// purpose: 跑一轮异常退出检测,把结论落成事件与 state 里的 abnormal_exit_watch 记录
56
+ /// params:
57
+ /// workspace: workspace 根,用于解析 rollout 相对路径
58
+ /// transport: pane 存活探测通道;只读探测,不注入
59
+ /// state: 可变运行时状态;本轮观测(size/mtime/世代/去重键)写回 coordinator.abnormal_exit_watch
60
+ /// event_log: 事件出口
61
+ /// targets: 本 tick 已采到的 pane 快照,避免每 agent 重新列一遍
62
+ /// returns: 检测跑完即 Ok;「有没有异常」不由返回值表达,只体现在事件与 state 里
63
+ /// errors: 事件写入等不可恢复失败时返回 TickError;单个 agent 的 rollout metadata 读不到不算错误,记为 unverifiable 继续
64
+ /// ---
26
65
  pub(crate) fn detect_abnormal_exits(
27
66
  workspace: &Path,
28
67
  transport: &dyn crate::transport::Transport,
@@ -476,6 +515,12 @@ fn agent_pid(agent: &Value) -> Option<Pid> {
476
515
  .find_map(|key| json_u32(agent.get(key)).map(Pid::new))
477
516
  }
478
517
 
518
+ /// ---
519
+ /// purpose: 只从 agent JSON 里已写明的存活字段读出进程存活态,不做任何主动探测
520
+ /// params:
521
+ /// agent: agent 状态对象;先下钻 provider_process / process 子对象,再看本层的 *_liveness 串字段
522
+ /// returns: 命中显式字段时给出 Alive/Dead/Unverifiable;没有任何显式字段时为 None,交调用方走探测路径
523
+ /// ---
479
524
  pub(crate) fn explicit_process_liveness(agent: &Value) -> Option<ProcessLiveness> {
480
525
  if let Some(process) = agent
481
526
  .get("provider_process")
@@ -836,6 +881,12 @@ fn process_liveness_wire(state: ProcessLiveness) -> &'static str {
836
881
  }
837
882
  }
838
883
 
884
+ /// ---
885
+ /// purpose: 把文件修改时间归一成自 UNIX 纪元起的纳秒整数,供跨 tick 比较「rollout 有没有变过」
886
+ /// params:
887
+ /// metadata: 目标文件的 metadata
888
+ /// returns: 纳秒时间戳;取不到 mtime 或早于纪元时为 None。乘加均为 saturating,不会回绕
889
+ /// ---
839
890
  pub(crate) fn metadata_mtime_ns(metadata: &std::fs::Metadata) -> Option<u64> {
840
891
  let duration = metadata
841
892
  .modified()
@@ -977,6 +1028,14 @@ const ABNORMAL_TAIL_BYTES: u64 = 131_072;
977
1028
  ///
978
1029
  /// P1: bounded tail read; a partial first line is harmless (the consumer only parses
979
1030
  /// the latest complete JSONL record) and lossy UTF-8 keeps a mid-codepoint seek safe.
1031
+ /// ---
1032
+ /// purpose: 有界读取文件尾部文本,避免整份 rollout 进内存
1033
+ /// params:
1034
+ /// path: 目标文件
1035
+ /// max_bytes: 最多回读的字节数;文件更短时整份读出
1036
+ /// returns: 尾部文本。按字节 seek,首行可能被截断,且用有损 UTF-8 解码,切在码点中间也安全
1037
+ /// errors: 打开、取长度或读取失败时返回底层 io::Error
1038
+ /// ---
980
1039
  pub(crate) fn read_tail_text(path: &Path, max_bytes: u64) -> std::io::Result<String> {
981
1040
  use std::io::{Read, Seek, SeekFrom};
982
1041
  let mut file = std::fs::File::open(path)?;
@@ -1,3 +1,13 @@
1
+ //! ---
2
+ //! purpose: coordinator tick delivery 步骤组的占位命名空间——实现仍在 tick.rs
3
+ //! contract:
4
+ //! provides: []
5
+ //! depends: []
6
+ //! boundary:
7
+ //! - 本文件当前不含任何 item:消息投递 FSM 的 tick 步骤未来迁入处
8
+ //! - 迁移落地前不要在此新增逻辑,否则步骤顺序会分裂成两处
9
+ //! maturity: signature_only
10
+ //! ---
1
11
  //!
2
12
  //! unit-11 (Stage 4) — coordinator tick `delivery` step group.
3
13
  //!
@@ -1,3 +1,13 @@
1
+ //! ---
2
+ //! purpose: coordinator tick health_sync 步骤组的占位命名空间——实现仍在 tick.rs
3
+ //! contract:
4
+ //! provides: []
5
+ //! depends: []
6
+ //! boundary:
7
+ //! - 本文件当前不含任何 item:worker 存活对账步骤未来迁入处
8
+ //! - 迁移落地前不要在此新增逻辑,否则步骤顺序会分裂成两处
9
+ //! maturity: signature_only
10
+ //! ---
1
11
  //!
2
12
  //! unit-11 (Stage 4) — coordinator tick `health_sync` step group.
3
13
  //!
@@ -1,3 +1,17 @@
1
+ //! ---
2
+ //! purpose: tick 步骤组的命名空间与规范顺序——给每个步骤组一个稳定标签与固定次序,供编排器与事件日志共用
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: ordered
6
+ //! what: 步骤组的规范顺序(穷尽枚举),编排器必须按此序调用
7
+ //! - name: as_str
8
+ //! what: 步骤组的稳定标签(tick.<group>),进事件与指标字段
9
+ //! depends: []
10
+ //! boundary:
11
+ //! - 不含任何步骤的实现,只定义分组身份与顺序
12
+ //! - persist 必须排最后、session_gate 必须排最前,这是顺序本身承载的约束
13
+ //! maturity: wired
14
+ //! ---
1
15
  //!
2
16
  //! unit-11 (Stage 4) — coordinator tick steps namespace.
3
17
  //!
@@ -37,6 +51,10 @@ pub enum TickStepGroup {
37
51
 
38
52
  impl TickStepGroup {
39
53
  /// Stable label used in event-log and metric fields.
54
+ /// ---
55
+ /// purpose: 给步骤组一个稳定标签,供事件日志与指标字段使用
56
+ /// returns: 形如 tick.<group> 的稳定串;已发布的取值不可改写,只可新增
57
+ /// ---
40
58
  pub fn as_str(self) -> &'static str {
41
59
  match self {
42
60
  Self::SessionGate => "tick.session_gate",
@@ -50,6 +68,10 @@ impl TickStepGroup {
50
68
 
51
69
  ///
52
70
  /// Canonical ordering as a const slice — exhaustive over the enum.
71
+ /// ---
72
+ /// purpose: 给出步骤组的规范调用顺序
73
+ /// returns: 穷尽覆盖本 enum 全部变体的静态切片,session_gate 在首、persist 在尾;编排器按此序调用,持久化才能反映之前每一步的结果
74
+ /// ---
53
75
  pub fn ordered() -> &'static [TickStepGroup] {
54
76
  &[
55
77
  TickStepGroup::SessionGate,
@@ -1,3 +1,13 @@
1
+ //! ---
2
+ //! purpose: coordinator tick persist 步骤组的占位命名空间——实现仍在 tick.rs
3
+ //! contract:
4
+ //! provides: []
5
+ //! depends: []
6
+ //! boundary:
7
+ //! - 本文件当前不含任何 item:tick 末尾的持久化步骤未来迁入处
8
+ //! - 迁移落地前不要在此新增逻辑,否则步骤顺序会分裂成两处
9
+ //! maturity: signature_only
10
+ //! ---
1
11
  //!
2
12
  //! unit-11 (Stage 4) — coordinator tick `persist` step group.
3
13
  //!
@@ -1,3 +1,13 @@
1
+ //! ---
2
+ //! purpose: coordinator tick runtime_prompts 步骤组的占位命名空间——实现仍在 tick.rs
3
+ //! contract:
4
+ //! provides: []
5
+ //! depends: []
6
+ //! boundary:
7
+ //! - 本文件当前不含任何 item:startup / approval / abnormal-exit 提示步骤未来迁入处
8
+ //! - 迁移落地前不要在此新增逻辑,否则步骤顺序会分裂成两处
9
+ //! maturity: signature_only
10
+ //! ---
1
11
  //!
2
12
  //! unit-11 (Stage 4) — coordinator tick `runtime_prompts` step group.
3
13
  //!
@@ -1,3 +1,13 @@
1
+ //! ---
2
+ //! purpose: coordinator tick session_gate 步骤组的占位命名空间——实现仍在 tick.rs
3
+ //! contract:
4
+ //! provides: []
5
+ //! depends: []
6
+ //! boundary:
7
+ //! - 本文件当前不含任何 item:provider session 捕获与就绪门步骤未来迁入处
8
+ //! - 迁移落地前不要在此新增逻辑,否则步骤顺序会分裂成两处
9
+ //! maturity: signature_only
10
+ //! ---
1
11
  //!
2
12
  //! unit-11 (Stage 4) — coordinator tick `session_gate` step group.
3
13
  //!
@@ -1,3 +1,37 @@
1
+ //! ---
2
+ //! purpose: coordinator 的核心——Coordinator 本体与单次 tick 的固定顺序编排,外加心跳 sidecar 与 worker 运行态解析
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: tick
6
+ //! what: 跑一轮固定顺序的编排:会话门 → 捕获缺失 session → 启动/审批提示 → 健康对账 → 投递与到期事件 → 只读探测 → 原子持久化 → 回收结果
7
+ //! - name: write_coordinator_heartbeat
8
+ //! what: 写 runtime/coordinator_tick.json 心跳 sidecar(阶段、状态、boot id、迭代计数)
9
+ //! - name: read_coordinator_heartbeat
10
+ //! what: 只读取出最近一次心跳,供 status/diagnose 消费
11
+ //! - name: resolve_worker_runtime_state_with_fg_pgrp
12
+ //! what: 由 agent JSON 与活动分类结果解析 worker 运行态,缺信号时判 Unknown 而非 Idle
13
+ //! depends:
14
+ //! - super::health
15
+ //! - super::types
16
+ //! - super::steps
17
+ //! - super::steps::abnormal
18
+ //! - super::runtime_observation
19
+ //! - crate::state
20
+ //! - crate::message_store
21
+ //! - crate::messaging
22
+ //! - crate::leader
23
+ //! - crate::provider
24
+ //! - crate::transport
25
+ //! - crate::event_log
26
+ //! - crate::os_probe
27
+ //! boundary:
28
+ //! - 不实现投递、到期事件与结果回收的本体,那些在 crate::messaging,本模块只按序调用
29
+ //! - 无 pending obligation 时不注入任何探索性 prompt;无既定义务时唯一会投递的是 should_ping 成立时的一条中性提示。例外:屏上出现审批提示且策略允许时,runtime_approval 会经 transport.send_keys 注入应答按键(不是 prompt,是对已观测提示的应答)
30
+ //! - 监测步骤失败降级并继续,TickReport.ok=true 不代表每一步都成功——各步失败只在事件里
31
+ //! - 持久化失败走 degraded 报告而不是 panic,也不是 Err
32
+ //! - 不直接依赖 provider client crate,provider 一律经注入的 ProviderAdapter
33
+ //! maturity: wired
34
+ //! ---
1
35
  //!
2
36
  //! Coordinator core:daemon lifecycle 宿主 + 单次 tick 编排(19 步固定顺序)+ health/start/stop。
3
37
 
@@ -166,6 +200,14 @@ pub struct Coordinator {
166
200
 
167
201
  impl Coordinator {
168
202
  /// 构造(注入 provider registry + transport)。spawn 出的 daemon 在 `run` 前装配它。
203
+ /// ---
204
+ /// purpose: 装配一个 Coordinator——注入 provider registry 与 transport,两者都是 trait object 以便替换与 mock
205
+ /// params:
206
+ /// workspace: 本实例服务的 workspace 根
207
+ /// provider_registry: provider adapter 解析器;provider 一律经它取,绝不直连 provider client crate
208
+ /// transport: 会话/pane 控制面,用于存活探测与注入
209
+ /// returns: 未绑定 team_key、无 save 钩子、无顺序探针的实例
210
+ /// ---
169
211
  pub fn new(
170
212
  workspace: WorkspacePath,
171
213
  provider_registry: Box<dyn ProviderRegistry>,
@@ -181,6 +223,12 @@ impl Coordinator {
181
223
  }
182
224
  }
183
225
 
226
+ /// ---
227
+ /// purpose: 绑定 daemon 侧选定的 team_key,让 tick 的状态投影用它而不是可能过期的根 active_team_key
228
+ /// params:
229
+ /// team_key: 目标 team;None 或空串都视为未绑定,tick 会回落到 state 推导
230
+ /// returns: 绑定后的自身
231
+ /// ---
184
232
  pub(crate) fn with_team_key(mut self, team_key: Option<String>) -> Self {
185
233
  self.daemon_team_key = team_key.filter(|key| !key.is_empty());
186
234
  self
@@ -190,6 +238,17 @@ impl Coordinator {
190
238
  /// transport + mock provider registry + 可选 save 注入钩 + ORDER 探针。**纯 test-support
191
239
  /// 脚手架**(真实 impl,非 `unimplemented!()`):它只装配字段,不执行任何 daemon 逻辑;
192
240
  /// tick/health/start/stop 仍是 `unimplemented!()` 生产体,因此调它们的契约仍 RED。
241
+ /// ---
242
+ /// purpose: 测试装配口——直接构出实例并注入 save 钩子与步骤顺序探针
243
+ /// params:
244
+ /// workspace: workspace 根
245
+ /// provider_registry: 可 mock 的 provider adapter 解析器
246
+ /// transport: 可 mock 的传输控制面
247
+ /// save_hook: 替换真实持久化,用于制造持久化失败
248
+ /// order_recorder: 记录 tick 内各步骤名,用于断言固定顺序
249
+ /// returns: 只装配字段的实例,本函数自身不执行任何 daemon 逻辑
250
+ /// cfg: test
251
+ /// ---
193
252
  #[cfg(test)]
194
253
  pub(crate) fn for_test(
195
254
  workspace: WorkspacePath,
@@ -226,6 +285,11 @@ impl Coordinator {
226
285
  /// `state::save_runtime_state`,bug-084 测试注入失败);在每个 step8-11 原子调用点
227
286
  /// `if let Some(rec) = &self.order_recorder { rec.lock()...push(STEP_NAME) }`(tick
228
287
  /// 副作用 ORDER 测试断言固定序列)。生产两者均 `None`,零开销。
288
+ /// ---
289
+ /// purpose: 跑一轮 tick——按固定顺序把各步骤串起来,只投递既定义务,绝不凭空注入
290
+ /// returns: TickReport。传输会话不在了 → stop=true 且 reason=TmuxSessionMissing,主循环据此退出;持久化失败 → ok=false、reason=PersistenceDegraded、persisted=Some(false),但仍是 Ok;正常 → ok=true 并带本轮投递、到期事件、回收结果等清单。注意 ok=true 只说明主干走完,监测类步骤失败已被降级吞掉,真相只在事件日志里
291
+ /// errors: 加载状态、打开 message store、传输探测或事件写入这些主干操作失败时返回 TickError,由主循环 catch 后退避
292
+ /// ---
229
293
  pub fn tick(&self) -> Result<TickReport, TickError> {
230
294
  self.record_step(TickStepGroup::SessionGate, "load_state");
231
295
  let raw_state = crate::state::persist::load_runtime_state(self.workspace.as_path())?;
@@ -271,17 +335,12 @@ impl Coordinator {
271
335
  }
272
336
 
273
337
  self.record_step(TickStepGroup::SessionGate, "capture_missing");
274
- let pending_context_fork_audits =
275
- match self.capture_missing_sessions(&mut state, &event_log) {
276
- Ok(audits) => audits,
277
- Err(error) => {
278
- let _ = event_log.write(
279
- "coordinator.tick.capture_missing_failed",
280
- serde_json::json!({"error": error.to_string()}),
281
- );
282
- Vec::new()
283
- }
284
- };
338
+ if let Err(error) = self.capture_missing_sessions(&mut state, &event_log) {
339
+ let _ = event_log.write(
340
+ "coordinator.tick.capture_missing_failed",
341
+ serde_json::json!({"error": error.to_string()}),
342
+ );
343
+ }
285
344
 
286
345
  // Slice 1 energy gate: one pane snapshot per tick feeds probe eligibility,
287
346
  // health sync, and abnormal-exit detection. Missing panes are filtered
@@ -461,15 +520,6 @@ impl Coordinator {
461
520
  collections,
462
521
  ));
463
522
  }
464
- for context_fork in &pending_context_fork_audits {
465
- context_fork.write_audit(&event_log).map_err(|error| {
466
- eprintln!(
467
- "[coordinator] context_fork audit publish failed after state commit: {error}"
468
- );
469
- TickError::EventLog(error)
470
- })?;
471
- }
472
-
473
523
  self.record_step(TickStepGroup::Delivery, "collect_results");
474
524
  collections.results =
475
525
  collect_results(crate::messaging::collect_results_and_notify_watchers(
@@ -489,7 +539,7 @@ impl Coordinator {
489
539
  &self,
490
540
  state: &mut Value,
491
541
  event_log: &EventLog,
492
- ) -> Result<Vec<crate::lifecycle::launch::ContextForkFinalized>, TickError> {
542
+ ) -> Result<(), TickError> {
493
543
  let report = crate::session_capture::capture_missing_provider_sessions_once(
494
544
  state,
495
545
  &mut |provider| self.provider_registry.adapter_for(provider),
@@ -653,7 +703,7 @@ impl Coordinator {
653
703
  }),
654
704
  )?;
655
705
  }
656
- Ok(report.context_forks)
706
+ Ok(())
657
707
  }
658
708
 
659
709
  fn sync_agent_health(
@@ -1371,6 +1421,11 @@ impl Coordinator {
1371
1421
 
1372
1422
  /// `coordinator_health`(`lifecycle.py:26`)。pid + meta + schema 三合一健康。
1373
1423
  /// doctor / start 前置调它。`ok = running ∧ metadata_ok ∧ schema_ok`。
1424
+ /// ---
1425
+ /// purpose: 取本 workspace 的 coordinator 健康报告
1426
+ /// returns: 与 health::coordinator_health 完全一致的报告
1427
+ /// errors: 当前实现恒为 Ok;返回类型保留 Result 以便未来健康判定引入可失败路径
1428
+ /// ---
1374
1429
  pub fn health(&self) -> Result<HealthReport, TickError> {
1375
1430
  Ok(super::health::coordinator_health(&self.workspace))
1376
1431
  }
@@ -1379,11 +1434,23 @@ impl Coordinator {
1379
1434
  /// schema 不兼容拒启给 hint;否则 spawn 自身二进制子命令(`team-agent coordinator --workspace ..`,
1380
1435
  /// Python 是 `python -m team_agent.coordinator`,`lifecycle.py:108`)。
1381
1436
  /// **schema 兼容门**:三元任一不匹配 → restart_incompatible,**不可静默继续**(card §89)。
1437
+ /// ---
1438
+ /// purpose: 幂等启动本 workspace 的 daemon 子进程
1439
+ /// returns: 与 health::start_coordinator 完全一致的报告;「拒启」体现为 ok=false 而非 Err
1440
+ /// errors: 建目录、开日志、spawn 或写 pid/metadata 失败时返回 StartError
1441
+ /// ---
1382
1442
  pub fn start(&self) -> Result<StartReport, StartError> {
1383
1443
  super::health::start_coordinator(&self.workspace)
1384
1444
  }
1385
1445
 
1386
1446
  /// `stop_coordinator`(`lifecycle.py:229`)。SIGTERM + 清 pid/meta。pid 非整数 → 清文件返回。
1447
+ /// ---
1448
+ /// purpose: 清掉本 workspace 的 coordinator pid/meta 文件
1449
+ /// returns: pid 文件不存在 → Missing;文件在且内容可解析 → Stopped 并带该 pid;文件在但内容非法 → InvalidPidRemoved
1450
+ /// errors: 删文件失败时返回 StopError
1451
+ /// ---
1452
+ /// 注意:本方法只删文件,**不向 daemon 发任何信号**——与 health::stop_coordinator
1453
+ /// 的「真终止进程」语义不同。调它之后 daemon 仍会继续 tick,而健康探测会报 Missing。
1387
1454
  pub fn stop(&self) -> Result<StopReport, StopError> {
1388
1455
  let pid_path = coordinator_pid_path(&self.workspace);
1389
1456
  if !pid_path.exists() {
@@ -1412,6 +1479,10 @@ impl Coordinator {
1412
1479
 
1413
1480
  /// `message_store_schema_health`(`lifecycle.py:197`)。DB 列兼容门:区分 pre-init 必需列缺失
1414
1481
  /// (拒启)vs migratable 列缺失(可迁移)。`doctor --fix-schema` 用其 action hint。
1482
+ /// ---
1483
+ /// purpose: 查本队 message store 的 schema 兼容门
1484
+ /// returns: 与 health::message_store_schema_health 一致;打不开库时 ok=false 并带原文与修复 hint
1485
+ /// ---
1415
1486
  pub fn schema_health(&self) -> SchemaHealth {
1416
1487
  // A-8: the gate must inspect the REAL team.db (Python lifecycle.py:197+
1417
1488
  // message_store_schema_health); a hardcoded ok:true left the card §89
@@ -1525,6 +1596,21 @@ fn increment_coordinator_tick_iteration_count(workspace: &WorkspacePath) {
1525
1596
  );
1526
1597
  }
1527
1598
 
1599
+ /// ---
1600
+ /// purpose: 写心跳 sidecar runtime/coordinator_tick.json,让外部不必进程内也能看到 daemon 走到哪一步
1601
+ /// params:
1602
+ /// workspace: workspace 根
1603
+ /// pid: 当前 daemon pid
1604
+ /// boot_id: daemon 世代 id;None 时沿用文件里已有值,都没有则用 coord_unknown_<pid>
1605
+ /// last_phase: 本次写入代表的阶段名;值为 tick_running 时刷新 last_tick_started_at,否则刷新 last_tick_finished_at
1606
+ /// last_tick_status: 上一轮 tick 的状态串
1607
+ /// last_error: 上一轮的错误串
1608
+ /// increment_count: 是否把迭代计数加一
1609
+ /// returns: 写成功返回 ();先写临时文件再 rename,读方不会看到半份
1610
+ /// errors: 建目录、写临时文件或 rename 失败时返回 io::Error
1611
+ /// ---
1612
+ /// ⚠ 字段 `host_boot_time` 写的是**本次写入时刻的墙钟**,不是主机启动时间——
1613
+ /// 每次心跳都会变。主机启动身份在 `host_boot_id`,按名消费 host_boot_time 会拿到假事实。
1528
1614
  pub(crate) fn write_coordinator_heartbeat(
1529
1615
  workspace: &WorkspacePath,
1530
1616
  pid: Pid,
@@ -1597,6 +1683,10 @@ fn coordinator_heartbeat_path(workspace: &WorkspacePath) -> PathBuf {
1597
1683
  /// falls back to a formatted timestamp. Returns None when the platform
1598
1684
  /// signal cannot be read — the caller MUST NOT guess; downstream
1599
1685
  /// treats a missing/mismatched host boot separately from a matched one.
1686
+ /// ---
1687
+ /// purpose: 探测当前主机的启动身份,用来识别「所有 pane/pid/会话绑定都早于本次开机」
1688
+ /// returns: Linux 读 boot_id 得 linux-<uuid>;macOS 读 kern.boottime 得 macos-<...>;测试可用 TEAM_AGENT_TEST_HOST_BOOT_ID 覆盖。平台信号读不到时为 None——调用方必须把「读不到」与「不匹配」分开处理,不许猜
1689
+ /// ---
1600
1690
  pub(crate) fn probe_host_boot_id() -> Option<String> {
1601
1691
  if let Ok(override_id) = std::env::var("TEAM_AGENT_TEST_HOST_BOOT_ID") {
1602
1692
  if !override_id.is_empty() {
@@ -1634,6 +1724,12 @@ pub(crate) fn probe_host_boot_id() -> Option<String> {
1634
1724
  /// error (missing file, malformed JSON) returns None — callers treat
1635
1725
  /// that as "unknown, fall back to existing facts" per locate §8 risk
1636
1726
  /// note, they must never guess a stale/fresh verdict from absence.
1727
+ /// ---
1728
+ /// purpose: 只读取出最近一次写入的心跳 sidecar
1729
+ /// params:
1730
+ /// workspace: workspace 根
1731
+ /// returns: 解析成功才有值;文件缺失或 JSON 损坏一律 None。调用方须把 None 当作「未知」回落到既有事实,不得据此断定新鲜或陈旧
1732
+ /// ---
1637
1733
  pub fn read_coordinator_heartbeat(workspace: &WorkspacePath) -> Option<Value> {
1638
1734
  let path = coordinator_heartbeat_path(workspace);
1639
1735
  let text = std::fs::read_to_string(path).ok()?;
@@ -2529,6 +2625,13 @@ fn write_activity(
2529
2625
  /// conflict (or probe unavailable + activity idle).
2530
2626
  /// 5. Unknown — missing pane_pid, probe error, or no decisive
2531
2627
  /// signal. Iron law: never silently Idle.
2628
+ /// ---
2629
+ /// purpose: 由 agent 状态与活动分类结果解析出 worker 的运行态
2630
+ /// params:
2631
+ /// agent: agent 状态对象;读 pane_pid、awaiting_human_confirm 与审批/信任标志
2632
+ /// activity: JSONL 活动分类结果;None 表示本轮没有分类信号
2633
+ /// returns: 按优先级判定——先 Blocked(等人确认/等信任提示),再用前台进程组探测判 Busy,再由活动分类给 Busy/ProbablyIdle;任何一步缺信号都落到 Unknown。铁律是绝不在信息不足时静默判 Idle
2634
+ /// ---
2532
2635
  pub(crate) fn resolve_worker_runtime_state_with_fg_pgrp(
2533
2636
  agent: &Value,
2534
2637
  activity: Option<&crate::messaging::AgentActivity>,
@@ -2612,6 +2715,14 @@ fn agent_health_status_wire(status: crate::messaging::ActivityStatus) -> &'stati
2612
2715
  /// 0.4.x Phase 1: read the worker_state wire string that `write_activity`
2613
2716
  /// persisted alongside the legacy activity. Returns None when no worker_state
2614
2717
  /// has been written yet (older state row pre-upgrade).
2718
+ /// ---
2719
+ /// purpose: 读回 write_activity 与活动一起持久化的 worker_state 线字符串
2720
+ /// params:
2721
+ /// agent: agent 状态对象
2722
+ /// returns: 已写过 worker_state 时解析出运行态;升级前的旧状态行没有该字段,返回 None
2723
+ /// ---
2724
+ /// 现状:本仓内没有任何调用点(仅定义),与其他 pub(crate) 项不同——按机器事实记录,
2725
+ /// 不据此推断它「已接线」。
2615
2726
  pub(crate) fn agent_worker_state(agent: &Value) -> Option<crate::messaging::WorkerRuntimeState> {
2616
2727
  agent
2617
2728
  .get("worker_state")