@team-agent/installer 0.2.11 → 0.3.0

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 (326) hide show
  1. package/Cargo.lock +744 -0
  2. package/Cargo.toml +34 -0
  3. package/crates/team-agent/Cargo.toml +33 -0
  4. package/crates/team-agent/src/cli/adapters.rs +1343 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +554 -0
  6. package/crates/team-agent/src/cli/emit.rs +1077 -0
  7. package/crates/team-agent/src/cli/helpers.rs +88 -0
  8. package/crates/team-agent/src/cli/leader.rs +216 -0
  9. package/crates/team-agent/src/cli/mod.rs +1141 -0
  10. package/crates/team-agent/src/cli/profile.rs +306 -0
  11. package/crates/team-agent/src/cli/send.rs +215 -0
  12. package/crates/team-agent/src/cli/status.rs +179 -0
  13. package/crates/team-agent/src/cli/status_port.rs +502 -0
  14. package/crates/team-agent/src/cli/tests/base.rs +616 -0
  15. package/crates/team-agent/src/cli/tests/compile.rs +96 -0
  16. package/crates/team-agent/src/cli/tests/divergence.rs +509 -0
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +333 -0
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +395 -0
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +675 -0
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +390 -0
  21. package/crates/team-agent/src/cli/tests/mod.rs +97 -0
  22. package/crates/team-agent/src/cli/tests/peer_allow.rs +137 -0
  23. package/crates/team-agent/src/cli/tests/repair_state_byte_lock.rs +302 -0
  24. package/crates/team-agent/src/cli/tests/run_delegation.rs +305 -0
  25. package/crates/team-agent/src/cli/tests/status_send.rs +385 -0
  26. package/crates/team-agent/src/cli/tests/verb_profile.rs +182 -0
  27. package/crates/team-agent/src/cli/tests/verb_settle.rs +236 -0
  28. package/crates/team-agent/src/cli/tests/verb_validate.rs +184 -0
  29. package/crates/team-agent/src/cli/types.rs +605 -0
  30. package/crates/team-agent/src/compiler/tests.rs +701 -0
  31. package/crates/team-agent/src/compiler.rs +489 -0
  32. package/crates/team-agent/src/coordinator/backoff.rs +153 -0
  33. package/crates/team-agent/src/coordinator/health.rs +436 -0
  34. package/crates/team-agent/src/coordinator/mod.rs +80 -0
  35. package/crates/team-agent/src/coordinator/orphan.rs +179 -0
  36. package/crates/team-agent/src/coordinator/tests/abnormal.rs +255 -0
  37. package/crates/team-agent/src/coordinator/tests/basics.rs +262 -0
  38. package/crates/team-agent/src/coordinator/tests/daemon.rs +323 -0
  39. package/crates/team-agent/src/coordinator/tests/health_sync.rs +263 -0
  40. package/crates/team-agent/src/coordinator/tests/main_preserved.rs +136 -0
  41. package/crates/team-agent/src/coordinator/tests/mod.rs +310 -0
  42. package/crates/team-agent/src/coordinator/tests/spine.rs +261 -0
  43. package/crates/team-agent/src/coordinator/tests/takeover.rs +227 -0
  44. package/crates/team-agent/src/coordinator/tests/tick_core.rs +256 -0
  45. package/crates/team-agent/src/coordinator/tests/watch.rs +167 -0
  46. package/crates/team-agent/src/coordinator/tick.rs +2032 -0
  47. package/crates/team-agent/src/coordinator/types.rs +584 -0
  48. package/crates/team-agent/src/db/migration.rs +716 -0
  49. package/crates/team-agent/src/db/mod.rs +23 -0
  50. package/crates/team-agent/src/db/schema.rs +378 -0
  51. package/crates/team-agent/src/event_log.rs +375 -0
  52. package/crates/team-agent/src/fake_worker.rs +253 -0
  53. package/crates/team-agent/src/leader/helpers.rs +190 -0
  54. package/crates/team-agent/src/leader/inject.rs +33 -0
  55. package/crates/team-agent/src/leader/lease.rs +1063 -0
  56. package/crates/team-agent/src/leader/mod.rs +99 -0
  57. package/crates/team-agent/src/leader/owner_bind.rs +292 -0
  58. package/crates/team-agent/src/leader/rediscover/tests.rs +525 -0
  59. package/crates/team-agent/src/leader/rediscover.rs +1099 -0
  60. package/crates/team-agent/src/leader/start.rs +273 -0
  61. package/crates/team-agent/src/leader/takeover.rs +235 -0
  62. package/crates/team-agent/src/leader/tests/basics.rs +183 -0
  63. package/crates/team-agent/src/leader/tests/byte_findings.rs +234 -0
  64. package/crates/team-agent/src/leader/tests/identity.rs +206 -0
  65. package/crates/team-agent/src/leader/tests/idle.rs +271 -0
  66. package/crates/team-agent/src/leader/tests/lease_api.rs +225 -0
  67. package/crates/team-agent/src/leader/tests/lease_claim.rs +253 -0
  68. package/crates/team-agent/src/leader/tests/mod.rs +125 -0
  69. package/crates/team-agent/src/leader/tests/rediscover.rs +351 -0
  70. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +204 -0
  71. package/crates/team-agent/src/leader/types.rs +487 -0
  72. package/crates/team-agent/src/lib.rs +85 -0
  73. package/crates/team-agent/src/lifecycle/display.rs +228 -0
  74. package/crates/team-agent/src/lifecycle/helpers.rs +112 -0
  75. package/crates/team-agent/src/lifecycle/launch/plan.rs +227 -0
  76. package/crates/team-agent/src/lifecycle/launch.rs +1833 -0
  77. package/crates/team-agent/src/lifecycle/mod.rs +62 -0
  78. package/crates/team-agent/src/lifecycle/restart/agent.rs +533 -0
  79. package/crates/team-agent/src/lifecycle/restart/common.rs +517 -0
  80. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +41 -0
  81. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +268 -0
  82. package/crates/team-agent/src/lifecycle/restart/remove.rs +780 -0
  83. package/crates/team-agent/src/lifecycle/restart/selection.rs +208 -0
  84. package/crates/team-agent/src/lifecycle/restart/team_state.rs +242 -0
  85. package/crates/team-agent/src/lifecycle/restart.rs +76 -0
  86. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +455 -0
  87. package/crates/team-agent/src/lifecycle/tests/core.rs +989 -0
  88. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +583 -0
  89. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +933 -0
  90. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +265 -0
  91. package/crates/team-agent/src/lifecycle/tests.rs +27 -0
  92. package/crates/team-agent/src/lifecycle/types.rs +685 -0
  93. package/crates/team-agent/src/main.rs +41 -0
  94. package/crates/team-agent/src/mcp_server/helpers.rs +228 -0
  95. package/crates/team-agent/src/mcp_server/mod.rs +183 -0
  96. package/crates/team-agent/src/mcp_server/normalize.rs +312 -0
  97. package/crates/team-agent/src/mcp_server/tests/golden.rs +283 -0
  98. package/crates/team-agent/src/mcp_server/tests/normalize.rs +244 -0
  99. package/crates/team-agent/src/mcp_server/tests/scoped.rs +189 -0
  100. package/crates/team-agent/src/mcp_server/tests/send.rs +222 -0
  101. package/crates/team-agent/src/mcp_server/tests/tools.rs +158 -0
  102. package/crates/team-agent/src/mcp_server/tests/wire.rs +159 -0
  103. package/crates/team-agent/src/mcp_server/tests.rs +38 -0
  104. package/crates/team-agent/src/mcp_server/tools.rs +603 -0
  105. package/crates/team-agent/src/mcp_server/types.rs +421 -0
  106. package/crates/team-agent/src/mcp_server/wire.rs +388 -0
  107. package/crates/team-agent/src/message_store.rs +767 -0
  108. package/crates/team-agent/src/messaging/activity.rs +433 -0
  109. package/crates/team-agent/src/messaging/delivery.rs +542 -0
  110. package/crates/team-agent/src/messaging/helpers.rs +209 -0
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +340 -0
  112. package/crates/team-agent/src/messaging/mod.rs +147 -0
  113. package/crates/team-agent/src/messaging/peers.rs +32 -0
  114. package/crates/team-agent/src/messaging/results.rs +537 -0
  115. package/crates/team-agent/src/messaging/scheduler.rs +344 -0
  116. package/crates/team-agent/src/messaging/selftest.rs +100 -0
  117. package/crates/team-agent/src/messaging/send.rs +582 -0
  118. package/crates/team-agent/src/messaging/tests/basic.rs +357 -0
  119. package/crates/team-agent/src/messaging/tests/main_preserved.rs +122 -0
  120. package/crates/team-agent/src/messaging/tests/mod.rs +293 -0
  121. package/crates/team-agent/src/messaging/tests/runtime.rs +1422 -0
  122. package/crates/team-agent/src/messaging/tests/spine.rs +437 -0
  123. package/crates/team-agent/src/messaging/trust.rs +192 -0
  124. package/crates/team-agent/src/messaging/types.rs +355 -0
  125. package/crates/team-agent/src/messaging/watchers.rs +591 -0
  126. package/crates/team-agent/src/model/enums.rs +311 -0
  127. package/crates/team-agent/src/model/errors.rs +17 -0
  128. package/crates/team-agent/src/model/ids.rs +155 -0
  129. package/crates/team-agent/src/model/mod.rs +22 -0
  130. package/crates/team-agent/src/model/paths.rs +228 -0
  131. package/crates/team-agent/src/model/permissions.rs +567 -0
  132. package/crates/team-agent/src/model/routing.rs +340 -0
  133. package/crates/team-agent/src/model/spec.rs +680 -0
  134. package/crates/team-agent/src/model/task_graph.rs +380 -0
  135. package/crates/team-agent/src/model/testdata/fuzz.golden.yaml +43 -0
  136. package/crates/team-agent/src/model/testdata/fuzz.yaml +43 -0
  137. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +207 -0
  138. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +206 -0
  139. package/crates/team-agent/src/model/testdata/team.spec.yaml +206 -0
  140. package/crates/team-agent/src/model/yaml/tests.rs +288 -0
  141. package/crates/team-agent/src/model/yaml.rs +800 -0
  142. package/crates/team-agent/src/packaging/install.rs +305 -0
  143. package/crates/team-agent/src/packaging/migrate.rs +30 -0
  144. package/crates/team-agent/src/packaging/mod.rs +82 -0
  145. package/crates/team-agent/src/packaging/repair.rs +24 -0
  146. package/crates/team-agent/src/packaging/tests.rs +829 -0
  147. package/crates/team-agent/src/packaging/types.rs +369 -0
  148. package/crates/team-agent/src/provider/adapter.rs +801 -0
  149. package/crates/team-agent/src/provider/approvals/mod.rs +2 -0
  150. package/crates/team-agent/src/provider/approvals/parsing.rs +452 -0
  151. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +163 -0
  152. package/crates/team-agent/src/provider/classify.rs +456 -0
  153. package/crates/team-agent/src/provider/faults.rs +136 -0
  154. package/crates/team-agent/src/provider/helpers.rs +41 -0
  155. package/crates/team-agent/src/provider/mod.rs +53 -0
  156. package/crates/team-agent/src/provider/startup_prompt.rs +423 -0
  157. package/crates/team-agent/src/provider/tests/adapter.rs +239 -0
  158. package/crates/team-agent/src/provider/tests/classify.rs +240 -0
  159. package/crates/team-agent/src/provider/tests/faults.rs +120 -0
  160. package/crates/team-agent/src/provider/tests/idle.rs +208 -0
  161. package/crates/team-agent/src/provider/tests/wire.rs +213 -0
  162. package/crates/team-agent/src/provider/tests.rs +31 -0
  163. package/crates/team-agent/src/provider/types.rs +424 -0
  164. package/crates/team-agent/src/state/identity.rs +656 -0
  165. package/crates/team-agent/src/state/mod.rs +58 -0
  166. package/crates/team-agent/src/state/owner_gate.rs +423 -0
  167. package/crates/team-agent/src/state/persist.rs +712 -0
  168. package/crates/team-agent/src/state/projection.rs +657 -0
  169. package/crates/team-agent/src/state/selector.rs +105 -0
  170. package/crates/team-agent/src/state/testdata/state-rich.canonical.json +133 -0
  171. package/crates/team-agent/src/tmux_backend/tests.rs +586 -0
  172. package/crates/team-agent/src/tmux_backend.rs +758 -0
  173. package/crates/team-agent/src/transport/test_support.rs +252 -0
  174. package/crates/team-agent/src/transport/tests/behavior.rs +327 -0
  175. package/crates/team-agent/src/transport/tests/mod.rs +199 -0
  176. package/crates/team-agent/src/transport/tests/wire.rs +527 -0
  177. package/crates/team-agent/src/transport.rs +774 -0
  178. package/npm/install.mjs +90 -106
  179. package/package.json +15 -13
  180. package/crates/team-agent-core/Cargo.toml +0 -12
  181. package/crates/team-agent-core/src/lib.rs +0 -332
  182. package/crates/team-agent-core/src/main.rs +0 -152
  183. package/pyproject.toml +0 -18
  184. package/scripts/install.py +0 -88
  185. package/scripts/run_regression_tests.py +0 -83
  186. package/src/team_agent/__init__.py +0 -3
  187. package/src/team_agent/__main__.py +0 -5
  188. package/src/team_agent/_legacy_pane_discovery.py +0 -186
  189. package/src/team_agent/abnormal_track.py +0 -253
  190. package/src/team_agent/approvals/__init__.py +0 -65
  191. package/src/team_agent/approvals/constants.py +0 -6
  192. package/src/team_agent/approvals/parsing.py +0 -176
  193. package/src/team_agent/approvals/runtime_prompts.py +0 -171
  194. package/src/team_agent/approvals/status.py +0 -176
  195. package/src/team_agent/cli/__init__.py +0 -137
  196. package/src/team_agent/cli/commands.py +0 -481
  197. package/src/team_agent/cli/e2e.py +0 -202
  198. package/src/team_agent/cli/helpers.py +0 -226
  199. package/src/team_agent/cli/parser.py +0 -540
  200. package/src/team_agent/compiler.py +0 -334
  201. package/src/team_agent/coordinator/__init__.py +0 -53
  202. package/src/team_agent/coordinator/__main__.py +0 -119
  203. package/src/team_agent/coordinator/lifecycle.py +0 -411
  204. package/src/team_agent/coordinator/metadata.py +0 -61
  205. package/src/team_agent/coordinator/paths.py +0 -17
  206. package/src/team_agent/diagnose/__init__.py +0 -48
  207. package/src/team_agent/diagnose/checks.py +0 -101
  208. package/src/team_agent/diagnose/comms.py +0 -213
  209. package/src/team_agent/diagnose/health.py +0 -241
  210. package/src/team_agent/diagnose/orphan_cleanup.py +0 -364
  211. package/src/team_agent/diagnose/preflight.py +0 -194
  212. package/src/team_agent/diagnose/quick_start.py +0 -324
  213. package/src/team_agent/display/__init__.py +0 -92
  214. package/src/team_agent/display/adaptive.py +0 -511
  215. package/src/team_agent/display/backend.py +0 -46
  216. package/src/team_agent/display/close.py +0 -154
  217. package/src/team_agent/display/ghostty.py +0 -77
  218. package/src/team_agent/display/rebuild.py +0 -102
  219. package/src/team_agent/display/tiling.py +0 -156
  220. package/src/team_agent/display/worker_window.py +0 -114
  221. package/src/team_agent/display/workspace.py +0 -382
  222. package/src/team_agent/errors.py +0 -10
  223. package/src/team_agent/events.py +0 -84
  224. package/src/team_agent/fake_worker.py +0 -80
  225. package/src/team_agent/idle_predicate.py +0 -218
  226. package/src/team_agent/idle_takeover.py +0 -59
  227. package/src/team_agent/idle_takeover_wiring.py +0 -114
  228. package/src/team_agent/launch/__init__.py +0 -41
  229. package/src/team_agent/launch/bootstrap.py +0 -85
  230. package/src/team_agent/launch/config.py +0 -106
  231. package/src/team_agent/launch/core.py +0 -301
  232. package/src/team_agent/launch/requirements.py +0 -57
  233. package/src/team_agent/leader/__init__.py +0 -926
  234. package/src/team_agent/leader_binding.py +0 -183
  235. package/src/team_agent/lifecycle/__init__.py +0 -5
  236. package/src/team_agent/lifecycle/agents.py +0 -278
  237. package/src/team_agent/lifecycle/operations.py +0 -411
  238. package/src/team_agent/lifecycle/paste_buffer_hygiene.py +0 -39
  239. package/src/team_agent/lifecycle/start.py +0 -363
  240. package/src/team_agent/mcp_server/__init__.py +0 -42
  241. package/src/team_agent/mcp_server/__main__.py +0 -7
  242. package/src/team_agent/mcp_server/contracts.py +0 -148
  243. package/src/team_agent/mcp_server/normalize.py +0 -257
  244. package/src/team_agent/mcp_server/server.py +0 -150
  245. package/src/team_agent/mcp_server/tools.py +0 -352
  246. package/src/team_agent/message_store/__init__.py +0 -23
  247. package/src/team_agent/message_store/agent_health.py +0 -113
  248. package/src/team_agent/message_store/core.py +0 -497
  249. package/src/team_agent/message_store/leader_notification_log.py +0 -198
  250. package/src/team_agent/message_store/result_watchers.py +0 -251
  251. package/src/team_agent/message_store/schema.py +0 -308
  252. package/src/team_agent/message_store/schema_migration.py +0 -448
  253. package/src/team_agent/messaging/__init__.py +0 -1
  254. package/src/team_agent/messaging/activity_detector.py +0 -262
  255. package/src/team_agent/messaging/delivery.py +0 -504
  256. package/src/team_agent/messaging/deps.py +0 -247
  257. package/src/team_agent/messaging/idle_alerts.py +0 -423
  258. package/src/team_agent/messaging/internal_delivery.py +0 -46
  259. package/src/team_agent/messaging/leader.py +0 -497
  260. package/src/team_agent/messaging/leader_api_errors.py +0 -216
  261. package/src/team_agent/messaging/leader_panes.py +0 -673
  262. package/src/team_agent/messaging/owner_bypass.py +0 -29
  263. package/src/team_agent/messaging/result_delivery.py +0 -539
  264. package/src/team_agent/messaging/results.py +0 -447
  265. package/src/team_agent/messaging/scheduler.py +0 -450
  266. package/src/team_agent/messaging/send.py +0 -532
  267. package/src/team_agent/messaging/session_drift.py +0 -94
  268. package/src/team_agent/messaging/tmux_io.py +0 -506
  269. package/src/team_agent/messaging/tmux_prompt.py +0 -338
  270. package/src/team_agent/messaging/trust_auto_answer.py +0 -52
  271. package/src/team_agent/orchestrator/__init__.py +0 -376
  272. package/src/team_agent/orchestrator/plan.py +0 -122
  273. package/src/team_agent/orchestrator/state.py +0 -128
  274. package/src/team_agent/paths.py +0 -45
  275. package/src/team_agent/permissions.py +0 -123
  276. package/src/team_agent/profiles/__init__.py +0 -82
  277. package/src/team_agent/profiles/constants.py +0 -19
  278. package/src/team_agent/profiles/core.py +0 -407
  279. package/src/team_agent/profiles/helpers.py +0 -69
  280. package/src/team_agent/profiles/provider_env.py +0 -188
  281. package/src/team_agent/profiles/smoke.py +0 -201
  282. package/src/team_agent/provider_cli/__init__.py +0 -43
  283. package/src/team_agent/provider_cli/adapter.py +0 -172
  284. package/src/team_agent/provider_cli/base.py +0 -48
  285. package/src/team_agent/provider_cli/claude.py +0 -503
  286. package/src/team_agent/provider_cli/codex.py +0 -336
  287. package/src/team_agent/provider_cli/copilot.py +0 -8
  288. package/src/team_agent/provider_cli/fake.py +0 -39
  289. package/src/team_agent/provider_cli/gemini.py +0 -95
  290. package/src/team_agent/provider_cli/opencode.py +0 -8
  291. package/src/team_agent/provider_cli/prompt.py +0 -62
  292. package/src/team_agent/provider_cli/registry.py +0 -18
  293. package/src/team_agent/provider_cli/unsupported.py +0 -32
  294. package/src/team_agent/provider_state/README.md +0 -78
  295. package/src/team_agent/provider_state/__init__.py +0 -91
  296. package/src/team_agent/provider_state/claude.py +0 -86
  297. package/src/team_agent/provider_state/codex.py +0 -84
  298. package/src/team_agent/provider_state/common.py +0 -207
  299. package/src/team_agent/provider_state/registry.py +0 -118
  300. package/src/team_agent/providers.py +0 -163
  301. package/src/team_agent/quality_gates.py +0 -104
  302. package/src/team_agent/restart/__init__.py +0 -34
  303. package/src/team_agent/restart/orchestration.py +0 -554
  304. package/src/team_agent/restart/selection.py +0 -89
  305. package/src/team_agent/restart/snapshot.py +0 -70
  306. package/src/team_agent/routing.py +0 -84
  307. package/src/team_agent/runtime.py +0 -1243
  308. package/src/team_agent/rust_core.py +0 -327
  309. package/src/team_agent/sessions/__init__.py +0 -25
  310. package/src/team_agent/sessions/capture.py +0 -144
  311. package/src/team_agent/sessions/inventory.py +0 -44
  312. package/src/team_agent/sessions/resume.py +0 -135
  313. package/src/team_agent/simple_yaml.py +0 -236
  314. package/src/team_agent/spec.py +0 -370
  315. package/src/team_agent/state.py +0 -693
  316. package/src/team_agent/status/__init__.py +0 -63
  317. package/src/team_agent/status/approvals.py +0 -52
  318. package/src/team_agent/status/compact.py +0 -158
  319. package/src/team_agent/status/constants.py +0 -18
  320. package/src/team_agent/status/inbox.py +0 -58
  321. package/src/team_agent/status/peek.py +0 -117
  322. package/src/team_agent/status/queries.py +0 -199
  323. package/src/team_agent/task_graph.py +0 -80
  324. package/src/team_agent/terminal.py +0 -57
  325. package/src/team_agent/wake.py +0 -58
  326. package/src/team_agent/watch/__init__.py +0 -145
