@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,4 +1,46 @@
1
- //!
1
+ //! ---
2
+ //! purpose: 各 provider 共用的通用候选扫描与解析底座——枚举可能的会话文件、读头解析、按进程世代与身份 marker 过滤
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: candidate_session_files
6
+ //! what: 按 provider 枚举候选文件(隔离根 + spawn_cwd 递归 + provider HOME 目录),去重排序并按 mtime 截到 300 条
7
+ //! - name: parse_candidate_files
8
+ //! what: 逐个读头 64KB 解析成 CapturedSessionCandidate,途中套 cwd 匹配、embedded 身份、claude cwd 字段与 leader marker 四道排除
9
+ //! - name: apply_spawned_at_filter
10
+ //! what: 剔除早于席位进程世代边界(spawned_at)的存档;spawned_at 存在但解析不了则清空候选
11
+ //! - name: apply_spawn_time_window_if_unique
12
+ //! what: 只有当"落在 spawn 之后"的候选恰好剩一条时才据此收窄,否则不动
13
+ //! - name: sort_expected_first_if_needed
14
+ //! what: 有 pending id 时把命中的候选排到最前
15
+ //! - name: read_head_text
16
+ //! what: 读文件头若干字节并截到最后一个完整行
17
+ //! - name: parse_session_records
18
+ //! what: 先按整体 JSON 解析(数组/单值),失败再按 JSONL 逐行解析
19
+ //! - name: embedded_team_agent_worker_id_from_text
20
+ //! what: 从正文里抽出 "You are Team Agent worker `<id>`" 的 id,字符集受限校验
21
+ //! - name: rollout_path_embedded_team_agent_worker_id
22
+ //! what: 上一条的按路径版本(读头后再抽)
23
+ //! - name: record_cwd
24
+ //! what: 从记录里取 cwd(顶层 cwd 或 session_meta.payload.cwd / payload.cwd)
25
+ //! - name: paths_equivalent
26
+ //! what: 路径等价判定:相等、canonicalize 后相等、或左的父目录等于右
27
+ //! - name: CAPTURE_HEAD_BYTES
28
+ //! what: 统一的读头窗口 64KB
29
+ //! requires:
30
+ //! - name: crate::provider::helpers
31
+ //! what: find_session_id 与 parse_jsonl_records
32
+ //! - name: super::claude
33
+ //! what: claude 家族的 projects 目录推导、cwd 字段判据与 leader marker 判据
34
+ //! - name: super::codex
35
+ //! what: spawned_at 解析、uuid 精度截断与 rollout 创建时间
36
+ //! boundary:
37
+ //! - 一律只读:不写盘、不改 state、不发事件
38
+ //! - 只产出候选与事实;归属最终由上游分配器裁定,本文件不写会话
39
+ //! - spawn_cwd 递归最深 4 层,且跳过 .team 运行时目录(claude 的隔离根例外)
40
+ //! - 候选上限 300 条,超出按 mtime 由新到旧截断——截掉的部分不留痕迹
41
+ //! - "看起来像会话文件"的判据很宽(.json/.jsonl 后缀,或名字含 session/rollout/席位 id),收窄靠后续 provider 专属过滤
42
+ //! maturity: wired
43
+ //! ---
2
44
  use std::path::{Path, PathBuf};
3
45
 
4
46
  use crate::provider::helpers::{find_session_id, parse_jsonl_records};
@@ -21,6 +63,23 @@ const CAPTURE_CANDIDATE_CAP: usize = 300;
21
63
  /// P2 (C-P2-1): head window >= Python's 200-line read.
22
64
  pub(super) const CAPTURE_HEAD_BYTES: u64 = 65_536;
23
65
 
