@team-agent/installer 0.5.60 → 0.5.62

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 (265) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +49 -3
  4. package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +184 -0
  6. package/crates/team-agent/src/cli/emit.rs +104 -21
  7. package/crates/team-agent/src/cli/helpers.rs +1 -0
  8. package/crates/team-agent/src/cli/leader.rs +14 -2
  9. package/crates/team-agent/src/cli/leaders.rs +2 -0
  10. package/crates/team-agent/src/cli/mod.rs +296 -8
  11. package/crates/team-agent/src/cli/named_address.rs +26 -2
  12. package/crates/team-agent/src/cli/profile.rs +2 -1
  13. package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
  14. package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
  15. package/crates/team-agent/src/cli/send/persist.rs +1 -0
  16. package/crates/team-agent/src/cli/send/presentation.rs +3 -0
  17. package/crates/team-agent/src/cli/send/resolve.rs +206 -1
  18. package/crates/team-agent/src/cli/send.rs +2 -1
  19. package/crates/team-agent/src/cli/spec.rs +5 -1
  20. package/crates/team-agent/src/cli/status.rs +2 -1
  21. package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
  22. package/crates/team-agent/src/cli/status_port/format.rs +1 -0
  23. package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
  24. package/crates/team-agent/src/cli/status_port/store.rs +7 -3
  25. package/crates/team-agent/src/cli/tests/named_address.rs +65 -0
  26. package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
  27. package/crates/team-agent/src/cli/types.rs +36 -2
  28. package/crates/team-agent/src/communication_mode/mod.rs +54 -0
  29. package/crates/team-agent/src/compiler/tests.rs +5 -5
  30. package/crates/team-agent/src/compiler.rs +54 -1
  31. package/crates/team-agent/src/conpty/backend.rs +1 -0
  32. package/crates/team-agent/src/conpty/mod.rs +1 -0
  33. package/crates/team-agent/src/coordinator/backoff.rs +5 -8
  34. package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
  35. package/crates/team-agent/src/coordinator/health.rs +1 -0
  36. package/crates/team-agent/src/coordinator/mod.rs +2 -2
  37. package/crates/team-agent/src/coordinator/orphan.rs +4 -116
  38. package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
  39. package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
  40. package/crates/team-agent/src/coordinator/steps/abnormal.rs +6 -5
  41. package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
  42. package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
  43. package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
  44. package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
  45. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
  46. package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
  47. package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
  48. package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +0 -9
  49. package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
  50. package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
  51. package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -42
  52. package/crates/team-agent/src/coordinator/tick.rs +24 -28
  53. package/crates/team-agent/src/coordinator/types.rs +3 -176
  54. package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
  55. package/crates/team-agent/src/db/message_store.rs +47 -2
  56. package/crates/team-agent/src/db/migration.rs +5 -4
  57. package/crates/team-agent/src/db/mod.rs +1 -0
  58. package/crates/team-agent/src/db/schema.rs +1 -0
  59. package/crates/team-agent/src/diagnose/comms.rs +2 -1
  60. package/crates/team-agent/src/diagnose/mod.rs +1 -0
  61. package/crates/team-agent/src/diagnose/orphans.rs +1 -0
  62. package/crates/team-agent/src/fake_worker.rs +21 -1
  63. package/crates/team-agent/src/layout/manager.rs +3 -7
  64. package/crates/team-agent/src/layout/mod.rs +2 -2
  65. package/crates/team-agent/src/layout/overlay.rs +6 -1
  66. package/crates/team-agent/src/layout/placement.rs +1 -0
  67. package/crates/team-agent/src/layout/recovery.rs +3 -0
  68. package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
  69. package/crates/team-agent/src/layout/sessions.rs +8 -1
  70. package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
  71. package/crates/team-agent/src/layout/worker_env.rs +3 -0
  72. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  73. package/crates/team-agent/src/leader/helpers.rs +1 -0
  74. package/crates/team-agent/src/leader/incident.rs +1 -0
  75. package/crates/team-agent/src/leader/lease.rs +27 -22
  76. package/crates/team-agent/src/leader/mod.rs +2 -3
  77. package/crates/team-agent/src/leader/owner_bind.rs +13 -3
  78. package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
  79. package/crates/team-agent/src/leader/rediscover.rs +4 -17
  80. package/crates/team-agent/src/leader/registry.rs +3 -15
  81. package/crates/team-agent/src/leader/start.rs +1072 -186
  82. package/crates/team-agent/src/leader/takeover.rs +10 -232
  83. package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
  84. package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
  85. package/crates/team-agent/src/leader/tests/identity.rs +71 -59
  86. package/crates/team-agent/src/leader/tests/identity_session_names.rs +42 -0
  87. package/crates/team-agent/src/leader/tests/idle.rs +0 -193
  88. package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
  89. package/crates/team-agent/src/leader/tests/mod.rs +4 -41
  90. package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
  91. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
  92. package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
  93. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
  94. package/crates/team-agent/src/leader/types.rs +1 -28
  95. package/crates/team-agent/src/lib.rs +7 -0
  96. package/crates/team-agent/src/lifecycle/display.rs +7 -1
  97. package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
  98. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
  99. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
  100. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +1 -1
  101. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
  102. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
  103. package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
  104. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
  105. package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
  106. package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -28
  107. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
  108. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
  109. package/crates/team-agent/src/lifecycle/launch.rs +11 -12
  110. package/crates/team-agent/src/lifecycle/mod.rs +2 -1
  111. package/crates/team-agent/src/lifecycle/restart/agent.rs +4 -4
  112. package/crates/team-agent/src/lifecycle/restart/common.rs +1 -1
  113. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
  114. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
  115. package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
  116. package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
  117. package/crates/team-agent/src/lifecycle/restart.rs +13 -11
  118. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
  119. package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
  120. package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
  121. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
  122. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
  123. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
  124. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
  125. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
  126. package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
  127. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
  128. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
  129. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
  130. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
  131. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
  132. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
  133. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
  134. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
  135. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
  136. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
  137. package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
  138. package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
  139. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
  140. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
  141. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
  142. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
  143. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
  144. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
  145. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
  146. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
  147. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
  148. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
  149. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
  150. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
  151. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
  152. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
  153. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
  154. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
  155. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
  156. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
  157. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
  158. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
  159. package/crates/team-agent/src/lifecycle/tests.rs +59 -0
  160. package/crates/team-agent/src/lifecycle/types.rs +0 -9
  161. package/crates/team-agent/src/lifecycle/worker_command_context.rs +29 -11
  162. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  163. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
  164. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
  165. package/crates/team-agent/src/mcp_server/mod.rs +1 -0
  166. package/crates/team-agent/src/mcp_server/normalize.rs +5 -3
  167. package/crates/team-agent/src/mcp_server/tests/send.rs +26 -0
  168. package/crates/team-agent/src/mcp_server/tests/wire.rs +1 -1
  169. package/crates/team-agent/src/mcp_server/tools.rs +101 -66
  170. package/crates/team-agent/src/mcp_server/types.rs +6 -18
  171. package/crates/team-agent/src/mcp_server/wire.rs +32 -26
  172. package/crates/team-agent/src/messaging/activity.rs +1 -0
  173. package/crates/team-agent/src/messaging/address.rs +1 -0
  174. package/crates/team-agent/src/messaging/delivery.rs +109 -41
  175. package/crates/team-agent/src/messaging/helpers.rs +2 -1
  176. package/crates/team-agent/src/messaging/leader_channel.rs +34 -10
  177. package/crates/team-agent/src/messaging/leader_receiver.rs +42 -24
  178. package/crates/team-agent/src/messaging/mod.rs +10 -14
  179. package/crates/team-agent/src/messaging/peers.rs +2 -0
  180. package/crates/team-agent/src/messaging/persist.rs +3 -1
  181. package/crates/team-agent/src/messaging/presentation.rs +111 -1
  182. package/crates/team-agent/src/messaging/results.rs +166 -17
  183. package/crates/team-agent/src/messaging/scheduler.rs +1 -0
  184. package/crates/team-agent/src/messaging/selftest.rs +1 -1
  185. package/crates/team-agent/src/messaging/send.rs +135 -89
  186. package/crates/team-agent/src/messaging/tests/leader_channel.rs +6 -6
  187. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +187 -0
  188. package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
  189. package/crates/team-agent/src/messaging/tests/runtime.rs +13 -6
  190. package/crates/team-agent/src/messaging/types.rs +7 -2
  191. package/crates/team-agent/src/messaging/watchers.rs +24 -13
  192. package/crates/team-agent/src/model/enums.rs +1 -0
  193. package/crates/team-agent/src/model/errors.rs +1 -0
  194. package/crates/team-agent/src/model/ids.rs +1 -0
  195. package/crates/team-agent/src/model/mod.rs +2 -0
  196. package/crates/team-agent/src/model/name_similarity.rs +1 -0
  197. package/crates/team-agent/src/model/pane_authority_refusal.rs +359 -0
  198. package/crates/team-agent/src/model/paths.rs +1 -0
  199. package/crates/team-agent/src/model/permissions.rs +3 -2
  200. package/crates/team-agent/src/model/routing.rs +1 -0
  201. package/crates/team-agent/src/model/spec.rs +17 -0
  202. package/crates/team-agent/src/model/task_graph.rs +1 -0
  203. package/crates/team-agent/src/model/yaml/tests.rs +1 -0
  204. package/crates/team-agent/src/model/yaml.rs +1 -0
  205. package/crates/team-agent/src/packaging/install.rs +9 -147
  206. package/crates/team-agent/src/packaging/migrate.rs +2 -0
  207. package/crates/team-agent/src/packaging/mod.rs +1 -0
  208. package/crates/team-agent/src/packaging/repair.rs +2 -0
  209. package/crates/team-agent/src/packaging/tests.rs +29 -218
  210. package/crates/team-agent/src/packaging/types.rs +8 -15
  211. package/crates/team-agent/src/platform/argv.rs +4 -0
  212. package/crates/team-agent/src/platform/errors.rs +10 -1
  213. package/crates/team-agent/src/platform/file_lock.rs +4 -157
  214. package/crates/team-agent/src/platform/mod.rs +0 -61
  215. package/crates/team-agent/src/platform/process.rs +1 -0
  216. package/crates/team-agent/src/provider/adapter.rs +1 -0
  217. package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
  218. package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
  219. package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
  220. package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
  221. package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
  222. package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
  223. package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
  224. package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
  225. package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
  226. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
  227. package/crates/team-agent/src/provider/classify.rs +1 -0
  228. package/crates/team-agent/src/provider/faults.rs +1 -26
  229. package/crates/team-agent/src/provider/helpers.rs +1 -0
  230. package/crates/team-agent/src/provider/mod.rs +1 -1
  231. package/crates/team-agent/src/provider/session/capture.rs +41 -22
  232. package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
  233. package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
  234. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
  235. package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
  236. package/crates/team-agent/src/provider/session/mod.rs +2 -4
  237. package/crates/team-agent/src/provider/session/resume.rs +2 -209
  238. package/crates/team-agent/src/provider/session_scan/claude.rs +93 -3
  239. package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
  240. package/crates/team-agent/src/provider/session_scan/common.rs +24 -7
  241. package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
  242. package/crates/team-agent/src/provider/session_scan.rs +6 -42
  243. package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
  244. package/crates/team-agent/src/provider/types.rs +1 -0
  245. package/crates/team-agent/src/provider/wire.rs +1 -0
  246. package/crates/team-agent/src/state/identity.rs +16 -5
  247. package/crates/team-agent/src/state/identity_keys.rs +1 -0
  248. package/crates/team-agent/src/state/mod.rs +2 -0
  249. package/crates/team-agent/src/state/owner_gate.rs +5 -0
  250. package/crates/team-agent/src/state/ownership.rs +10 -4
  251. package/crates/team-agent/src/state/paths.rs +12 -0
  252. package/crates/team-agent/src/state/persist.rs +9 -3
  253. package/crates/team-agent/src/state/projection.rs +15 -2
  254. package/crates/team-agent/src/state/repository/intent.rs +1 -0
  255. package/crates/team-agent/src/state/repository/tests.rs +1 -0
  256. package/crates/team-agent/src/state/repository.rs +7 -0
  257. package/crates/team-agent/src/state/selector.rs +1 -0
  258. package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
  259. package/crates/team-agent/src/tmux_backend.rs +63 -0
  260. package/crates/team-agent/src/topology.rs +3 -0
  261. package/crates/team-agent/src/transport.rs +15 -0
  262. package/package.json +4 -4
  263. package/skills/team-agent/command-coverage.json +379 -0
  264. package/crates/team-agent/src/leader/inject.rs +0 -33
  265. package/crates/team-agent/src/provider/command.rs +0 -80
