@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
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Typed presentation metadata shared by message and result ingress.
2
3
 
3
4
  use serde::{Deserialize, Serialize};
@@ -98,6 +99,12 @@ pub struct PresentationDecision {
98
99
  pub policy_version: String,
99
100
  }
100
101
 
102
+ #[derive(Debug, Clone, PartialEq, Eq)]
103
+ pub struct SendPresentationInput {
104
+ pub request: PresentationRequest,
105
+ pub deprecation: Option<String>,
106
+ }
107
+
101
108
  impl Default for PresentationRequest {
102
109
  fn default() -> Self {
103
110
  Self {
@@ -177,7 +184,80 @@ pub fn normalize_report_presentation(
177
184
  (request, None)
178
185
  }
179
186
 
180
- pub fn normalize_presentation(value: Option<&Value>) -> (PresentationRequest, Option<String>) {
187
+ /// Normalize the one-bit send surface and the temporary S-011 compatibility
188
+ /// input into the existing presentation disposition primitive.
189
+ ///
190
+ /// S-011 sensitive: if the compatibility period is ended by a hard-cut
191
+ /// decision, replace this table with one uniform legacy-input refusal.
192
+ pub fn normalize_send_presentation(
193
+ mailbox: Option<&Value>,
194
+ legacy_presentation: Option<&Value>,
195
+ ) -> Result<SendPresentationInput, String> {
196
+ if mailbox.is_some() && legacy_presentation.is_some() {
197
+ return Err("mailbox_conflicts_with_deprecated_presentation".to_string());
198
+ }
199
+ if let Some(mailbox) = mailbox {
200
+ let Some(mailbox) = mailbox.as_bool() else {
201
+ return Err("mailbox_must_be_boolean".to_string());
202
+ };
203
+ return Ok(SendPresentationInput {
204
+ request: PresentationRequest {
205
+ sink: if mailbox {
206
+ PresentationSink::Casefile
207
+ } else {
208
+ PresentationSink::Leader
209
+ },
210
+ class: PresentationClass::Message,
211
+ case_id: None,
212
+ },
213
+ deprecation: None,
214
+ });
215
+ }
216
+ let Some(legacy) = legacy_presentation else {
217
+ return Ok(SendPresentationInput {
218
+ request: PresentationRequest::default(),
219
+ deprecation: None,
220
+ });
221
+ };
222
+ let Some(object) = legacy.as_object() else {
223
+ return Err("malformed_presentation".to_string());
224
+ };
225
+ let Some(class) = object.get("class").and_then(Value::as_str) else {
226
+ return Err("missing_class".to_string());
227
+ };
228
+ let Some(class) = PresentationClass::parse(class) else {
229
+ return Err(format!("unknown_class:{class}"));
230
+ };
231
+ let sink = match class {
232
+ PresentationClass::Message | PresentationClass::Blocking | PresentationClass::Timeout => {
233
+ PresentationSink::Leader
234
+ }
235
+ PresentationClass::Progress => PresentationSink::Casefile,
236
+ PresentationClass::StageResult
237
+ | PresentationClass::StagePass
238
+ | PresentationClass::Bounce
239
+ | PresentationClass::FinalReview => {
240
+ return Err(format!(
241
+ "deprecated_message_class:{}_requires_result_route",
242
+ class.as_str()
243
+ ));
244
+ }
245
+ };
246
+ Ok(SendPresentationInput {
247
+ request: PresentationRequest {
248
+ sink,
249
+ class: PresentationClass::Message,
250
+ case_id: None,
251
+ },
252
+ deprecation: Some(format!(
253
+ "message-class={} is deprecated; use mailbox={} instead",
254
+ class.as_str(),
255
+ sink != PresentationSink::Leader
256
+ )),
257
+ })
258
+ }
259
+
260
+ pub(crate) fn normalize_presentation(value: Option<&Value>) -> (PresentationRequest, Option<String>) {
181
261
  let Some(value) = value else {
182
262
  return (PresentationRequest::default(), None);
183
263
  };
@@ -304,4 +384,34 @@ mod tests {
304
384
  "send normalization remains unchanged"
305
385
  );
306
386
  }
387
+
388
+ #[test]
389
+ fn send_mailbox_and_s011_compatibility_map_to_one_bit() {
390
+ let mailbox = normalize_send_presentation(Some(&json!(true)), None).unwrap();
391
+ assert_eq!(mailbox.request.sink, PresentationSink::Casefile);
392
+ assert_eq!(mailbox.deprecation, None);
393
+
394
+ for (class, sink) in [
395
+ ("message", PresentationSink::Leader),
396
+ ("progress", PresentationSink::Casefile),
397
+ ("blocking", PresentationSink::Leader),
398
+ ("timeout", PresentationSink::Leader),
399
+ ] {
400
+ let legacy = json!({"sink": "silent", "class": class});
401
+ let mapped = normalize_send_presentation(None, Some(&legacy)).unwrap();
402
+ assert_eq!(mapped.request.sink, sink, "{class}");
403
+ assert!(mapped.deprecation.is_some(), "{class}");
404
+ }
405
+ }
406
+
407
+ #[test]
408
+ fn s011_stage_classes_require_result_route() {
409
+ for class in ["stage_result", "stage_pass", "bounce", "final_review"] {
410
+ let legacy = json!({"sink": "leader", "class": class});
411
+ assert_eq!(
412
+ normalize_send_presentation(None, Some(&legacy)).unwrap_err(),
413
+ format!("deprecated_message_class:{class}_requires_result_route")
414
+ );
415
+ }
416
+ }
307
417
  }
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! results.py — collect + report_result + envelope 校验编排 (card §66/§67)。
2
3
 
3
4
  use std::path::{Path, PathBuf};
@@ -14,6 +15,95 @@ use super::MessagingError;
14
15
  use crate::model::ids::TaskId;
15
16
  use crate::state::projection::OwnerTeamResolution;
16
17
 
18
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
19
+ pub enum ResultRoute {
20
+ Leader,
21
+ Pipeline,
22
+ }
23
+
24
+ impl ResultRoute {
25
+ pub const ALL: [Self; 2] = [Self::Leader, Self::Pipeline];
26
+
27
+ pub const fn as_str(self) -> &'static str {
28
+ match self {
29
+ Self::Leader => "leader",
30
+ Self::Pipeline => "pipeline",
31
+ }
32
+ }
33
+
34
+ pub fn parse(value: &str) -> Option<Self> {
35
+ match value {
36
+ "leader" => Some(Self::Leader),
37
+ "pipeline" => Some(Self::Pipeline),
38
+ _ => None,
39
+ }
40
+ }
41
+ }
42
+
43
+ impl Default for ResultRoute {
44
+ fn default() -> Self {
45
+ Self::Leader
46
+ }
47
+ }
48
+
49
+ pub fn results_for_case(
50
+ workspace: &Path,
51
+ case_id: &str,
52
+ owner_team_id: Option<&str>,
53
+ requested_owner_team_id: Option<&str>,
54
+ ) -> Result<Vec<serde_json::Value>, MessagingError> {
55
+ let current_db = workspace.join(".team/runtime/team.db");
56
+ let legacy_db = workspace.join(".team/team.db");
57
+ if current_db.exists() {
58
+ return results_for_case_in_db(
59
+ &current_db,
60
+ case_id,
61
+ owner_team_id,
62
+ requested_owner_team_id,
63
+ );
64
+ }
65
+ if legacy_db.exists() {
66
+ return results_for_case_in_db(&legacy_db, case_id, owner_team_id, requested_owner_team_id);
67
+ }
68
+ Ok(Vec::new())
69
+ }
70
+
71
+ fn results_for_case_in_db(
72
+ db_path: &Path,
73
+ case_id: &str,
74
+ owner_team_id: Option<&str>,
75
+ requested_owner_team_id: Option<&str>,
76
+ ) -> Result<Vec<serde_json::Value>, MessagingError> {
77
+ let conn = crate::db::schema::open_db(db_path)?;
78
+ // The second predicate works around
79
+ // `.team/bugs/BUG-caseid-writable-when-route-undeclared-20260730.md`;
80
+ // it is not design intent.
81
+ let case_predicate = "task_id = ?1 or json_extract(envelope,'$.presentation.case_id') = ?1";
82
+ let sql = match owner_team_id {
83
+ Some(_) => format!(
84
+ "select envelope from results where ({case_predicate}) \
85
+ and (owner_team_id = ?2 or owner_team_id = ?3) \
86
+ order by created_at, result_id"
87
+ ),
88
+ None => format!(
89
+ "select envelope from results where {case_predicate} order by created_at, result_id"
90
+ ),
91
+ };
92
+ let mut stmt = conn.prepare(&sql)?;
93
+ let row_mapper = |row: &rusqlite::Row<'_>| row.get::<_, String>(0);
94
+ let rows = match (owner_team_id, requested_owner_team_id) {
95
+ (Some(team), Some(requested_team)) => {
96
+ stmt.query_map(params![case_id, team, requested_team], row_mapper)
97
+ }
98
+ (Some(team), None) => stmt.query_map(params![case_id, team, team], row_mapper),
99
+ (None, _) => stmt.query_map(params![case_id], row_mapper),
100
+ }?
101
+ .collect::<Result<Vec<_>, _>>()?;
102
+ rows.into_iter()
103
+ .map(|envelope| serde_json::from_str(&envelope).map_err(MessagingError::from))
104
+ .collect()
105
+ }
106
+
17
107
  /// `collect` (`results.py:45`):投递 pending、捞 uncollected results、校验 envelope、更新任务态、
18
108
  /// 写 team_state、ensure coordinator。CLI `collect` + coordinator tick 调。
19
109
  pub fn collect(
@@ -627,18 +717,12 @@ fn report_result_for_owner_team_inner(
627
717
  explicit_owner_team: Option<&str>,
628
718
  fallback_primary_error: Option<&str>,
629
719
  ) -> Result<serde_json::Value, MessagingError> {
630
- validate_result_envelope(envelope)?;
631
- let (presentation_request, presentation_error) =
632
- super::presentation::normalize_report_presentation(envelope.get("presentation"));
633
- if let Some(error) = presentation_error {
634
- return Err(MessagingError::Validation(format!(
635
- "invalid presentation: {error}"
636
- )));
720
+ if envelope.get("result_route").is_some() {
721
+ return Err(MessagingError::Validation(
722
+ "report_result result_route is task-owned and cannot be set by a worker".to_string(),
723
+ ));
637
724
  }
638
- let presentation = super::presentation::decide_presentation(
639
- &presentation_request,
640
- super::presentation::PresentationSource::ReportResult,
641
- );
725
+ validate_result_envelope(envelope)?;
642
726
  let store = MessageStore::open(workspace)?;
643
727
  let result_id = envelope
644
728
  .get("result_id")
@@ -656,12 +740,6 @@ fn report_result_for_owner_team_inner(
656
740
  );
657
741
  }
658
742
  }
659
- if let Some(obj) = stored.as_object_mut() {
660
- obj.insert(
661
- "presentation".to_string(),
662
- serde_json::to_value(&presentation)?,
663
- );
664
- }
665
743
  let conn = crate::db::schema::open_db(store.db_path())?;
666
744
  let state_for_owner =
667
745
  crate::state::persist::load_runtime_state(workspace).unwrap_or(serde_json::json!({}));
@@ -669,6 +747,42 @@ fn report_result_for_owner_team_inner(
669
747
  .filter(|team| !team.is_empty())
670
748
  .map(str::to_string)
671
749
  .unwrap_or_else(|| super::leader_receiver::active_team_key(workspace, &state_for_owner));
750
+ let (result_route, route_declared) = task_result_route(&state_for_owner, &owner_team, task_id)?;
751
+ let presentation = if route_declared {
752
+ let request = super::presentation::PresentationRequest {
753
+ sink: match result_route {
754
+ ResultRoute::Leader => super::presentation::PresentationSink::Leader,
755
+ ResultRoute::Pipeline => super::presentation::PresentationSink::Casefile,
756
+ },
757
+ class: match result_route {
758
+ ResultRoute::Leader => super::presentation::PresentationClass::Message,
759
+ ResultRoute::Pipeline => super::presentation::PresentationClass::StageResult,
760
+ },
761
+ case_id: (result_route == ResultRoute::Pipeline).then(|| task_id.to_string()),
762
+ };
763
+ super::presentation::decide_presentation(
764
+ &request,
765
+ super::presentation::PresentationSource::ReportResult,
766
+ )
767
+ } else {
768
+ let (request, error) =
769
+ super::presentation::normalize_report_presentation(envelope.get("presentation"));
770
+ if let Some(error) = error {
771
+ return Err(MessagingError::Validation(format!(
772
+ "invalid presentation: {error}"
773
+ )));
774
+ }
775
+ super::presentation::decide_presentation(
776
+ &request,
777
+ super::presentation::PresentationSource::ReportResult,
778
+ )
779
+ };
780
+ if let Some(obj) = stored.as_object_mut() {
781
+ obj.insert(
782
+ "presentation".to_string(),
783
+ serde_json::to_value(&presentation)?,
784
+ );
785
+ }
672
786
  let inserted = insert_result_if_absent(
673
787
  &conn,
674
788
  &result_id,
@@ -968,6 +1082,8 @@ fn report_result_for_owner_team_inner(
968
1082
  let leader_notified = matches!(outcome.status, crate::messaging::DeliveryStatus::Delivered);
969
1083
  let notification_status_wire = if leader_notified {
970
1084
  "delivered"
1085
+ } else if outcome.channel.as_deref() == Some("leader_receipt_source_unavailable") {
1086
+ "injected_awaiting_receipt"
971
1087
  } else if outcome.channel.as_deref() == Some("leader_acceptance_pending") {
972
1088
  "submitted_pending_acceptance"
973
1089
  } else if outcome.channel.as_deref() == Some("rebind_required")
@@ -1045,6 +1161,39 @@ fn report_result_for_owner_team_inner(
1045
1161
  Ok(serde_json::Value::Object(out))
1046
1162
  }
1047
1163
 
1164
+ fn task_result_route(
1165
+ state: &serde_json::Value,
1166
+ owner_team: &str,
1167
+ task_id: &str,
1168
+ ) -> Result<(ResultRoute, bool), MessagingError> {
1169
+ let scoped_tasks = state
1170
+ .get("teams")
1171
+ .and_then(|teams| teams.get(owner_team))
1172
+ .and_then(|team| team.get("tasks"))
1173
+ .and_then(serde_json::Value::as_array);
1174
+ let top_level_tasks = state.get("tasks").and_then(serde_json::Value::as_array);
1175
+ let task = scoped_tasks
1176
+ .into_iter()
1177
+ .flatten()
1178
+ .chain(top_level_tasks.into_iter().flatten())
1179
+ .find(|task| task.get("id").and_then(serde_json::Value::as_str) == Some(task_id));
1180
+ let Some(route) = task.and_then(|task| task.get("result_route")) else {
1181
+ return Ok((ResultRoute::default(), false));
1182
+ };
1183
+ let route = route.as_str().ok_or_else(|| {
1184
+ MessagingError::Validation(format!(
1185
+ "task {task_id} has non-string result_route and cannot accept results"
1186
+ ))
1187
+ })?;
1188
+ ResultRoute::parse(route)
1189
+ .map(|route| (route, true))
1190
+ .ok_or_else(|| {
1191
+ MessagingError::Validation(format!(
1192
+ "task {task_id} has unknown result_route {route:?} and cannot accept results"
1193
+ ))
1194
+ })
1195
+ }
1196
+
1048
1197
  fn copy_report_attribution_fields(
1049
1198
  envelope: &serde_json::Value,
1050
1199
  out: &mut serde_json::Map<String, serde_json::Value>,
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! scheduler.py — 调度器心脏 + stuck 检测 + 告警抑制 (card §18/§68/§73)。
2
3
 
3
4
  use std::path::Path;
@@ -291,7 +291,7 @@ fn message_owner_team(db_path: &Path, message_id: &str) -> Result<Option<String>
291
291
  }
292
292
 
293
293
  /// `evaluate_idle_behavior` (`diagnose/comms.py:50`):idle 分类准确性评估。零 token,走 driver。
294
- pub fn evaluate_idle_behavior(
294
+ pub(crate) fn evaluate_idle_behavior(
295
295
  workspace: &Path,
296
296
  agent_id: &str,
297
297
  claimed_status: &str,