@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
@@ -2,6 +2,7 @@
2
2
  use super::*;
3
3
  use serde_json::json;
4
4
  use std::sync::atomic::{AtomicU32, Ordering};
5
+ use std::sync::OnceLock;
5
6
 
6
7
  static SEQ: AtomicU32 = AtomicU32::new(0);
7
8
 
@@ -20,6 +21,24 @@ fn sess(s: &str) -> SessionName {
20
21
  SessionName(s.to_string())
21
22
  }
22
23
 
24
+ pub(crate) fn test_binary_path() -> &'static str {
25
+ static PATH: OnceLock<String> = OnceLock::new();
26
+ PATH.get_or_init(|| {
27
+ if let Ok(path) = std::env::var("CARGO_BIN_EXE_team-agent") {
28
+ return path;
29
+ }
30
+ let current = std::env::current_exe().expect("test executable path");
31
+ current
32
+ .parent()
33
+ .and_then(|deps| deps.parent())
34
+ .map(|target| target.join("team-agent"))
35
+ .expect("team-agent test binary path")
36
+ .to_string_lossy()
37
+ .into_owned()
38
+ })
39
+ .as_str()
40
+ }
41
+
23
42
  mod agent_ops;
24
43
  mod core;
25
44
  mod lane_ops;
@@ -29,3 +48,43 @@ mod main_preserved;
29
48
  mod phase_b_contracts;
30
49
  mod phase_golden;
31
50
  mod restart;
51
+ mod b0_legacy_snapshot_nonauthority_contract;
52
+ mod b0_reader_hideone_audit_contract;
53
+ mod behavioral_diff_264_red;
54
+ mod claude_profile_launch_red;
55
+ mod codex_mcp_approval_inheritance_red;
56
+ mod codex_weak_window_attribution_red;
57
+ mod copilot_provider_red;
58
+ mod display_adaptive_red;
59
+ mod core_034_real_red;
60
+ mod f032_startup_prompt_best_effort_red;
61
+ mod harvest2_a_batch_red;
62
+ mod host_cotenant_death_p0_contract;
63
+ mod lifecycle_rollback_red;
64
+ mod quick_start_worker_readiness_red;
65
+ mod realmachine_clusters_1_6_red;
66
+ mod realmachine_residual_g1_g4_red;
67
+ mod restart_build_before_destroy_0540_contract;
68
+ mod restart_liveness_red;
69
+ mod restart_rebind_hotfix_252_red;
70
+ mod restart_session_capture_red;
71
+ mod resume_recover_red;
72
+ mod stale_team_saveconflict_contract;
73
+ mod startup_latency_contract;
74
+ mod status_credential_redaction_contract;
75
+ mod subprep_codex_trust_roundtrip_red;
76
+ mod swallow_batch4_semantics_red;
77
+ mod team_in_team_identity_scope_red;
78
+ mod team_in_team_sibling_quick_start_red;
79
+ mod team_in_team_state_scope_red;
80
+ mod team_key_retirement_roster_red;
81
+ mod team_key_retirement_txn_red;
82
+ mod test_isolation_escape_contract;
83
+ mod upgrade_compat_0211_red;
84
+ mod verify_rs031_window_consistency_red;
85
+ mod worker_dangerous_bypass_argv_red;
86
+ mod worker_spawn_env_red;
87
+ mod acceptance_b_batch_red;
88
+ mod claude_compatible_config_red;
89
+ mod communication_mode_runtime_contract_red;
90
+ mod clone_fork_copilot_perms_red;
@@ -16,15 +16,6 @@ use super::DisplayBackend;
16
16
  // CROSS-LANE PLACEHOLDERS(13/14/15 兄弟 lane 尚未交付;leader 集成时 reconcile)
17
17
  // ===========================================================================
18
18
 