package/Cargo.lock CHANGED
@@ -575,7 +575,7 @@ dependencies = [
575
575
 
576
576
  [[package]]
577
577
  name = "team-agent"
578
- version = "0.5.60"
578
+ version = "0.5.62"
579
579
  dependencies = [
580
580
  "anyhow",
581
581
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-trans
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.5.60"
12
+ version = "0.5.62"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! cli · adapters — 每子命令的薄壳 `cmd_*`(commands.py)。委派 status/lifecycle/diagnose/
2
3
  //! leader/messaging port,把委派结果包成 [`CmdResult`]。含逻辑的:`cmd_status`(三态互斥)、
3
4
  //! `cmd_doctor`(gate/comms/fix-schema/cleanup-orphans 分派)。
@@ -221,7 +222,8 @@ fn warn_ignored_owner_team_id(team_dir: &std::path::Path) {
221
222
 
222
223
  /// `cmd_status`(`commands.py:90`)。三态:`--summary`(xor json,xor agent)→五行文本;
223
224
  /// `--json`→`status_port::status(compact=!detail)`;else→`status_port::format_status(agent)`。
224
- pub fn cmd_status(args: &StatusArgs) -> Result<CmdResult, CliError> {
225
+ #[cfg(test)]
226
+ pub(crate) fn cmd_status(args: &StatusArgs) -> Result<CmdResult, CliError> {
225
227
  cmd_status_for_team(args, args.team.as_deref())
226
228
  }
227
229
 
@@ -408,7 +410,8 @@ pub fn cmd_sessions(args: &SessionsArgs) -> Result<CmdResult, CliError> {
408
410
  }
409
411
 
410
412
  /// `cmd_collect`(`parser.py:292`)。
411
- pub fn cmd_collect(args: &CollectArgs) -> Result<CmdResult, CliError> {
413
+ #[cfg(test)]
414
+ pub(crate) fn cmd_collect(args: &CollectArgs) -> Result<CmdResult, CliError> {
412
415
  cmd_collect_for_team(args, args.team.as_deref())
413
416
  }
414
417
 
@@ -483,6 +486,43 @@ pub fn cmd_collect_for_team(args: &CollectArgs, team: Option<&str>) -> Result<Cm
483
486
  ))
484
487
  }
485
488
 
489
+ pub fn cmd_results(args: &ResultsArgs) -> Result<CmdResult, CliError> {
490
+ let selected = match crate::state::selector::resolve_active_team(
491
+ &args.workspace,
492
+ args.team.as_deref(),
493
+ crate::state::selector::SelectorMode::RuntimeOnly,
494
+ ) {
495
+ Ok(selected) => selected,
496
+ Err(error) => {
497
+ return Ok(CmdResult::from_json(
498
+ json!({
499
+ "ok": false,
500
+ "error": error.to_string(),
501
+ "workspace": args.workspace.to_string_lossy().to_string(),
502
+ }),
503
+ args.json,
504
+ ));
505
+ }
506
+ };
507
+ let results = messaging::results::results_for_case(
508
+ &selected.run_workspace,
509
+ &args.case_id,
510
+ Some(selected.team_key.as_str()),
511
+ args.team.as_deref(),
512
+ )?;
513
+ let value = json!({
514
+ "ok": true,
515
+ "case_id": args.case_id,
516
+ "results": results,
517
+ "workspace": selected.run_workspace.to_string_lossy().to_string(),
518
+ });
519
+ Ok(if args.json {
520
+ CmdResult::from_ordered_json(value)
521
+ } else {
522
+ CmdResult::from_json(value, false)
523
+ })
524
+ }
525
+
486
526
  /// `cmd_allow_peer_talk`(`parser.py allow-peer-talk`).
487
527
  pub fn cmd_allow_peer_talk(args: &AllowPeerTalkArgs) -> Result<CmdResult, CliError> {
488
528
  if args.team.is_some() {
@@ -1485,7 +1525,13 @@ pub fn cmd_doctor(args: &DoctorArgs) -> Result<CmdResult, CliError> {
1485
1525
  } else if args.fix_schema {
1486
1526
  diagnose_port::fix_schema(&args.workspace)?
1487
1527
  } else {
1488
- diagnose_port::doctor(&args.workspace, args.spec.as_deref())?
1528
+ let mut value = diagnose_port::doctor(&args.workspace, args.spec.as_deref())?;
1529
+ crate::cli::diagnose::append_selected_live_leader_workspace_mismatch(
1530
+ &args.workspace,
1531
+ args.team.as_deref(),
1532
+ &mut value,
1533
+ );
1534
+ value
1489
1535
  };
1490
1536
  Ok(CmdResult::from_json(value, args.json))
1491
1537
  }
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use super::*;
2
3
 
3
4
  /// `team-agent attach-app-server-leader` writes a typed codex_app_server
@@ -1,5 +1,11 @@
1
+ //!
1
2
  //! diagnose/preflight/wait-ready CLI helpers.
2
3
  use super::*;
4
+ use crate::model::pane_authority_refusal::{
5
+ PaneAuthorityRecoveryAction, PaneAuthorityRecoveryHint, PaneAuthorityRefusal,
6
+ PaneAuthorityRefusalFacts, PaneAuthorityRefusalField, ACTION_FIELD, ACTION_REQUIRED_FIELD,
7
+ HINT_ACTION_FIELD, REASON_FIELD,
8
+ };
3
9
  use crate::provider::wire::{command_name, parse_provider, provider_wire};
4
10
  use crate::transport::Transport;
5
11
 
@@ -195,12 +201,190 @@ pub(crate) fn diagnose_runtime_for_workspace(
195
201
  backend: &dyn Transport,
196
202
  ) -> (Value, Value) {
197
203
  let (mut issues, mut repairs) = diagnose_runtime(state, backend);
204
+ append_live_leader_workspace_mismatch_issue(
205
+ workspace,
206
+ state,
207
+ backend,
208
+ &mut issues,
209
+ &mut repairs,
210
+ );
198
211
  append_legacy_snapshot_issue(workspace, state, &mut issues);
199
212
  append_coordinator_health_issue(workspace, state, &mut issues, &mut repairs);
200
213
  append_runtime_bindings_stale_after_boot_issue(workspace, state, &mut issues, &mut repairs);
201
214
  (issues, repairs)
202
215
  }
203
216
 
217
+ fn append_live_leader_workspace_mismatch_issue(
218
+ workspace: &std::path::Path,
219
+ state: &Value,
220
+ backend: &dyn Transport,
221
+ issues: &mut Value,
222
+ repairs: &mut Value,
223
+ ) {
224
+ let Some((issue, repair)) = live_leader_workspace_mismatch(workspace, state, backend) else {
225
+ return;
226
+ };
227
+ if let Some(items) = issues.as_array_mut() {
228
+ items.push(issue);
229
+ }
230
+ if let Some(items) = repairs.as_array_mut() {
231
+ items.push(repair);
232
+ }
233
+ }
234
+
235
+ fn live_leader_workspace_mismatch(
236
+ workspace: &std::path::Path,
237
+ state: &Value,
238
+ backend: &dyn Transport,
239
+ ) -> Option<(Value, Value)> {
240
+ let receiver = state.get("leader_receiver")?;
241
+ let facts = match crate::messaging::resolve_live_leader_channel(workspace, receiver, backend) {
242
+ crate::messaging::LeaderChannelResolution::Unbound(
243
+ crate::messaging::LeaderChannelUnbound::PaneWorkspaceMismatch(facts),
244
+ ) => facts,
245
+ _ => return None,
246
+ };
247
+ let team = state
248
+ .get("team_key")
249
+ .and_then(Value::as_str)
250
+ .filter(|team| !team.is_empty())
251
+ .unwrap_or("current");
252
+ let provider = receiver
253
+ .get("provider")
254
+ .and_then(Value::as_str)
255
+ .filter(|provider| !provider.is_empty())
256
+ .unwrap_or("claude");
257
+ let refusal =
258
+ PaneAuthorityRefusal::new(PaneAuthorityRefusalFacts::PaneWorkspaceMismatch(facts));
259
+ let PaneAuthorityRefusalFacts::PaneWorkspaceMismatch(facts) = &refusal.facts else {
260
+ return None;
261
+ };
262
+ let mut fields = serde_json::Map::new();
263
+ fields.insert(
264
+ REASON_FIELD.to_string(),
265
+ Value::String(refusal.reason().as_str().to_string()),
266
+ );
267
+ fields.insert(
268
+ PaneAuthorityRefusalField::RequestedWorkspace
269
+ .as_str()
270
+ .to_string(),
271
+ Value::String(facts.requested_workspace.to_string_lossy().into_owned()),
272
+ );
273
+ fields.insert(
274
+ PaneAuthorityRefusalField::ObservedPaneId
275
+ .as_str()
276
+ .to_string(),
277
+ Value::String(facts.observed_pane_id.clone()),
278
+ );
279
+ fields.insert(
280
+ PaneAuthorityRefusalField::ObservedPaneWorkspace
281
+ .as_str()
282
+ .to_string(),
283
+ Value::String(facts.observed_pane_workspace.to_string_lossy().into_owned()),
284
+ );
285
+ fields.insert(
286
+ PaneAuthorityRefusalField::Endpoint.as_str().to_string(),
287
+ Value::String(facts.endpoint.clone()),
288
+ );
289
+ let mut issue = Value::Object(fields.clone());
290
+ if let Some(object) = issue.as_object_mut() {
291
+ object.insert(
292
+ "id".to_string(),
293
+ Value::String("leader_channel_workspace_mismatch".to_string()),
294
+ );
295
+ }
296
+ let mut repair = Value::Object(fields);
297
+ if let Some(object) = repair.as_object_mut() {
298
+ object.insert(
299
+ "issue".to_string(),
300
+ Value::String("leader_channel_workspace_mismatch".to_string()),
301
+ );
302
+ object.insert(
303
+ ACTION_REQUIRED_FIELD.to_string(),
304
+ Value::Bool(refusal.recovery.action_required),
305
+ );
306
+ object.insert("advisory".to_string(), Value::Bool(true));
307
+ object.insert(
308
+ "broken_class".to_string(),
309
+ Value::String("leader_channel_workspace_mismatch".to_string()),
310
+ );
311
+ object.insert(
312
+ HINT_ACTION_FIELD.to_string(),
313
+ Value::String(
314
+ match refusal.recovery.hint_action {
315
+ PaneAuthorityRecoveryHint::AttachLeader => "team-agent attach-leader",
316
+ }
317
+ .to_string(),
318
+ ),
319
+ );
320
+ object.insert(
321
+ "dedupe_key".to_string(),
322
+ Value::String(format!("{team}:leader_channel_workspace_mismatch")),
323
+ );
324
+ object.insert(
325
+ ACTION_FIELD.to_string(),
326
+ Value::String(match refusal.recovery.action {
327
+ PaneAuthorityRecoveryAction::OpenTerminalOutsideCurrentTmuxPaneOrAttachFromMatchingPane => {
328
+ format!(
329
+ "open a new terminal window outside the current tmux/pane, change \
330
+ directory to the requested workspace, then run \
331
+ `team-agent attach-leader --team {team} --provider {provider} \
332
+ --confirm --json`; then rerun \
333
+ `team-agent diagnose --team {team} --json`"
334
+ )
335
+ }
336
+ }),
337
+ );
338
+ }
339
+ Some((issue, repair))
340
+ }
341
+
342
+ pub(crate) fn append_selected_live_leader_workspace_mismatch(
343
+ workspace: &std::path::Path,
344
+ team: Option<&str>,
345
+ report: &mut Value,
346
+ ) {
347
+ let Some((issue, repair)) = selected_live_leader_workspace_mismatch(workspace, team) else {
348
+ return;
349
+ };
350
+ let Some(object) = report.as_object_mut() else {
351
+ return;
352
+ };
353
+ object.insert("issues".to_string(), Value::Array(vec![issue]));
354
+ object.insert("suggested_repairs".to_string(), Value::Array(vec![repair]));
355
+ object.insert("ok".to_string(), Value::Bool(false));
356
+ if object.get("error").is_none_or(Value::is_null) {
357
+ object.insert(
358
+ "error".to_string(),
359
+ Value::String("PaneWorkspaceMismatch".to_string()),
360
+ );
361
+ }
362
+ }
363
+
364
+ fn selected_live_leader_workspace_mismatch(
365
+ workspace: &std::path::Path,
366
+ team: Option<&str>,
367
+ ) -> Option<(Value, Value)> {
368
+ let selected = crate::state::selector::resolve_active_team(
369
+ workspace,
370
+ team,
371
+ crate::state::selector::SelectorMode::RuntimeOnly,
372
+ )
373
+ .ok()?;
374
+ let backend: Box<dyn crate::transport::Transport> =
375
+ match crate::transport_factory::resolve_read_only_transport(
376
+ &selected.run_workspace,
377
+ Some(&selected.state),
378
+ crate::transport_factory::TransportPurpose::Diagnose,
379
+ ) {
380
+ Ok(resolved) => resolved.backend,
381
+ Err(_) => Box::new(crate::tmux_backend::TmuxBackend::for_workspace(
382
+ &selected.run_workspace,
383
+ )),
384
+ };
385
+ live_leader_workspace_mismatch(&selected.run_workspace, &selected.state, backend.as_ref())
386
+ }
387
+
204
388
  /// 0.5.41 Slice 1 (fault-invisibility-locate.md §5/§6.2): read the
205
389
  /// coordinator heartbeat sidecar for the last-observed host boot
206
390
  /// identity and compare it against the current host boot. If they
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! cli · emit — `emit`(--json vs 人读 dict 逐键)+ 顶层 `run` 调度(parser.py `main`)+
2
3
  //! 人读标量/集合渲染(`human_value` / `json_dumps_like`)。
3
4
 
@@ -5,17 +6,30 @@ use super::spec::{command_spec, CommandKind, CommandTier, ALL_DISPATCH_KINDS, CO
5
6
  use super::*;
6
7
  use std::io::{ErrorKind, Write as _};
7
8
 
9
+ ///
8
10
  /// `emit`(`helpers.py:12-23`):`--json`→`json.dumps(indent=2, ensure_ascii=False, sort_keys=True)`;
9
11
  /// 否则 dict 逐键 `key: value`(嵌套 dict/list 内联 compact json,`ensure_ascii=False`)、非 dict 直接 print。
10
12
  /// 返回应打印到 stdout 的字符串(bin main 负责实际 println)。
11
13
  pub fn emit(output: &CmdOutput, as_json: bool) -> Option<String> {
14
+ emit_with_json_order(output, as_json, false)
15
+ }
16
+
17
+ fn emit_with_json_order(
18
+ output: &CmdOutput,
19
+ as_json: bool,
20
+ preserve_json_order: bool,
21
+ ) -> Option<String> {
12
22
  match output {
13
23
  CmdOutput::None => None,
14
24
  CmdOutput::Human(text) => Some(crate::redaction::redact_external_text(text)),
15
25
  CmdOutput::Json(value) => {
16
26
  let value = crate::redaction::redact_external_value(value);
17
27
  if as_json {
18
- return serde_json::to_string_pretty(&sort_json(&value)).ok();
28
+ return if preserve_json_order {
29
+ serde_json::to_string_pretty(&value).ok()
30
+ } else {
31
+ serde_json::to_string_pretty(&sort_json(&value)).ok()
32
+ };
19
33
  }
20
34
  if let Value::Object(obj) = value {
21
35
  let lines: Vec<String> = obj
@@ -30,6 +44,7 @@ pub fn emit(output: &CmdOutput, as_json: bool) -> Option<String> {
30
44
  }
31
45
  }
32
46
 
47
+ ///
33
48
  /// `main(argv)`(`parser.py:84`):**CLI 唯一进程入口**。codex/claude/copilot passthrough 早返回 →
34
49
  /// 解析 argv 到 subcommand → 调对应 handler → 异常落盘 + 信封 + `ExitCode::Error` →
35
50
  /// `consume_leader_inbox_summary` → `emit` → `result.ok is False ? Error : Ok`。
@@ -84,7 +99,7 @@ fn emit_result(r: CmdResult) -> ExitCode {
84
99
  .map(ToString::to_string),
85
100
  _ => None,
86
101
  };
87
- if let Some(text) = emit(&r.output, r.as_json) {
102
+ if let Some(text) = emit_with_json_order(&r.output, r.as_json, r.preserve_json_order) {
88
103
  if let Err(error) = write_stdout_line(&text) {
89
104
  if let Some(message_id) = persisted_message_id {
90
105
  let stderr = std::io::stderr();
@@ -180,6 +195,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
180
195
  cmd_collect_for_team(&collect_args(args, cwd)?, parse_args(args).team.as_deref())
181
196
  .map(emit_result)
182
197
  }
198
+ "results" => cmd_results(&results_args(args, cwd)?).map(emit_result),
183
199
  "diagnose" => cmd_diagnose(&diagnose_args(args, cwd)).map(emit_result),
184
200
  "preflight" => cmd_preflight(&preflight_args(args, cwd)).map(emit_result),
185
201
  "wait-ready" => cmd_wait_ready(&wait_ready_args(args, cwd)).map(emit_result),
@@ -226,6 +242,7 @@ const DISPATCH_COMMANDS: &[&str] = &[
226
242
  "install-skill",
227
243
  "profile",
228
244
  "collect",
245
+ "results",
229
246
  "diagnose",
230
247
  "preflight",
231
248
  "wait-ready",
@@ -271,7 +288,7 @@ fn default_help() -> String {
271
288
  append_help_section(
272
289
  &mut out,
273
290
  "Core",
274
- &["quick-start", "send", "status", "collect"],
291
+ &["quick-start", "send", "status", "collect", "results"],
275
292
  );
276
293
  append_help_section(
277
294
  &mut out,
@@ -319,6 +336,7 @@ fn compat_hidden_help(command: &str, usage: &str) -> String {
319
336
  format!("{usage}\n\nstatus: hidden compatibility command\nsunset: {sunset}\naction: {action}")
320
337
  }
321
338
 
339
+ ///
322
340
  /// Test-only public accessor for `command_help` — allows integration
323
341
  /// tests to grep the help copy without depending on internal parser
324
342
  /// machinery.
@@ -326,6 +344,7 @@ pub fn __test_command_help(command: Option<&str>) -> String {
326
344
  command_help(command)
327
345
  }
328
346
 
347
+ ///
329
348
  /// Test-only public accessor for `quick_start_args` — allows
330
349
  /// integration tests to exercise the parser without going through
331
350
  /// stdio + the full `main` entrypoint.
@@ -346,10 +365,10 @@ fn command_help(command: Option<&str>) -> String {
346
365
  Some("send") => concat!(
347
366
  "usage: team-agent send TO MESSAGE... ",
348
367
  "[--workspace WORKSPACE] [--team TEAM] ",
349
- "[--presentation-sink leader|casefile|silent --message-class CLASS [--case-id CASE]] ",
368
+ "[--mailbox] ",
350
369
  "[--json]\n\n",
351
370
  "TO is a logical recipient; send returns after the message is persisted. ",
352
- "Presentation changes live display only; every sink remains durable."
371
+ "--mailbox stores durably without live injection; omitted sends to the live conversation."
353
372
  )
354
373
  .to_string(),
355
374
  Some("allow-peer-talk") => "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]".to_string(),
@@ -383,6 +402,7 @@ fn command_help(command: Option<&str>) -> String {
383
402
  Some("install-skill") => "usage: team-agent install-skill (--source DIR | --uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]".to_string(),
384
403
  Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--json]".to_string(),
385
404
  Some("collect") => "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]".to_string(),
405
+ Some("results") => "usage: team-agent results --case CASE_ID [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
386
406
  Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
387
407
  Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
388
408
  Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--team TEAM] [--timeout SECONDS] [--json]".to_string(),
@@ -806,6 +826,7 @@ struct ParsedArgs {
806
826
  socket: Option<String>,
807
827
  thread_id: Option<String>,
808
828
  message_id: Option<String>,
829
+ mailbox: bool,
809
830
  presentation_sink: Option<String>,
810
831
  message_class: Option<String>,
811
832
  case_id: Option<String>,
@@ -899,6 +920,7 @@ fn parse_args(args: &[String]) -> ParsedArgs {
899
920
  "--socket" => parsed.socket = next_arg(args, &mut i),
900
921
  "--thread-id" => parsed.thread_id = next_arg(args, &mut i),
901
922
  "--message-id" => parsed.message_id = next_arg(args, &mut i),
923
+ "--mailbox" => parsed.mailbox = true,
902
924
  "--presentation-sink" => parsed.presentation_sink = next_arg(args, &mut i),
903
925
  "--message-class" => parsed.message_class = next_arg(args, &mut i),
904
926
  "--case-id" => parsed.case_id = next_arg(args, &mut i),
@@ -1077,6 +1099,7 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
1077
1099
  };
1078
1100
  let message_start = usize::from(target.is_some());
1079
1101
  let workspace = workspace(&parsed, cwd);
1102
+ let sender = trusted_cli_sender(&workspace, parsed.team.as_deref())?;
1080
1103
  let presentation_value = if parsed.presentation_sink.is_some()
1081
1104
  || parsed.message_class.is_some()
1082
1105
  || parsed.case_id.is_some()
@@ -1089,10 +1112,14 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
1089
1112
  } else {
1090
1113
  None
1091
1114
  };
1092
- let (presentation, presentation_error) =
1093
- crate::messaging::presentation::normalize_presentation(presentation_value.as_ref());
1094
- if let Some(error) = presentation_error {
1095
- return Err(CliError::Usage(format!("invalid presentation: {error}")));
1115
+ let mailbox_value = parsed.mailbox.then(|| serde_json::json!(true));
1116
+ let normalized = crate::messaging::presentation::normalize_send_presentation(
1117
+ mailbox_value.as_ref(),
1118
+ presentation_value.as_ref(),
1119
+ )
1120
+ .map_err(|error| CliError::Usage(format!("invalid send routing: {error}")))?;
1121
+ if let Some(deprecation) = normalized.deprecation {
1122
+ eprintln!("warning: {deprecation}");
1096
1123
  }
1097
1124
  Ok(SendArgs {
1098
1125
  target,
@@ -1106,7 +1133,7 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
1106
1133
  workspace,
1107
1134
  team: parsed.team,
1108
1135
  task: None,
1109
- sender: trusted_cli_sender(),
1136
+ sender,
1110
1137
  no_ack: false,
1111
1138
  no_wait: true,
1112
1139
  watch_result: false,
@@ -1114,21 +1141,49 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
1114
1141
  confirm_human: false,
1115
1142
  json: parsed.json,
1116
1143
  message_id: None,
1117
- presentation,
1144
+ presentation: normalized.request,
1118
1145
  pane: parsed.pane.clone(),
1119
1146
  to_name: parsed.to_name.clone(),
1120
1147
  to_leader: parsed.to_leader.clone(),
1121
1148
  })
1122
1149
  }
1123
1150
 
1124
- fn trusted_cli_sender() -> TrustedSender {
1125
- std::env::var("TEAM_AGENT_ID")
1151
+ fn trusted_cli_sender(workspace: &Path, team: Option<&str>) -> Result<TrustedSender, CliError> {
1152
+ let agent_id = std::env::var("TEAM_AGENT_ID")
1126
1153
  .ok()
1127
1154
  .map(|value| value.trim().to_string())
1128
- .filter(|value| !value.is_empty())
1129
- .map(crate::model::ids::AgentId::new)
1130
- .map(TrustedSender::from_runtime_identity)
1131
- .unwrap_or_else(TrustedSender::leader)
1155
+ .filter(|value| !value.is_empty());
1156
+ let worker_context = ["TEAM_AGENT_OWNER_TEAM_ID", "TEAM_AGENT_AGENT_ID"]
1157
+ .into_iter()
1158
+ .any(|key| {
1159
+ std::env::var(key)
1160
+ .ok()
1161
+ .is_some_and(|value| !value.trim().is_empty())
1162
+ });
1163
+ let Some(agent_id) = agent_id else {
1164
+ if worker_context {
1165
+ return Err(CliError::Usage(
1166
+ "send requires framework-injected TEAM_AGENT_ID; refusing leader identity fallback"
1167
+ .to_string(),
1168
+ ));
1169
+ }
1170
+ return Ok(TrustedSender::leader());
1171
+ };
1172
+ let source_workspace = std::env::var("TEAM_AGENT_WORKSPACE")
1173
+ .ok()
1174
+ .filter(|value| !value.trim().is_empty())
1175
+ .unwrap_or_else(|| workspace.display().to_string());
1176
+ let source_team = std::env::var("TEAM_AGENT_OWNER_TEAM_ID")
1177
+ .ok()
1178
+ .or_else(|| std::env::var("TEAM_AGENT_TEAM_ID").ok())
1179
+ .or_else(|| team.map(ToOwned::to_owned))
1180
+ .filter(|value| !value.trim().is_empty())
1181
+ .unwrap_or_else(|| "unknown".to_string());
1182
+ Ok(TrustedSender::from_runtime_identity_with_source(
1183
+ crate::model::ids::AgentId::new(agent_id),
1184
+ source_workspace,
1185
+ &source_team,
1186
+ ))
1132
1187
  }
1133
1188
 
1134
1189
  fn warn_send_legacy_delivery_flags(args: &[String]) {
@@ -1177,6 +1232,7 @@ fn validate_send_flags(args: &[String]) -> Result<(), CliError> {
1177
1232
  "--timeout",
1178
1233
  "--confirm-human",
1179
1234
  "--message-id",
1235
+ "--mailbox",
1180
1236
  "--presentation-sink",
1181
1237
  "--message-class",
1182
1238
  "--case-id",
@@ -1593,6 +1649,27 @@ fn collect_args(args: &[String], cwd: &Path) -> Result<CollectArgs, CliError> {
1593
1649
  })
1594
1650
  }
1595
1651
 
1652
+ fn results_args(args: &[String], cwd: &Path) -> Result<ResultsArgs, CliError> {
1653
+ if args
1654
+ .iter()
1655
+ .any(|arg| arg == "--to" || arg.starts_with("--to="))
1656
+ {
1657
+ return Err(CliError::Usage(
1658
+ "results does not accept --to; it is a read-only command".to_string(),
1659
+ ));
1660
+ }
1661
+ let parsed = parse_args(args);
1662
+ let case_id = option_value(args, "--case")
1663
+ .filter(|value| !value.is_empty())
1664
+ .ok_or_else(|| CliError::Usage("missing --case".to_string()))?;
1665
+ Ok(ResultsArgs {
1666
+ case_id,
1667
+ workspace: workspace(&parsed, cwd),
1668
+ team: parsed.team,
1669
+ json: parsed.json,
1670
+ })
1671
+ }
1672
+
1596
1673
  fn option_value(args: &[String], flag: &str) -> Option<String> {
1597
1674
  let prefix = format!("{flag}=");
1598
1675
  let mut i = 0usize;
@@ -2093,8 +2170,14 @@ mod tests {
2093
2170
  }
2094
2171
 
2095
2172
  #[test]
2096
- fn send_presentation_flags_map_to_the_typed_request() {
2173
+ fn send_mailbox_and_legacy_progress_map_to_the_one_bit_request() {
2097
2174
  let cwd = tmp_workspace();
2175
+ let mailbox = send_args(&cli_argv(&["leader", "progress", "--mailbox"]), &cwd).unwrap();
2176
+ assert_eq!(
2177
+ mailbox.presentation.sink,
2178
+ crate::messaging::presentation::PresentationSink::Casefile
2179
+ );
2180
+
2098
2181
  let args = send_args(
2099
2182
  &cli_argv(&[
2100
2183
  "leader",
@@ -2115,9 +2198,9 @@ mod tests {
2115
2198
  );
2116
2199
  assert_eq!(
2117
2200
  args.presentation.class,
2118
- crate::messaging::presentation::PresentationClass::Progress
2201
+ crate::messaging::presentation::PresentationClass::Message
2119
2202
  );
2120
- assert_eq!(args.presentation.case_id.as_deref(), Some("case-9"));
2203
+ assert_eq!(args.presentation.case_id, None);
2121
2204
  }
2122
2205
 
2123
2206
  #[test]
@@ -2130,7 +2213,7 @@ mod tests {
2130
2213
  .unwrap_err();
2131
2214
  assert!(matches!(
2132
2215
  error,
2133
- CliError::Usage(message) if message == "invalid presentation: missing_class"
2216
+ CliError::Usage(message) if message == "invalid send routing: missing_class"
2134
2217
  ));
2135
2218
  }
2136
2219
 
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! cli · helpers — 跨子模块共用的小工具:tmux 冲突解析、Python 真值/falsy-or 语义、
2
3
  //! 字符级截断、相对 age 文本、稳定 key-sorted JSON。
3
4
 
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! cli · leader — `codex`/`claude`/`copilot` passthrough(`cmd_leader_passthrough` + `_provider_args` /
2
3
  //! `_leader_launcher_args`)+ leader fallback inbox 摘要(`consume_leader_inbox_summary` 及
3
4
  //! `_leader_inbox_entries` / `_leader_inbox_summary` / `_leader_inbox_entry_title`)。
@@ -8,6 +9,7 @@ use super::*;
8
9
  // `codex`/`claude` passthrough 解析(helpers.py `_provider_args`/`_leader_launcher_args`)
9
10
  // =============================================================================
10
11
 
12
+ ///
11
13
  /// `_provider_args`(`helpers.py:190-193`):剥掉前导 `--`(REMAINDER 第一个 token)。
12
14
  pub fn provider_args(values: &[String]) -> Vec<String> {
13
15
  match values.first().map(String::as_str) {
@@ -16,8 +18,9 @@ pub fn provider_args(values: &[String]) -> Vec<String> {
16
18
  }
17
19
  }
18
20
 
21
+ ///
19
22
  /// `_leader_launcher_args`(`helpers.py:196-226`):解析 attach 旗标;`--attach-session` 缺值 → Err。
20
- pub fn leader_launcher_args(values: &[String]) -> Result<LeaderLauncherArgs, CliError> {
23
+ pub(super) fn leader_launcher_args(values: &[String]) -> Result<LeaderLauncherArgs, CliError> {
21
24
  let mut out = LeaderLauncherArgs::default();
22
25
  let mut idx = 0;
23
26
  while idx < values.len() {
@@ -39,6 +42,8 @@ pub fn leader_launcher_args(values: &[String]) -> Result<LeaderLauncherArgs, Cli
39
42
  out.confirm_attach = true;
40
43
  } else if token == "--external-leader" {
41
44
  out.external_leader = true;
45
+ } else if token == "--allow-nested-attach" {
46
+ out.allow_nested_attach = true;
42
47
  } else if token == "--attach-session" {
43
48
  let Some(value) = values.get(idx + 1) else {
44
49
  return Err(CliError::Runtime(
@@ -60,7 +65,12 @@ pub fn leader_launcher_args(values: &[String]) -> Result<LeaderLauncherArgs, Cli
60
65
  fn is_leader_launcher_flag(value: &str) -> bool {
61
66
  matches!(
62
67
  value,
63
- "--external-leader" | "--attach" | "--attach-existing" | "--confirm" | "--attach-session"
68
+ "--external-leader"
69
+ | "--allow-nested-attach"
70
+ | "--attach"
71
+ | "--attach-existing"
72
+ | "--confirm"
73
+ | "--attach-session"
64
74
  ) || value.starts_with("--attach-session=")
65
75
  }
66
76
 
@@ -85,6 +95,7 @@ fn without_leader_json(values: &[String]) -> Vec<String> {
85
95
  out
86
96
  }
87
97
 
98
+ ///
88
99
  /// `codex`/`claude`/`copilot` passthrough(`parser.py:86`/`_run_leader_passthrough`):leader 早返回,
89
100
  /// **不**进 subparser。`-h`/`--help` 打 usage 直接返回 [`CmdResult::none`]。否则解析 attach
90
101
  /// 旗标 + `lifecycle_port::start_leader`。`command` ∈ {codex, claude, copilot}。
@@ -113,6 +124,7 @@ pub(crate) fn leader_passthrough_provider(command: &str) -> crate::model::enums:
113
124
  // leader fallback inbox 摘要(helpers.py `consume_leader_inbox_summary`)
114
125
  // =============================================================================
115
126
 
127
+ ///
116
128
  /// `consume_leader_inbox_summary`(`helpers.py:26-55`):每条命令后读
117
129
  /// `.team/runtime/leader-inbox.log`,从游标 `.cursor` 起读新增字节,渲染 N 条 fallback 条目摘要
118
130
  /// (`_leader_inbox_summary`,字节预算 budget=500 截断 + `Hint: team-agent inbox leader`),