66
+ /// ---
67
+ /// purpose: 枚举本席位可能对应的会话文件,产出待解析的候选路径表
68
+ /// params:
69
+ /// provider: 决定要不要去 ~/.codex/sessions 或 ~/.claude/projects 找
70
+ /// context: provider_projects_root(隔离根)、spawn_cwd(必扫)、agent_id(参与文件名匹配)
71
+ /// returns: 去重并排序后的候选;requires_cwd_match=false 的排前面,同组按路径字典序
72
+ /// errors: 只有 spawn_cwd 这一层根目录读不开才上抛 Io;provider HOME 侧目录读失败静默跳过
73
+ /// contract:
74
+ /// provides:
75
+ /// - name: candidate_session_files
76
+ /// what: 只列路径,不打开文件内容(截断时会取 mtime)
77
+ /// boundary:
78
+ /// - spawn_cwd 递归最深 4 层;工作区是用户代码仓,任何名含 session 的 json 都会被列进来
79
+ /// - .team 运行时目录一律排除,除非它就在 claude 的隔离根之下
80
+ /// - 超过 300 条按 mtime 由新到旧截断;被截掉的候选不产生任何信号
81
+ /// - Copilot / Grok / CursorAgent / GeminiCli / Fake 不走 HOME 分支——它们各有专属扫描
82
+ /// ---
24
83
  pub(super) fn candidate_session_files(
25
84
  provider: Provider,
26
85
  context: &CaptureSessionContext,
@@ -60,7 +119,11 @@ pub(super) fn candidate_session_files(
60
119
  &mut out,
61
120
  )?;
62
121
  }
63
- Provider::Copilot | Provider::GeminiCli | Provider::Fake => {}
122
+ Provider::Copilot
123
+ | Provider::Grok
124
+ | Provider::CursorAgent
125
+ | Provider::GeminiCli
126
+ | Provider::Fake => {}
64
127
  }
65
128
  }