@@ -0,0 +1,542 @@
1
+ //! internal_delivery.py + delivery.py — coordinator/调度器侧 thin wrapper + 单条 tmux 注入投递
2
+ //! + trust 有界重试 + turn-open arm (card §16/§65)。
3
+
4
+ use std::path::Path;
5
+
6
+ use rusqlite::{params, OptionalExtension};
7
+
8
+ use crate::event_log::EventLog;
9
+ use crate::message_store::MessageStore;
10
+ use crate::model::enums::Provider;
11
+ use crate::model::ids::TeamKey;
12
+ use crate::transport::{InjectPayload, Key, PaneId, SessionName, Target, Transport, WindowName};
13
+
14
+ use super::helpers::{message_exists, MessageStatusShadow};
15
+ use super::{
16
+ DeliveryOutcome, DeliveryRefusal, DeliveryStage, DeliveryStatus, MessagingError,
17
+ PaneWidthQuery, TrustRetryPayload,
18
+ };
19
+
20
+ // ===========================================================================
21
+ // internal_delivery.py — coordinator/调度器侧 thin wrapper (card §65)
22
+ // ===========================================================================
23
+
24
+ /// `deliver_stored_message` (`internal_delivery.py:16`):coordinator/调度器侧 team-scoped 单发
25
+ /// (不重路由)。加 `_runtime_lock("send")`,直走 `_send_single_message_unlocked`。
26
+ #[allow(clippy::too_many_arguments)]
27
+ pub fn deliver_stored_message(
28
+ workspace: &Path,
29
+ target: Option<&str>,
30
+ content: &str,
31
+ task_id: Option<&crate::model::ids::TaskId>,
32
+ sender: &str,
33
+ requires_ack: bool,
34
+ wait_visible: bool,
35
+ timeout: f64,
36
+ team: Option<&TeamKey>,
37
+ ) -> Result<DeliveryOutcome, MessagingError> {
38
+ let _ = (wait_visible, timeout);
39
+ let recipient = target.unwrap_or("leader");
40
+ let store = MessageStore::open(workspace)?;
41
+ let message_id = store.create_message(
42
+ task_id.map(crate::model::ids::TaskId::as_str),
43
+ sender,
44
+ recipient,
45
+ content,
46
+ None,
47
+ requires_ack,
48
+ team.map(TeamKey::as_str),
49
+ )?;
50
+ Ok(DeliveryOutcome {
51
+ ok: true,
52
+ status: DeliveryStatus::Queued,
53
+ message_status: MessageStatusShadow("accepted".to_string()),
54
+ message_id: Some(message_id),
55
+ verification: None,
56
+ stage: None,
57
+ reason: None,
58
+ channel: None,
59
+ })
60
+ }
61
+
62
+ // ===========================================================================
63
+ // delivery.py — 单条 tmux 注入投递 + trust 有界重试 + turn-open arm (card §16)
64
+ // ===========================================================================
65
+
66
+ /// `_tmux_pane_width` (`delivery.py:20`):查询 pane 列宽。**fail-safe** (bug-064/082):失败
67
+ /// 返回 [`PaneWidthQuery::Failed`],**绝不**给默认宽度。借 step 9 transport 的 query。
68
+ pub fn tmux_pane_width(transport: &dyn Transport, target: &Target) -> PaneWidthQuery {
69
+ let queried = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
70
+ transport.query(target, crate::transport::PaneField::PaneWidth)
71
+ }));
72
+ let result = match queried {
73
+ Ok(result) => result,
74
+ Err(_) => {
75
+ return PaneWidthQuery::Failed {
76
+ error: "tmux_query_failed:panic".to_string(),
77
+ };
78
+ }
79
+ };
80
+ match result {
81
+ Ok(Some(raw)) => match raw.trim().parse::<u32>() {
82
+ Ok(pane_width) if pane_width > 0 => PaneWidthQuery::Ok { pane_width },
83
+ Ok(_) => PaneWidthQuery::Failed { error: "non_positive_width".to_string() },
84
+ Err(_) => PaneWidthQuery::Failed { error: "unparseable_output".to_string() },
85
+ },
86
+ Ok(None) => PaneWidthQuery::Failed { error: "empty_output".to_string() },
87
+ Err(err) => PaneWidthQuery::Failed { error: format!("tmux_query_failed:{err}") },
88
+ }
89
+ }
90
+
91
+ /// `_deliver_pending_message` (`delivery.py:63`):对一条消息做 tmux 注入投递 (含 trust 提示
92
+ /// 自动应答 + turn-open arm + first_send_at 戳)。daemon-path → Result。
93
+ pub fn deliver_pending_message(
94
+ workspace: &Path,
95
+ store: &MessageStore,
96
+ transport: &dyn Transport,
97
+ message_id: &str,
98
+ event_log: &EventLog,
99
+ state: &serde_json::Value,
100
+ ) -> Result<DeliveryOutcome, MessagingError> {
101
+ if !message_exists(store, message_id)? {
102
+ return Ok(DeliveryOutcome {
103
+ ok: false,
104
+ status: DeliveryStatus::Failed,
105
+ message_status: MessageStatusShadow("failed".to_string()),
106
+ message_id: Some(message_id.to_string()),
107
+ verification: None,
108
+ stage: None,
109
+ reason: None,
110
+ channel: None,
111
+ });
112
+ }
113
+ let message = message_for_delivery(store, message_id)?;
114
+ if !store.claim_for_delivery(message_id)? {
115
+ return Ok(DeliveryOutcome {
116
+ ok: false,
117
+ status: DeliveryStatus::Refused,
118
+ message_status: MessageStatusShadow("target_resolved".to_string()),
119
+ message_id: Some(message_id.to_string()),
120
+ verification: None,
121
+ stage: None,
122
+ reason: Some(DeliveryRefusal::MessageAlreadyClaimed),
123
+ channel: None,
124
+ });
125
+ }
126
+ let Some(message) = message else {
127
+ return Ok(DeliveryOutcome {
128
+ ok: false,
129
+ status: DeliveryStatus::Failed,
130
+ message_status: MessageStatusShadow("failed".to_string()),
131
+ message_id: Some(message_id.to_string()),
132
+ verification: None,
133
+ stage: None,
134
+ reason: Some(DeliveryRefusal::UnknownRecipient),
135
+ channel: None,
136
+ });
137
+ };
138
+ let target = resolve_inject_target(state, &message.recipient);
139
+ // Contract B / MUST-10 / N31/N32: physical paste+Enter into a startup trust/update
140
+ // menu is NOT provider delivery — the menu consumes the Enter and the task text
141
+ // is lost (PROBE-2 root-cause). Before injection, peek at the recipient's pane for
142
+ // a Codex actionable startup prompt; if present, mark the row `queued_until_trust`
143
+ // and DO NOT inject the task. The coordinator's startup-prompt phase will dismiss
144
+ // the trust prompt, and the SAME message_id is later replayed through this same
145
+ // delivery pipeline (no parallel side channel).
146
+ if recipient_pane_has_actionable_startup_prompt(transport, state, &message.recipient, &target) {
147
+ store.mark(message_id, "queued_until_trust", None)?;
148
+ event_log.write(
149
+ "delivery.deferred_startup_prompt",
150
+ serde_json::json!({
151
+ "message_id": message_id,
152
+ "recipient": message.recipient,
153
+ "reason": "actionable_startup_prompt",
154
+ }),
155
+ )?;
156
+ return Ok(DeliveryOutcome {
157
+ ok: false,
158
+ status: DeliveryStatus::RetryScheduled,
159
+ message_status: MessageStatusShadow("queued_until_trust".to_string()),
160
+ message_id: Some(message_id.to_string()),
161
+ verification: None,
162
+ stage: Some(DeliveryStage::TrustAutoAnswerDismissalWait),
163
+ reason: None,
164
+ channel: None,
165
+ });
166
+ }
167
+ let rendered = render_message(
168
+ &message.sender,
169
+ message.task_id.as_deref(),
170
+ &message.content,
171
+ message_id,
172
+ );
173
+ transport.inject(
174
+ &target,
175
+ &InjectPayload::Text(rendered),
176
+ Key::Enter,
177
+ true,
178
+ )?;
179
+ store.mark(message_id, "delivered", None)?;
180
+ event_log.write(
181
+ "message.delivered",
182
+ serde_json::json!({"message_id": message_id}),
183
+ )?;
184
+ let outcome = DeliveryOutcome {
185
+ ok: true,
186
+ status: DeliveryStatus::Delivered,
187
+ message_status: MessageStatusShadow("delivered".to_string()),
188
+ message_id: Some(message_id.to_string()),
189
+ verification: None,
190
+ stage: None,
191
+ reason: None,
192
+ channel: None,
193
+ };
194
+ stamp_first_send_at_if_leader_to_worker(workspace, state, &message.sender, &message.recipient)?;
195
+ record_turn_open_if_leader_to_worker(
196
+ workspace,
197
+ state,
198
+ &message.sender,
199
+ &message.recipient,
200
+ &outcome,
201
+ event_log,
202
+ )?;
203
+ Ok(outcome)
204
+ }
205
+
206
+ /// Render a message into the worker-facing protocol block (port of `rust_core.py:render_message`,
207
+ /// golden-verified): `Team Agent message from {sender}[ for {task_id}]:\n\n{content}\n\n
208
+ /// [team-agent-token:{message_id}]`. The worker (fake or real provider) only builds a result_envelope
209
+ /// when it sees this block + extracts the token — the bare content gives WORKING but never a report
210
+ /// (rt-host-a loop #4). token == message_id (exactly-once correlation).
211
+ fn render_message(sender: &str, task_id: Option<&str>, content: &str, message_id: &str) -> String {
212
+ let mut header = format!("Team Agent message from {sender}");
213
+ if let Some(task_id) = task_id.filter(|t| !t.is_empty()) {
214
+ header.push_str(&format!(" for {task_id}"));
215
+ }
216
+ format!("{header}:\n\n{content}\n\n[team-agent-token:{message_id}]")
217
+ }
218
+
219
+ /// Resolve a recipient agent-id to a tmux-RESOLVABLE inject target: the persisted pane-id if present,
220
+ /// else a session-qualified `SessionWindow` (state.session_name + the agent's window, defaulting to the
221
+ /// id). NEVER the bare agent-id as a pane — a clientless coordinator cannot resolve that
222
+ /// ("can't find pane: w1", rt-host-a loop #3). Mirrors `coordinator/tick.rs::capture_target`.
223
+ fn resolve_inject_target(state: &serde_json::Value, recipient: &str) -> Target {
224
+ let agent = state.get("agents").and_then(|a| a.get(recipient));
225
+ if let Some(pane_id) = agent
226
+ .and_then(|a| a.get("pane_id"))
227
+ .and_then(serde_json::Value::as_str)
228
+ .filter(|s| !s.is_empty())
229
+ {
230
+ return Target::Pane(PaneId::new(pane_id));
231
+ }
232
+ let session = state
233
+ .get("session_name")
234
+ .and_then(serde_json::Value::as_str)
235
+ .unwrap_or_default();
236
+ let window = agent
237
+ .and_then(|a| a.get("window"))
238
+ .and_then(serde_json::Value::as_str)
239
+ .filter(|s| !s.is_empty())
240
+ .unwrap_or(recipient);
241
+ Target::SessionWindow {
242
+ session: SessionName::new(session),
243
+ window: WindowName::new(window),
244
+ }
245
+ }
246
+
247
+ /// `_deliver_pending_messages` (`delivery.py:484`):扫 pending 队列逐条投递;busy 收件人写
248
+ /// `send.deferred_busy` 跳过 (**不丢**,card §131)。返回投递的 message_id 列表。
249
+ pub fn deliver_pending_messages(
250
+ workspace: &Path,
251
+ state: &serde_json::Value,
252
+ transport: &dyn Transport,
253
+ event_log: &EventLog,
254
+ ) -> Result<Vec<String>, MessagingError> {
255
+ let store = MessageStore::open(workspace)?;
256
+ let message_ids = {
257
+ let conn = crate::db::schema::open_db(store.db_path())?;
258
+ let mut stmt = conn.prepare(
259
+ "select message_id from messages
260
+ where status in ('pending', 'accepted')
261
+ order by created_at, message_id",
262
+ )?;
263
+ let rows = stmt.query_map([], |row| row.get::<_, String>(0))?;
264
+ rows.collect::<Result<Vec<_>, _>>()?
265
+ };
266
+ let mut delivered = Vec::new();
267
+ for message_id in message_ids {
268
+ if let Some(message) = message_for_delivery(&store, &message_id)? {
269
+ if recipient_is_busy(state, &message.recipient) {
270
+ event_log.write(
271
+ "send.deferred_busy",
272
+ serde_json::json!({
273
+ "message_id": message_id,
274
+ "sender": message.sender,
275
+ "recipient": message.recipient,
276
+ "reason": "recipient_busy",
277
+ }),
278
+ )?;
279
+ continue;
280
+ }
281
+ }
282
+ let outcome = deliver_pending_message(workspace, &store, transport, &message_id, event_log, state)?;
283
+ if outcome.ok {
284
+ delivered.push(message_id);
285
+ }
286
+ }
287
+ Ok(delivered)
288
+ }
289
+
290
+ struct PendingMessage {
291
+ sender: String,
292
+ recipient: String,
293
+ content: String,
294
+ task_id: Option<String>,
295
+ }
296
+
297
+ fn message_for_delivery(
298
+ store: &MessageStore,
299
+ message_id: &str,
300
+ ) -> Result<Option<PendingMessage>, MessagingError> {
301
+ let conn = crate::db::schema::open_db(store.db_path())?;
302
+ let message = conn
303
+ .query_row(
304
+ "select sender, recipient, content, task_id from messages where message_id = ?1",
305
+ params![message_id],
306
+ |row| {
307
+ Ok(PendingMessage {
308
+ sender: row.get::<_, String>(0)?,
309
+ recipient: row.get::<_, String>(1)?,
310
+ content: row.get::<_, String>(2)?,
311
+ task_id: row.get::<_, Option<String>>(3)?,
312
+ })
313
+ },
314
+ )
315
+ .optional()?;
316
+ Ok(message)
317
+ }
318
+
319
+ /// Pre-inject gate (Contract B): peek the recipient pane and answer "is there an
320
+ /// actionable Codex startup prompt right now (trust menu or update prompt)" using
321
+ /// the SHARED provider/startup_prompt recognizer — no second classifier, no provider
322
+ /// API calls. Returns `false` if capture fails so non-Codex providers (or any pane
323
+ /// without the trust-menu shape) keep flowing through normal delivery.
324
+ fn recipient_pane_has_actionable_startup_prompt(
325
+ transport: &dyn Transport,
326
+ state: &serde_json::Value,
327
+ recipient: &str,
328
+ target: &Target,
329
+ ) -> bool {
330
+ let agent = state
331
+ .get("agents")
332
+ .and_then(serde_json::Value::as_object)
333
+ .and_then(|agents| agents.get(recipient));
334
+ let provider = agent
335
+ .and_then(|agent| agent.get("provider"))
336
+ .and_then(serde_json::Value::as_str);
337
+ if !matches!(provider, Some("codex")) {
338
+ return false;
339
+ }
340
+ // step2-retry/scrollback root-cause (rt binary 6c9c6c1c): once the agent's
341
+ // `startup_prompts` has been flipped to `handled`/`complete`, the trust modal
342
+ // has been answered and is the AUTHORITATIVE record of "no actionable startup
343
+ // prompt remains". A `tmux capture-pane -S -` Full capture STILL contains the
344
+ // dismissed modal text in scrollback ("Do you trust …" + `› 1. Yes, continue`),
345
+ // so the recognizer's actionable-shape override matches the residue and the
346
+ // delivery gate would loop forever (49-attempt no-deliver in real machine).
347
+ // Trust the state (same source step1-idem uses) and skip the classify entirely.
348
+ let startup_prompts = agent
349
+ .and_then(|agent| agent.get("startup_prompts"))
350
+ .and_then(serde_json::Value::as_str);
351
+ if matches!(startup_prompts, Some("handled" | "complete")) {
352
+ return false;
353
+ }
354
+ let captured = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
355
+ transport.capture(target, crate::transport::CaptureRange::Full)
356
+ })) {
357
+ Ok(Ok(captured)) => captured.text,
358
+ _ => return false,
359
+ };
360
+ matches!(
361
+ crate::provider::classify_codex_startup_screen(&captured),
362
+ crate::provider::StartupScreenDecision::AnswerWorkspaceTrust
363
+ | crate::provider::StartupScreenDecision::SkipUpdatePrompt
364
+ )
365
+ }
366
+
367
+ fn recipient_is_busy(state: &serde_json::Value, recipient: &str) -> bool {
368
+ state
369
+ .get("agents")
370
+ .and_then(serde_json::Value::as_object)
371
+ .and_then(|agents| agents.get(recipient))
372
+ .and_then(|agent| agent.get("status"))
373
+ .and_then(serde_json::Value::as_str)
374
+ == Some("busy")
375
+ }
376
+
377
+ /// `_handle_trust_retry_needed` (`delivery.py:221`):trust 应答失败时调度有界退避重试
378
+ /// (`attempt < MAX` → schedule;`>= MAX` → 终态 mark failed + `trust_auto_answer_exhausted`)。
379
+ pub fn handle_trust_retry_needed(
380
+ store: &MessageStore,
381
+ payload: &TrustRetryPayload,
382
+ event_log: &EventLog,
383
+ ) -> Result<DeliveryOutcome, MessagingError> {
384
+ if payload.attempt >= payload.max_attempts {
385
+ let _ = store.mark(&payload.message_id, "failed", Some("trust_auto_answer_exhausted"));
386
+ event_log.write(
387
+ "leader_panes.trust_auto_answer_exhausted",
388
+ serde_json::json!({"message_id": payload.message_id, "attempt": payload.attempt}),
389
+ )?;
390
+ return Ok(DeliveryOutcome {
391
+ ok: false,
392
+ status: DeliveryStatus::TrustAutoAnswerExhausted,
393
+ message_status: MessageStatusShadow("failed".to_string()),
394
+ message_id: Some(payload.message_id.clone()),
395
+ verification: None,
396
+ stage: Some(DeliveryStage::TrustAutoAnswerDismissalWait),
397
+ reason: None,
398
+ channel: None,
399
+ });
400
+ }
401
+ let next_attempt = payload.attempt.saturating_add(1);
402
+ let backoff = super::TRUST_RETRY_BACKOFF_SECONDS
403
+ .iter()
404
+ .find_map(|(attempt, seconds)| (*attempt == next_attempt).then_some(*seconds))
405
+ .unwrap_or(30);
406
+ let due_at = (chrono::Utc::now() + chrono::Duration::seconds(i64::from(backoff))).to_rfc3339();
407
+ let conn = crate::db::schema::open_db(store.db_path())?;
408
+ conn.execute(
409
+ "insert into scheduled_events(owner_team_id, due_at, target, kind, payload_json, status, created_at)
410
+ values (null, ?1, ?2, 'trust_retry', ?3, 'pending', ?4)",
411
+ params![
412
+ due_at,
413
+ payload.first_target.as_str(),
414
+ serde_json::json!({
415
+ "message_id": payload.message_id,
416
+ "attempt": next_attempt,
417
+ "max_attempts": payload.max_attempts,
418
+ "first_target": payload.first_target.as_str(),
419
+ })
420
+ .to_string(),
421
+ chrono::Utc::now().to_rfc3339(),
422
+ ],
423
+ )?;
424
+ let _ = store.mark(&payload.message_id, "queued_until_trust", None);
425
+ event_log.write(
426
+ "leader_panes.trust_auto_answer_retry_scheduled",
427
+ serde_json::json!({"message_id": payload.message_id, "attempt": next_attempt, "due_at": due_at}),
428
+ )?;
429
+ Ok(DeliveryOutcome {
430
+ ok: false,
431
+ status: DeliveryStatus::RetryScheduled,
432
+ message_status: MessageStatusShadow("queued_until_trust".to_string()),
433
+ message_id: Some(payload.message_id.clone()),
434
+ verification: None,
435
+ stage: Some(DeliveryStage::TrustAutoAnswerDismissalWait),
436
+ reason: None,
437
+ channel: None,
438
+ })
439
+ }
440
+
441
+ /// `_execute_trust_retry` (`delivery.py:330`):trust_retry scheduled event 的消费者 ——
442
+ /// 把行重置回 `accepted`,attempt 穿透,重跑 `_deliver_pending_message`。
443
+ pub fn execute_trust_retry(
444
+ workspace: &Path,
445
+ store: &MessageStore,
446
+ transport: &dyn Transport,
447
+ payload: &TrustRetryPayload,
448
+ event_log: &EventLog,
449
+ owner_team_id: Option<&TeamKey>,
450
+ ) -> Result<DeliveryOutcome, MessagingError> {
451
+ let _ = owner_team_id;
452
+ let _ = store.mark(&payload.message_id, "accepted", None);
453
+ let state = crate::state::persist::load_runtime_state(workspace)?;
454
+ deliver_pending_message(workspace, store, transport, &payload.message_id, event_log, &state)
455
+ }
456
+
457
+ /// `_record_turn_open_if_leader_to_worker` (`delivery.py:430`):**take-over arm 来自真实投递**
458
+ /// (card §121) —— 仅 leader→worker 注入**成功后**才调 `record_turn_open_after_delivery`,绝不凭空 arm。
459
+ pub fn record_turn_open_if_leader_to_worker(
460
+ workspace: &Path,
461
+ state: &serde_json::Value,
462
+ sender: &str,
463
+ recipient: &str,
464
+ delivered: &DeliveryOutcome,
465
+ event_log: &EventLog,
466
+ ) -> Result<(), MessagingError> {
467
+ let _ = state;
468
+ if !delivered.ok || !matches!(sender, "leader" | "Leader") || recipient == "leader" {
469
+ return Ok(());
470
+ }
471
+ let mut state = crate::state::persist::load_runtime_state(workspace)?;
472
+ let Some(root) = state.as_object_mut() else {
473
+ return Ok(());
474
+ };
475
+ let coordinator = root
476
+ .entry("coordinator")
477
+ .or_insert_with(|| serde_json::json!({}));
478
+ if let Some(obj) = coordinator.as_object_mut() {
479
+ obj.insert(
480
+ "turn_open".to_string(),
481
+ serde_json::json!({"armed": true, "node_id": recipient, "turn_id": delivered.message_id}),
482
+ );
483
+ }
484
+ crate::state::persist::save_runtime_state(workspace, &state)?;
485
+ event_log.write(
486
+ "turn_open.armed_after_delivery",
487
+ serde_json::json!({"agent_id": recipient, "message_id": delivered.message_id}),
488
+ )?;
489
+ Ok(())
490
+ }
491
+
492
+ /// `_stamp_first_send_at_if_leader_to_worker` (`delivery.py:380`):首次 leader→worker 投递戳
493
+ /// `first_send_at` (step 13 restart Route B atomicity 决策读它)。
494
+ pub fn stamp_first_send_at_if_leader_to_worker(
495
+ workspace: &Path,
496
+ state: &serde_json::Value,
497
+ sender: &str,
498
+ recipient: &str,
499
+ ) -> Result<(), MessagingError> {
500
+ let _ = state;
501
+ if !matches!(sender, "leader" | "Leader") || recipient == "leader" {
502
+ return Ok(());
503
+ }
504
+ let mut state = crate::state::persist::load_runtime_state(workspace)?;
505
+ let now = chrono::Utc::now().to_rfc3339();
506
+ if let Some(agent) = state
507
+ .get_mut("agents")
508
+ .and_then(serde_json::Value::as_object_mut)
509
+ .and_then(|agents| agents.get_mut(recipient))
510
+ .and_then(serde_json::Value::as_object_mut)
511
+ {
512
+ if !agent.contains_key("first_send_at") || agent.get("first_send_at").is_some_and(serde_json::Value::is_null) {
513
+ agent.insert("first_send_at".to_string(), serde_json::Value::String(now));
514
+ crate::state::persist::save_runtime_state(workspace, &state)?;
515
+ }
516
+ }
517
+ Ok(())
518
+ }
519
+
520
+ /// `retry_injection_after_trust_auto_answer` (`trust_auto_answer.py`):leader 路径 trust 应答
521
+ /// 后重注入 (查 pane_width fail-safe + attempt_trust_auto_answer + 等 dismissal + 重 inject)。
522
+ pub fn retry_injection_after_trust_auto_answer(
523
+ workspace: &Path,
524
+ state: &serde_json::Value,
525
+ transport: &dyn Transport,
526
+ target: &Target,
527
+ text: &str,
528
+ provider: Provider,
529
+ event_log: &EventLog,
530
+ ) -> Result<DeliveryOutcome, MessagingError> {
531
+ let _ = (workspace, state, transport, target, text, provider, event_log);
532
+ Ok(DeliveryOutcome {
533
+ ok: false,
534
+ status: DeliveryStatus::RetryScheduled,
535
+ message_status: MessageStatusShadow("retry_scheduled".to_string()),
536
+ message_id: None,
537
+ verification: None,
538
+ stage: Some(DeliveryStage::TrustAutoAnswerDismissalWait),
539
+ reason: None,
540
+ channel: None,
541
+ })
542
+ }