19
- /// step8 provider 还未暴露的 launch/resume/fork 命令构造门面。lifecycle 经它构造
20
- /// provider 命令字符串(`shell_command_for_agent`/`shell_resume_command_for_agent`/
21
- /// `shell_fork_command_for_agent`,`runtime` 自由函数)—— provider lane 落地后替换为
22
- /// 真实 `provider::shell_*` API。**PLACEHOLDER**:仅占位 argv,字段映射由 provider lane 定。
23
- #[derive(Debug, Clone, PartialEq, Eq)]
24
- pub struct ShellCommand {
25
- pub argv: Vec<String>,
26
- }
27
-
28
19
  /// `EventLog` 已在 step4 落地,但 lifecycle 发射的 typed `EventKind` 名集(`lifecycle.*`/
29
20
  /// `restart.*`/`display.*`)是 step4 owned 的稳定事件名(JSON 名与 Python 一致)。
30
21
  /// **PLACEHOLDER**:此处仅命名 lifecycle 关心的事件名常量集,leader 集成时映射到
@@ -1,5 +1,6 @@
1
1
  use std::path::Path;
2
2
 
3
+ use crate::communication_mode::CommunicationMode;
3
4
  use crate::lifecycle::types::{DangerousApproval, LifecycleError};
4
5
  use crate::model::enums::{Enforcement, Provider};
5
6
  use crate::model::ids::AgentId;
@@ -12,15 +13,10 @@ output. All communication must go through Team Agent MCP tools.
12
13
 
13
14
  ## Communication (mandatory)
14
15
 
15
- - Progress, blockers, questions: team_orchestrator.send_message(to='leader', content='...')
16
16
  - Coordinate with teammate: team_orchestrator.send_message(to='<agent_id>', content='...')
17
17
  - Broadcast to all teammates: team_orchestrator.send_message(to='*', content='...')
18
18
  - Task complete: team_orchestrator.report_result(summary='...') — call exactly once
19
19
 
20
- When you receive a message from the leader or a teammate, you MUST respond
21
- through MCP tools. Writing a reply in your terminal does nothing — the sender
22
- will never see it.
23
-
24
20
  ## Rules
25
21
 
26
22
  - Do not pass sender, task_id, or schema_version — the MCP runtime fills them.
@@ -45,6 +41,7 @@ pub(crate) struct WorkerCommandAgent {
45
41
  system_prompt_inline: Option<String>,
46
42
  system_prompt_file: Option<String>,
47
43
  output_contract_format: Option<String>,
44
+ communication_mode: CommunicationMode,
48
45
  }
49
46
 