66
129
  out.sort_by(|a, b| {
@@ -73,6 +136,22 @@ pub(super) fn candidate_session_files(
73
136
  Ok(out)
74
137
  }
75
138
 
139
+ /// ---
140
+ /// purpose: 把候选路径读头解析成结构化候选,并在解析途中就滤掉明显不属于本席位的
141
+ /// params:
142
+ /// provider: 决定是否套用 claude 专属的两道排除
143
+ /// context: 提供 spawn_cwd 与 agent_id 两个比对基准
144
+ /// candidates: candidate_session_files 的产物
145
+ /// returns: 通过全部排除的候选;每条带 embedded 身份、positive_agent_id_match 与 agent_path_match
146
+ /// contract:
147
+ /// provides:
148
+ /// - name: parse_candidate_files
149
+ /// what: 每个文件只读头 64KB,且只取到最后一个完整行
150
+ /// boundary:
151
+ /// - 四道排除:读不出头 / 解析不出记录 → 跳过;requires_cwd_match 的候选 cwd 对不上 → 跳过;embedded 身份存在且不等于本席位 → 跳过;claude 家族有 session id 却无 cwd 字段、或含 leader marker → 跳过
152
+ /// - 无 session id 的候选不丢弃,降级为 FsMtimeFallback + Confidence::Low
153
+ /// - 身份判定用两套文本:正文 text 判 TEAM_AGENT_ID,未截行的原始字节判 embedded worker id
154
+ /// ---
76
155
  pub(super) fn parse_candidate_files(
77
156
  provider: Provider,
78
157
  context: &CaptureSessionContext,
@@ -144,6 +223,21 @@ pub(super) fn parse_candidate_files(
144
223
  }
145
224
 
146
225
  /// Reject provider files older than the persisted process-cohort boundary.
226
+ /// ---
227
+ /// purpose: 用席位的进程世代边界剔除上一代/别人留下的旧存档
228
+ /// params:
229
+ /// provider: Codex 走 rollout 创建时间,其余走文件 mtime
230
+ /// context: spawned_at 缺失则不过滤(legacy 行);expected id 存在且是 Codex 时把边界截到毫秒
231
+ /// out: 就地过滤的候选列表
232
+ /// contract:
233
+ /// provides:
234
+ /// - name: apply_spawned_at_filter
235
+ /// what: fail-closed —— spawned_at 存在但解析不了(或截断失败)时清空候选,绝不放行
236
+ /// boundary:
237
+ /// - 无 spawned_at 时整体跳过过滤,把守卫让给分配器的身份与唯一性判据
238
+ /// - 无 rollout_path、取不到时间的候选一律剔除
239
+ /// - Codex 刻意不用 mtime:追加写与邻席活动都会抬升 mtime
240
+ /// ---
147
241
  pub(super) fn apply_spawned_at_filter(
148
242
  provider: Provider,
149
243
  context: &CaptureSessionContext,
@@ -186,6 +280,20 @@ fn candidate_mtime(path: &Path) -> Option<std::time::SystemTime> {
186
280
  .ok()
187
281
  }
188
282
 
283
+ /// ---
284
+ /// purpose: 在时间窗只能唯一确定一条候选时才据此收窄,避免用弱判据在多条候选间乱挑
285
+ /// params:
286
+ /// provider: Codex 用 rollout 创建时间,其余用 mtime
287
+ /// context: 需要 spawned_at;有 expected id 且候选不超过 1 条时直接不动
288
+ /// out: 就地可能被替换的候选列表
289
+ /// contract:
290
+ /// provides:
291
+ /// - name: apply_spawn_time_window_if_unique
292
+ /// what: 只在窗内恰好剩 1 条时替换整表;0 条或 ≥2 条一律保持原样
293
+ /// boundary:
294
+ /// - 无 spawned_at 或解析失败则不过滤(与 apply_spawned_at_filter 的 fail-closed 相反,此处是不作为)
295
+ /// - 不排序、不去重;只做"唯一即取"的收窄
296
+ /// ---
189
297
  pub(super) fn apply_spawn_time_window_if_unique(
190
298
  provider: Provider,
191
299
  context: &CaptureSessionContext,
@@ -221,6 +329,18 @@ pub(super) fn apply_spawn_time_window_if_unique(
221
329
  }
222
330
  }
223
331
 
332
+ /// ---
333
+ /// purpose: 有 pending id 时把命中的候选排到最前,让下游取首条即取到期望的那个
334
+ /// params:
335
+ /// context: 无 expected_session_id 则不动
336
+ /// out: 就地排序的候选切片
337
+ /// contract:
338
+ /// provides:
339
+ /// - name: sort_expected_first_if_needed
340
+ /// what: 稳定排序,只按"是否命中 expected"分两档,不改组内相对次序
341
+ /// boundary:
342
+ /// - 只排序不过滤——未命中的候选仍在表里
343
+ /// ---
224
344
  pub(super) fn sort_expected_first_if_needed(
225
345
  context: &CaptureSessionContext,
226
346
  out: &mut [CapturedSessionCandidate],
@@ -264,6 +384,22 @@ fn cap_candidates_by_mtime(out: &mut Vec<SessionCandidate>, cap: usize) {
264
384
  });
265
385
  }
266
386
 
387
+ /// ---
388
+ /// purpose: 读文件头若干字节并截到最后一个完整行,供各 provider 解析记录
389
+ /// params:
390
+ /// path: 待读文件
391
+ /// max_bytes: 读取上限;全模块统一用 CAPTURE_HEAD_BYTES(64KB)
392
+ /// returns: 到最后一个换行为止的文本;整段无换行时返回读到的全部
393
+ /// errors: 打不开或读失败时上抛 io::Error
394
+ /// contract:
395
+ /// provides:
396
+ /// - name: read_head_text
397
+ /// what: 只读、不加锁、不改 mtime
398
+ /// boundary:
399
+ /// - 刻意丢弃末尾不完整的一行——半条 JSON 会让解析器把整段判废
400
+ /// - 非 UTF-8 字节按 lossy 转换,不报错
401
+ /// - 只看头部;窗口之外的内容对所有基于本函数的判据都不可见
402
+ /// ---
267
403
  pub(super) fn read_head_text(path: &Path, max_bytes: u64) -> std::io::Result<String> {
268
404
  read_head_bytes(path, max_bytes).map(|bytes| complete_head_text(&bytes))
269
405
  }
@@ -352,6 +488,18 @@ fn looks_like_session_file(path: &Path, agent_id: &str, allowed_team_root: Optio
352
488
  || (!agent_id.is_empty() && name.contains(agent_id))
353
489
  }
354
490
 
491
+ /// ---
492
+ /// purpose: 把会话文本解析成记录数组,兼容整体 JSON 与 JSONL 两种存档形态
493
+ /// params:
494
+ /// text: 已读入的文本(通常是 read_head_text 的产物)
495
+ /// returns: JSON 数组则展开;JSON 单值则包成一条;都不是则按 JSONL 逐行解析
496
+ /// contract:
497
+ /// provides:
498
+ /// - name: parse_session_records
499
+ /// what: 纯解析,无 I/O
500
+ /// boundary:
501
+ /// - JSONL 分支跳过解析失败的行,不报错——因此"记录为空"不区分"文件空"与"全是坏行"
502
+ /// ---
355
503
  pub(super) fn parse_session_records(text: &str) -> Vec<serde_json::Value> {
356
504
  match serde_json::from_str::<serde_json::Value>(text) {
357
505
  Ok(serde_json::Value::Array(items)) => items,
@@ -387,6 +535,20 @@ fn candidate_text_has_team_agent_id(text: &str, context: &CaptureSessionContext)
387
535
  .any(|needle| text.contains(needle))
388
536
  }
389
537
 
538
+ /// ---
539
+ /// purpose: 从会话正文里抽出 team-agent 写进 prompt 的席位身份 marker,作为归属的强证据
540
+ /// params:
541
+ /// text: 待搜索的文本
542
+ /// returns: marker 后反引号之间的 id;未找到、为空或含非法字符时 None
543
+ /// contract:
544
+ /// provides:
545
+ /// - name: embedded_team_agent_worker_id_from_text
546
+ /// what: 纯文本查找,无 I/O
547
+ /// boundary:
548
+ /// - 只取第一处 marker;后续出现的一律忽略
549
+ /// - id 字符集限定为 ASCII 字母数字与 _ - .;越界即判无
550
+ /// - 找不到只说明"这段文本里没有",不等于该会话不属于任何席位
551
+ /// ---
390
552
  pub(crate) fn embedded_team_agent_worker_id_from_text(text: &str) -> Option<String> {
391
553
  const PREFIX: &str = "You are Team Agent worker `";
392
554
  let start = text.find(PREFIX)? + PREFIX.len();
@@ -403,6 +565,19 @@ pub(crate) fn embedded_team_agent_worker_id_from_text(text: &str) -> Option<Stri
403
565
  Some(id.to_string())
404
566
  }
405
567
 
568
+ /// ---
569
+ /// purpose: 按路径取该存档的 embedded 席位身份,供捕获链外的调用方直接查归属
570
+ /// params:
571
+ /// path: 存档文件路径
572
+ /// returns: 读头 64KB 后抽到的席位 id;读失败或没抽到给 None
573
+ /// contract:
574
+ /// provides:
575
+ /// - name: rollout_path_embedded_team_agent_worker_id
576
+ /// what: read_head_text + embedded_team_agent_worker_id_from_text 的组合,只读
577
+ /// boundary:
578
+ /// - 读失败与"文件里确实没有 marker"都返回 None,两者不可区分
579
+ /// - marker 若出现在 64KB 之后则看不见
580
+ /// ---
406
581
  pub(crate) fn rollout_path_embedded_team_agent_worker_id(path: &Path) -> Option<String> {
407
582
  read_head_text(path, CAPTURE_HEAD_BYTES)
408
583
  .ok()
@@ -421,6 +596,18 @@ fn candidate_path_matches_agent_id(path: &Path, context: &CaptureSessionContext)
421
596
  name.contains(id) || name.contains(&dashed)
422
597
  }
423
598
 
599
+ /// ---
600
+ /// purpose: 从一条会话记录里取工作目录,兼容 claude 顶层 cwd 与 codex session_meta.payload.cwd 两种形状
601
+ /// params:
602
+ /// record: 单条已解析记录
603
+ /// returns: 顶层 cwd 优先;否则取 session_meta.payload.cwd 或 payload.cwd;都没有则 None
604
+ /// contract:
605
+ /// provides:
606
+ /// - name: record_cwd
607
+ /// what: 纯取值,不做路径归一或存在性检查
608
+ /// boundary:
609
+ /// - 只认这三个位置;其它自定义字段一概不看
610
+ /// ---
424
611
  pub(super) fn record_cwd(record: &serde_json::Value) -> Option<String> {
425
612
  record
426
613
  .get("cwd")
@@ -436,6 +623,21 @@ pub(super) fn record_cwd(record: &serde_json::Value) -> Option<String> {
436
623
  .map(ToString::to_string)
437
624
  }
438
625
 
626
+ /// ---
627
+ /// purpose: 判断记录里的 cwd 与席位 spawn_cwd 是不是同一个工作目录
628
+ /// params:
629
+ /// left: 记录里的 cwd
630
+ /// right: 席位 spawn_cwd
631
+ /// returns: 字面相等、canonicalize 后相等、或 left 的父目录等于 right 时为 true
632
+ /// contract:
633
+ /// provides:
634
+ /// - name: paths_equivalent
635
+ /// what: 会触发文件系统 canonicalize;失败则退回原路径继续比
636
+ /// boundary:
637
+ /// - 父目录等价是刻意放宽的一档:记录 cwd 是 spawn_cwd 的直接子目录也算同 cwd,嵌套 workspace 下会把子目录的存档收进来
638
+ /// - 不对称:只查 left 的父等于 right,反向不查
639
+ /// - 不解析符号链接之外的等价(如大小写不敏感文件系统的同名不同写)
640
+ /// ---
439
641
  pub(super) fn paths_equivalent(left: &Path, right: &Path) -> bool {
440
642
  if left == right {
441
643
  return true;
@@ -1,10 +1,42 @@
1
- //!
1
+ //! ---
2
+ //! purpose: 只按 pending session id 到 copilot 的 session-store.db 里查一行,查得到才算捕获
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: scan_session_store
6
+ //! what: 有 expected_session_id 且 sessions 表里存在该 id 时返回唯一候选,否则空
7
+ //! requires:
8
+ //! - name: crate::provider::adapters::copilot_fork::copilot_home
9
+ //! what: session-store.db 的位置由 copilot adapter 决定,本文件不自行推导 HOME
10
+ //! - name: rusqlite
11
+ //! what: 只读打开 sqlite,不建表、不写入
12
+ //! boundary:
13
+ //! - 只服务 Provider::Copilot
14
+ //! - 无 pending id 一律返回空——绝不按 cwd/时间挑「最近那个会话」
15
+ //! - 候选的 rollout_path 是共享的 session-store.db 本身,不是每席位一份的文件
16
+ //! - embedded_agent_id 恒 None:copilot 存档里没有 team-agent 身份 marker 可读
17
+ //! - 打不开 HOME / 库不存在 / SQL 失败一律退成空向量,不上抛错误
18
+ //! maturity: wired
19
+ //! ---
2
20
  use std::path::Path;
3
21
 
4
22
  use crate::provider::types::{CaptureVia, CapturedSession, Confidence, RolloutPath, SessionId};
5
23
 
6
24
  use super::{CaptureSessionContext, CapturedSessionCandidate};
7
25
 
26
+ /// ---
27
+ /// purpose: 按 pending id 在 copilot session-store.db 的 sessions 表里做一次存在性查询
28
+ /// params:
29
+ /// context: 只用到 expected_session_id 与 spawn_cwd(后者仅原样写进候选)
30
+ /// returns: 命中给一条 FsWatch/High 候选,rollout_path 指向 session-store.db;未命中或无 pending id 给空向量
31
+ /// contract:
32
+ /// provides:
33
+ /// - name: scan_session_store
34
+ /// what: 只读 sqlite 一行 id,不读会话正文、不按 cwd 过滤
35
+ /// boundary:
36
+ /// - 无 expected_session_id 直接返回空,不做任何回落挑选
37
+ /// - 不校验该会话的 cwd 是否等于 spawn_cwd——库里 cwd 列存在但本函数不用
38
+ /// - 所有失败路径都退成空向量,调用方无法区分「库不存在」与「查无此行」
39
+ /// ---
8
40
  pub(super) fn scan_session_store(context: &CaptureSessionContext) -> Vec<CapturedSessionCandidate> {
9
41
  let Ok(home) = crate::provider::adapters::copilot_fork::copilot_home() else {
10
42
  return Vec::new();