50
47
  impl WorkerCommandAgent {
@@ -52,9 +49,9 @@ impl WorkerCommandAgent {
52
49
  agent: &crate::model::yaml::Value,
53
50
  fallback_id: Option<&str>,
54
51
  provider: Provider,
55
- ) -> Self {
52
+ ) -> Result<Self, LifecycleError> {
56
53
  let system_prompt = agent.get("system_prompt");
57
- Self {
54
+ Ok(Self {
58
55
  id: agent
59
56
  .get("id")
60
57
  .and_then(crate::model::yaml::Value::as_str)
@@ -90,16 +87,21 @@ impl WorkerCommandAgent {
90
87
  .and_then(|contract| contract.get("format"))
91
88
  .and_then(crate::model::yaml::Value::as_str)
92
89
  .map(str::to_string),
93
- }
90
+ communication_mode: communication_mode(
91
+ agent
92
+ .get("communication_mode")
93
+ .and_then(crate::model::yaml::Value::as_str),
94
+ )?,
95
+ })
94
96
  }
95
97
 
96
98
  pub(crate) fn from_json(
97
99
  agent: &serde_json::Value,
98
100
  fallback_id: Option<&str>,
99
101
  provider: Provider,
100
- ) -> Self {
102
+ ) -> Result<Self, LifecycleError> {
101
103
  let system_prompt = agent.get("system_prompt");
102
- Self {
104
+ Ok(Self {
103
105
  id: agent
104
106
  .get("id")
105
107
  .and_then(serde_json::Value::as_str)
@@ -135,7 +137,12 @@ impl WorkerCommandAgent {
135
137
  .and_then(|contract| contract.get("format"))
136
138
  .and_then(serde_json::Value::as_str)
137
139
  .map(str::to_string),
138
- }
140
+ communication_mode: communication_mode(
141
+ agent
142
+ .get("communication_mode")
143
+ .and_then(serde_json::Value::as_str),
144
+ )?,
145
+ })
139
146
  }
140
147
  }
141
148
 
@@ -149,6 +156,7 @@ pub(crate) fn compile_worker_system_prompt(
149
156
  let mut chunks = vec![
150
157
  identity_section(agent),
151
158
  runtime_contract_section(),
159
+ agent.communication_mode.runtime_contract().to_string(),
152
160
  role_body(agent)?,
153
161
  ];
154
162
  if let Some(contract) = output_contract(agent) {
@@ -197,6 +205,14 @@ fn runtime_contract_section() -> String {
197
205
  RUNTIME_CONTRACT_SECTION.to_string()
198
206
  }
199
207
 
208
+ fn communication_mode(value: Option<&str>) -> Result<CommunicationMode, LifecycleError> {
209
+ let Some(value) = value else {
210
+ return Ok(CommunicationMode::default());
211
+ };
212
+ CommunicationMode::parse(value)
213
+ .ok_or_else(|| LifecycleError::Compile(format!("unknown communication_mode {value:?}")))
214
+ }
215
+
200
216
  fn identity_section(agent: &WorkerCommandAgent) -> String {
201
217
  format!(
202
218
  "You are Team Agent worker `{}` with role `{}`. When asked about your role or identity, answer with this Team Agent worker identity first, not only the generic provider product identity.",
@@ -291,6 +307,7 @@ mod tests {
291
307
  system_prompt_inline: None,
292
308
  system_prompt_file: None,
293
309
  output_contract_format: None,
310
+ communication_mode: CommunicationMode::default(),
294
311
  };
295
312
  let tools =
296
313
  resolved_tool_strings_for_command(&agent, Provider::ClaudeCode, &disabled_safety())
@@ -335,6 +352,7 @@ mod tests {
335
352
  system_prompt_inline: Some("Implement the assigned slice.".to_string()),
336
353
  system_prompt_file: None,
337
354
  output_contract_format: Some("result_envelope_v1".to_string()),
355
+ communication_mode: CommunicationMode::default(),
338
356
  };
339
357
  let prompt = compile_worker_system_prompt(&agent).unwrap();
340
358
  assert!(
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 14a · mcp_server::helpers — pure regularization + shared free helpers.
2
3
 
3
4
  use std::io::Write as _;
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! MCP lifecycle tool facades.
2
3
  //!
3
4
  //! S0 keeps the old placeholder behavior behind stable module boundaries so
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use std::path::Path;
2
3
 
3
4
  use serde_json::Value;
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 14a · mcp_server — stdio MCP server (`team_orchestrator`) over JSON-RPC 2.0.
2
3
  //!
3
4
  //! Card: `docs/phase0/subsystems/14-mcp_cli.md` (MCP half).
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 14a · mcp_server::normalize — string-alias regularization + result compaction.
2
3
 
3
4
  use serde_json::Value;
@@ -28,9 +29,9 @@ pub fn normalize_result_status(value: Option<&str>) -> ResultStatus {
28
29
  normalize_result_status_observed(value).0
29
30
  }
30
31
 
31
- /// The observed variant: `.1` carries the raw unrecognized literal so ingestion
32
- /// boundaries (wire report_result) can emit `provider.result.unknown_status_normalized`.
33
- pub fn normalize_result_status_observed(value: Option<&str>) -> (ResultStatus, Option<String>) {
32
+ /// The internal observed variant: `.1` carries the raw unrecognized literal for
33
+ /// callers that need to retain it.
34
+ fn normalize_result_status_observed(value: Option<&str>) -> (ResultStatus, Option<String>) {
34
35
  let token = normalize_token(value);
35
36
  if token.is_empty() {
36
37
  return (ResultStatus::Success, None);
@@ -260,6 +261,7 @@ pub fn compact_tool_result(result: &Value) -> ToolResult {
260
261
  "notification_channel",
261
262
  "notification_event_id",
262
263
  "warnings",
264
+ "warning",
263
265
  ]
264
266
  };
265
267
  for key in keys {
@@ -78,6 +78,7 @@ fn send_outcome_worker_accepted_envelope_byte_stable() {
78
78
  let outcome = SendOutcome::WorkerAccepted {
79
79
  message_id: "42".to_string(),
80
80
  poll_via: "team-agent inbox 42".to_string(),
81
+ warning: None,
81
82
  };
82
83
  let v = outcome.to_value();
83
84
  assert_eq!(
@@ -160,6 +161,7 @@ fn send_message_worker_recipient_returns_accepted_with_poll_hint() {
160
161
  Ok(SendOutcome::WorkerAccepted {
161
162
  message_id,
162
163
  poll_via,
164
+ ..
163
165
  }) => {
164
166
  assert!(!message_id.is_empty());
165
167
  assert_eq!(poll_via, format!("team-agent inbox {message_id}"));
@@ -353,6 +355,30 @@ fn send_message_leader_recipient_is_direct_not_accepted() {
353
355
  );
354
356
  }
355
357
 
358
+ #[test]
359
+ fn send_message_mailbox_is_durable_without_live_injection() {
360
+ let tools = TeamOrchestratorTools::with_identity(
361
+ &unique_ws("send-leader-mailbox"),
362
+ Some(AgentId::new("worker-1")),
363
+ Some(TeamKey::new("teamA")),
364
+ );
365
+ let mailbox = json!(true);
366
+ let outcome = tools
367
+ .send_message_with_presentation(
368
+ &MessageTarget::Single("leader".to_string()),
369
+ "stored update",
370
+ None,
371
+ None,
372
+ None,
373
+ Some(&mailbox),
374
+ None,
375
+ )
376
+ .expect("mailbox send persists");
377
+ let value = outcome.to_value();
378
+ assert_eq!(value.get("status"), Some(&json!("stored_only")));
379
+ assert!(value.get("message_id").and_then(Value::as_str).is_some());
380
+ }
381
+
356
382
  // ════════════════════════════════════════════════════════════════════════
357
383
  // CROSS-TEAM PRE-REFUSAL (C23) — refuse_cross_team_peer (tools.py:185-213)
358
384
  // ════════════════════════════════════════════════════════════════════════
@@ -79,7 +79,7 @@ fn tools_contract_has_thirteen_tools_in_order() {
79
79
  .unwrap();
80
80
  assert_eq!(
81
81
  send["description"],
82
- json!("Send a message to a teammate, the leader, or '*' for all other team members. Team Agent fills identity and delivery metadata; optional presentation routing is durable and never drops the message.")
82
+ json!("Send a message to a teammate, the leader, or '*' for all other team members. mailbox=true stores durably without live injection; the default is live delivery.")
83
83
  );
84
84
  assert_eq!(send["inputSchema"]["additionalProperties"], json!(false));
85
85
  assert_eq!(send["inputSchema"]["required"], json!(["to", "content"]));
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 14a · mcp_server::tools — `TeamOrchestratorTools`, the 12 typed handlers.
2
3
 
3
4
  use std::path::{Path, PathBuf};
@@ -17,17 +18,17 @@ use crate::state::persist::{
17
18
  };
18
19
 
19
20
  // ── REUSE: step 11 messaging delegate surface ───────────────────────────────
20
- use crate::messaging::{self, MessageTarget, SendOptions, TrustedSender};
21
+ use crate::messaging::{self, DeliveryStatus, MessageTarget, SendOptions, TrustedSender};
21
22
 
22
23
  use super::helpers::{
23
24
  current_reportable_message_for, delivery_outcome_value, direct_message_attribution_for,
24
- ensure_object, enum_value, insert_array, is_worker_recipient, json_dumps_default,
25
- latest_task_for_assignee, non_empty_string, normalized_envelope_value, object_fields,
26
- requires_ack_for_target, tool_runtime_error, DirectMessageAttribution,
25
+ ensure_object, enum_value, is_worker_recipient, json_dumps_default, latest_task_for_assignee,
26
+ non_empty_string, object_fields, requires_ack_for_target, tool_runtime_error,
27
+ DirectMessageAttribution,
27
28
  };
28
29
  use super::normalize::{
29
- compact_tool_result, normalize_report_envelope, normalize_result_status_observed,
30
- report_result_integrity_warnings, validate_test_evidence_schema,
30
+ compact_tool_result, normalize_report_envelope, report_result_integrity_warnings,
31
+ validate_test_evidence_schema,
31
32
  };
32
33
  use super::types::{
33
34
  Scope, SendOutcome, ToolError, ToolErrorReason, ToolOk, ToolResult, VisiblePeers,
@@ -116,6 +117,22 @@ impl TeamOrchestratorTools {
116
117
  "ValueError",
117
118
  ));
118
119
  };
120
+ if let Some(route) = task.get("result_route") {
121
+ let Some(route) = route.as_str() else {
122
+ return Err(ToolError::new(
123
+ ToolErrorReason::InvalidToolArguments,
124
+ "assign_task task.result_route must be 'leader' or 'pipeline'",
125
+ "ValueError",
126
+ ));
127
+ };
128
+ if crate::messaging::results::ResultRoute::parse(route).is_none() {
129
+ return Err(ToolError::new(
130
+ ToolErrorReason::InvalidToolArguments,
131
+ format!("assign_task task.result_route has unknown value: {route}"),
132
+ "ValueError",
133
+ ));
134
+ }
135
+ }
119
136
 
120
137
  let task_value = Value::Object(task_obj.clone());
121
138
  let recovery = task_recovery_marker(&task_value);
@@ -185,6 +202,7 @@ impl TeamOrchestratorTools {
185
202
  requires_ack,
186
203
  scope_override,
187
204
  None,
205
+ None,
188
206
  )
189
207
  }
190
208
 
@@ -195,17 +213,20 @@ impl TeamOrchestratorTools {
195
213
  task_id: Option<&str>,
196
214
  requires_ack: Option<bool>,
197
215
  scope_override: Option<Scope>,
216
+ mailbox: Option<&Value>,
198
217
  presentation: Option<&Value>,
199
218
  ) -> Result<SendOutcome, ToolError> {
200
- let (presentation, presentation_error) =
201
- crate::messaging::presentation::normalize_presentation(presentation);
202
- if let Some(error) = presentation_error {
203
- return Err(ToolError::new(
204
- ToolErrorReason::InvalidToolArguments,
205
- format!("invalid presentation: {error}"),
206
- "PresentationError",
207
- ));
208
- }
219
+ let normalized =
220
+ crate::messaging::presentation::normalize_send_presentation(mailbox, presentation)
221
+ .map_err(|error| {
222
+ ToolError::new(
223
+ ToolErrorReason::InvalidToolArguments,
224
+ format!("invalid send routing: {error}"),
225
+ "PresentationError",
226
+ )
227
+ })?;
228
+ let presentation = normalized.request;
229
+ let deprecation = normalized.deprecation;
209
230
  let canonical_owner_team = self.canonical_owner_team_key()?;
210
231
  if matches!(scope_override, Some(Scope::Workspace)) {
211
232
  return Err(self.rpc_scope_refused(
@@ -261,6 +282,21 @@ impl TeamOrchestratorTools {
261
282
  if is_worker_recipient(to) {
262
283
  let out = messaging::send_message(&self.workspace, to, content, &opts)
263
284
  .map_err(tool_runtime_error)?;
285
+ if matches!(out.status, DeliveryStatus::StoredOnly) {
286
+ let value = delivery_outcome_value(&out);
287
+ let mut ok = compact_tool_result(&value)?;
288
+ if let Some(verification) = out.verification.as_deref() {
289
+ ok.fields.insert(
290
+ "verification".to_string(),
291
+ Value::String(verification.to_string()),
292
+ );
293
+ }
294
+ if let Some(deprecation) = deprecation {
295
+ ok.fields
296
+ .insert("warning".to_string(), Value::String(deprecation));
297
+ }
298
+ return Ok(SendOutcome::Direct(ok));
299
+ }
264
300
  // tools.py:175-181 — accepted+poll_via ONLY for a REAL message_id; any other
265
301
  // outcome falls back to the compacted direct result. Never invent an
266
302
  // `mcp_<timestamp>` id: it does not exist in the store and makes the
@@ -276,12 +312,25 @@ impl TeamOrchestratorTools {
276
312
  return Ok(SendOutcome::WorkerAccepted {
277
313
  poll_via: format!("team-agent inbox {message_id}"),
278
314
  message_id,
315
+ warning: deprecation,
279
316
  });
280
317
  }
281
318
  let out = messaging::send_message(&self.workspace, to, content, &opts)
282
319
  .map_err(tool_runtime_error)?;
283
320
  let value = delivery_outcome_value(&out);
284
- let ok = compact_tool_result(&value)?;
321
+ let mut ok = compact_tool_result(&value)?;
322
+ if matches!(out.status, DeliveryStatus::StoredOnly) {
323
+ if let Some(verification) = out.verification.as_deref() {
324
+ ok.fields.insert(
325
+ "verification".to_string(),
326
+ Value::String(verification.to_string()),
327
+ );
328
+ }
329
+ }
330
+ if let Some(deprecation) = deprecation {
331
+ ok.fields
332
+ .insert("warning".to_string(), Value::String(deprecation));
333
+ }
285
334
  Ok(SendOutcome::Direct(ok))
286
335
  }
287
336
 
@@ -352,10 +401,11 @@ impl TeamOrchestratorTools {
352
401
  task_id: Option<&str>,
353
402
  agent_id: Option<&str>,
354
403
  ) -> ToolResult {
404
+ let status = enum_value(status);
355
405
  self.report_result_with_presentation(
356
406
  envelope,
357
407
  summary,
358
- status,
408
+ status.as_str(),
359
409
  changes,
360
410
  tests,
361
411
  risks,
@@ -372,7 +422,7 @@ impl TeamOrchestratorTools {
372
422
  &self,
373
423
  envelope: Option<&Value>,
374
424
  summary: Option<&str>,
375
- status: ResultStatus,
425
+ status: Option<&str>,
376
426
  changes: Option<&[Value]>,
377
427
  tests: Option<&[Value]>,
378
428
  risks: Option<&[Value]>,
@@ -404,7 +454,10 @@ impl TeamOrchestratorTools {
404
454
  );
405
455
  }
406
456
  if !obj.contains_key("status") {
407
- obj.insert("status".to_string(), enum_value(status));
457
+ obj.insert(
458
+ "status".to_string(),
459
+ Value::String(status.unwrap_or("success").to_string()),
460
+ );
408
461
  }
409
462
  if !obj.contains_key("task_id") {
410
463
  // Blocker-1 (prerelease 0.4.0): scoped-team task inference +
@@ -499,29 +552,36 @@ impl TeamOrchestratorTools {
499
552
  obj.insert("agent_id".to_string(), Value::String(resolved));
500
553
  }
501
554
  if !obj.contains_key("changes") {
502
- insert_array(obj, "changes", changes);
555
+ obj.insert(
556
+ "changes".to_string(),
557
+ Value::Array(changes.unwrap_or(&[]).to_vec()),
558
+ );
503
559
  }
504
560
  if !obj.contains_key("tests") {
505
- insert_array(obj, "tests", tests);
561
+ obj.insert(
562
+ "tests".to_string(),
563
+ Value::Array(tests.unwrap_or(&[]).to_vec()),
564
+ );
506
565
  }
507
566
  if !obj.contains_key("risks") {
508
- insert_array(obj, "risks", risks);
567
+ obj.insert(
568
+ "risks".to_string(),
569
+ Value::Array(risks.unwrap_or(&[]).to_vec()),
570
+ );
509
571
  }
510
572
  if !obj.contains_key("artifacts") {
511
- insert_array(obj, "artifacts", artifacts);
573
+ obj.insert(
574
+ "artifacts".to_string(),
575
+ Value::Array(artifacts.unwrap_or(&[]).to_vec()),
576
+ );
512
577
  }
513
578
  if !obj.contains_key("next_actions") {
514
- insert_array(obj, "next_actions", next_actions);
579
+ obj.insert(
580
+ "next_actions".to_string(),
581
+ Value::Array(next_actions.unwrap_or(&[]).to_vec()),
582
+ );
515
583
  }
516
584
  }
517
- // T3-1 cr verdict (refined): an unknown non-empty status literal normalizes to
518
- // Partial and must be OBSERVABLE at this ingestion boundary (the envelope-borne
519
- // path; the wire `status` arg path emits at dispatch). Never a silent swallow.
520
- if let Some(raw) =
521
- normalize_result_status_observed(base.get("status").and_then(Value::as_str)).1
522
- {
523
- self.note_unknown_result_status(&raw);
524
- }
525
585
  if let Err(error) = validate_test_evidence_schema(base.get("tests")) {
526
586
  return Err(ToolError::new(
527
587
  ToolErrorReason::InvalidToolArguments,
@@ -538,8 +598,15 @@ impl TeamOrchestratorTools {
538
598
  ));
539
599
  }
540
600
  let warnings = report_result_integrity_warnings(&base, &normalized);
541
- let mut env_value = normalized_envelope_value(&normalized);
542
- copy_report_attribution_fields(&base, &mut env_value);
601
+ let mut env_value = base;
602
+ if let Some(obj) = env_value.as_object_mut() {
603
+ obj.entry("schema_version")
604
+ .or_insert_with(|| Value::String("result_envelope_v1".to_string()));
605
+ obj.insert(
606
+ "presentation".to_string(),
607
+ serde_json::to_value(&normalized.presentation).unwrap_or(Value::Null),
608
+ );
609
+ }
543
610
  if !warnings.is_empty() {
544
611
  if let Some(obj) = env_value.as_object_mut() {
545
612
  obj.insert("warnings".to_string(), Value::Array(warnings));
@@ -555,20 +622,6 @@ impl TeamOrchestratorTools {
555
622
  .and_then(|value| compact_tool_result(&value))
556
623
  }
557
624
 
558
- /// T3-1 cr verdict: the observable record of an unknown→Partial status
559
- /// normalization (`provider.result.unknown_status_normalized`, raw literal
560
- /// included) — MUST-NOT-13: the swallow is never silent.
561
- pub(crate) fn note_unknown_result_status(&self, raw: &str) {
562
- let _ = EventLog::new(&self.workspace).write(
563
- "provider.result.unknown_status_normalized",
564
- serde_json::json!({
565
- "agent_id": self.agent_id.as_ref().map(AgentId::as_str),
566
- "raw_status": raw,
567
- "normalized": "partial",
568
- }),
569
- );
570
- }
571
-
572
625
  /// `update_state` (`tools.py:316-325`): delegated through the lifecycle tools
573
626
  /// facade. S0 preserves the old placeholder behavior.
574
627
  pub fn update_state(&self, note: &str) -> ToolResult {
@@ -1179,24 +1232,6 @@ fn merge_object_fields(existing: &mut Value, incoming: &Value) {
1179
1232
  }
1180
1233
  }
1181
1234
 
1182
- fn copy_report_attribution_fields(source: &Value, target: &mut Value) {
1183
- let Some(src) = source.as_object() else {
1184
- return;
1185
- };
1186
- let Some(dst) = target.as_object_mut() else {
1187
- return;
1188
- };
1189
- for key in [
1190
- "attributed_message_id",
1191
- "attribution_scope",
1192
- "task_id_source",
1193
- ] {
1194
- if let Some(value) = src.get(key) {
1195
- dst.entry(key.to_string()).or_insert(value.clone());
1196
- }
1197
- }
1198
- }
1199
-
1200
1235
  fn push_report_warning(obj: &mut serde_json::Map<String, Value>, warning: Value) {
1201
1236
  let Some(code) = warning.get("code").and_then(Value::as_str) else {
1202
1237
  return;
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 14a · mcp_server::types — wire enums / envelopes / normalized-result carriers.
2
3
 
3
4
  use serde::{Deserialize, Serialize};
@@ -210,24 +211,6 @@ pub struct RpcError {
210
211
  pub message: String,
211
212
  }
212
213
 
213
- /// The `tools/call` result wrapper (`server.py:74-86`): `{content:[{type:"text",
214
- /// text:<json>}], isError:<bool>}`. `text` is the JSON-encoded [`ToolResult`]; the
215
- /// `is_error` flag mirrors `result.get("ok") is False`.
216
- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
217
- pub struct ToolCallResult {
218
- pub content: Vec<ToolCallContent>,
219
- #[serde(rename = "isError")]
220
- pub is_error: bool,
221
- }
222
-
223
- /// One `content[]` block — always `{type:"text", text:<json>}` here.
224
- #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225
- pub struct ToolCallContent {
226
- #[serde(rename = "type")]
227
- pub kind: String,
228
- pub text: String,
229
- }
230
-
231
214
  /// A successful tool result body — the compacted delegate result
232
215
  /// (`normalize._compact_tool_result`, `normalize.py:6-64`). The whitelist-key
233
216
  /// compaction (ok/error key sets + `fanout_*` extras + `acknowledged_count`) lives
@@ -322,6 +305,7 @@ pub enum SendOutcome {
322
305
  message_id: String,
323
306
  /// Byte-stable `"team-agent inbox <message_id>"`.
324
307
  poll_via: String,
308
+ warning: Option<String>,
325
309
  },
326
310
  /// Compacted delegate result (leader / `*` / broadcast / fanout).
327
311
  Direct(ToolOk),
@@ -335,12 +319,16 @@ impl SendOutcome {
335
319
  SendOutcome::WorkerAccepted {
336
320
  message_id,
337
321
  poll_via,
322
+ warning,
338
323
  } => {
339
324
  let mut obj = serde_json::Map::new();
340
325
  obj.insert("status".to_string(), Value::String("accepted".to_string()));
341
326
  obj.insert("delivery_pending".to_string(), Value::Bool(true));
342
327
  obj.insert("poll_via".to_string(), Value::String(poll_via.clone()));
343
328
  obj.insert("message_id".to_string(), Value::String(message_id.clone()));
329
+ if let Some(warning) = warning {
330
+ obj.insert("warning".to_string(), Value::String(warning.clone()));
331
+ }
344
332
  Value::Object(obj)
345
333
  }
346
334
  SendOutcome::Direct(ok) => Value::Object(ok.fields.clone()),