@team-agent/installer 0.2.10 → 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 -83
  203. package/src/team_agent/coordinator/lifecycle.py +0 -363
  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 -200
  226. package/src/team_agent/idle_takeover.py +0 -59
  227. package/src/team_agent/idle_takeover_wiring.py +0 -111
  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 -254
  255. package/src/team_agent/messaging/delivery.py +0 -473
  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 -457
  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 -86
  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 -1239
  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 -143
  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 -602
  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
@@ -1,450 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from team_agent.messaging.deps import (
4
- EventLog,
5
- MessageStore,
6
- check_team_owner,
7
- datetime,
8
- json,
9
- load_runtime_state,
10
- load_spec,
11
- save_runtime_state,
12
- send_message,
13
- team_state_key,
14
- timedelta,
15
- timezone,
16
- )
17
- from team_agent.messaging.activity_detector import classify_agent_activity, detect_compaction_degradation
18
- from team_agent.messaging.internal_delivery import deliver_stored_message
19
- from team_agent.messaging.result_delivery import delivered_result_message, result_id_from_text
20
- from team_agent.state import team_state_candidates
21
-
22
- from pathlib import Path
23
- from typing import Any
24
-
25
- _ACTIVE_TASK_STATUSES = {"pending", "assigned", "in_progress", "ready", "running", "needs_retry"}
26
- _INBOUND_WORK_STATUSES = {"pending", "accepted", "target_resolved", "injected"}
27
- _DELIVERED_MESSAGE_STATUSES = {"visible", "submitted", "delivered", "acknowledged"}
28
- _PROGRESS_EVENTS = {
29
- "mcp.report_result",
30
- "report_result.accepted",
31
- "send.deliver_attempt",
32
- "send.submitted",
33
- "leader_receiver.deliver_attempt",
34
- "leader_receiver.submitted",
35
- "communication.peer_mirrored",
36
- }
37
- _RESTART_RESET_EVENTS = {"restart.agent_start", "restart.complete", "reset_agent.complete", "start_agent.complete"}
38
- _ALERT_TYPES = {"stuck", "idle_fallback", "cross_worker_deadlock"}
39
-
40
-
41
- def _fire_due_scheduled_events(workspace: Path, store: MessageStore, event_log: EventLog) -> list[int]:
42
- fired: list[int] = []
43
- for row in store.due_scheduled_events():
44
- payload = json.loads(row["payload_json"] or "{}")
45
- try:
46
- if row["kind"] == "send":
47
- content = str(payload.get("content") or "")
48
- result_id = result_id_from_text(content)
49
- existing = delivered_result_message(
50
- store,
51
- result_id or "",
52
- task_id=payload.get("task_id"),
53
- owner_team_id=row.get("owner_team_id"),
54
- )
55
- if existing:
56
- result = {
57
- "ok": True,
58
- "status": "already_delivered",
59
- "message_id": existing.get("message_id"),
60
- "deduped": True,
61
- }
62
- event_log.write(
63
- "coordinator.scheduled_result_deduped",
64
- id=row["id"],
65
- target=row["target"],
66
- result_id=result_id,
67
- message_id=existing.get("message_id"),
68
- )
69
- store.mark_scheduled_event(int(row["id"]), "done", result)
70
- fired.append(int(row["id"]))
71
- continue
72
- deliver = deliver_stored_message if row.get("owner_team_id") else send_message
73
- result = deliver(
74
- workspace,
75
- row["target"],
76
- content,
77
- task_id=payload.get("task_id"),
78
- sender=payload.get("sender", "coordinator"),
79
- requires_ack=bool(payload.get("requires_ack", True)),
80
- wait_visible=bool(payload.get("wait_visible", True)),
81
- timeout=float(payload.get("timeout", 30)),
82
- team=row.get("owner_team_id"),
83
- )
84
- elif row["kind"] == "health_ping":
85
- result = {"ok": True, "status": "logged"}
86
- event_log.write("coordinator.health_ping", target=row["target"], payload=payload)
87
- elif row["kind"] == "trust_retry":
88
- # Spark MEDIUM sweep #3 (2026-05-26) — bounded-backoff consumer
89
- # for delivery.py:_handle_trust_retry_needed. payload carries the
90
- # message_id and current attempt; _execute_trust_retry resets the
91
- # row to 'accepted', re-runs _deliver_pending_message with the
92
- # attempt threaded through, and either delivers, reschedules, or
93
- # hits the terminal trust_auto_answer_exhausted branch.
94
- from team_agent.messaging.delivery import _execute_trust_retry
95
- result = _execute_trust_retry(
96
- workspace, store, event_log, payload,
97
- owner_team_id=row.get("owner_team_id"),
98
- )
99
- else:
100
- result = {"ok": False, "error": f"unknown scheduled event kind: {row['kind']}"}
101
- if not result.get("ok") and row["kind"] == "send":
102
- retry = _schedule_send_retry(store, row, payload, result)
103
- if retry:
104
- result = {**result, **retry}
105
- store.mark_scheduled_event(int(row["id"]), "retry_scheduled", result)
106
- event_log.write(
107
- "coordinator.scheduled_retry",
108
- id=row["id"],
109
- retry_event_id=retry["retry_event_id"],
110
- target=row["target"],
111
- attempt=retry["next_attempt"],
112
- )
113
- fired.append(int(row["id"]))
114
- continue
115
- store.mark_scheduled_event(int(row["id"]), "done" if result.get("ok") else "failed", result)
116
- fired.append(int(row["id"]))
117
- except Exception as exc:
118
- result = {"ok": False, "error": str(exc)}
119
- store.mark_scheduled_event(int(row["id"]), "failed", result)
120
- event_log.write("coordinator.scheduled_failed", id=row["id"], error=str(exc))
121
- return fired
122
-
123
-
124
- def _schedule_send_retry(
125
- store: MessageStore,
126
- row: dict[str, Any],
127
- payload: dict[str, Any],
128
- result: dict[str, Any],
129
- ) -> dict[str, Any] | None:
130
- attempt = int(payload.get("attempt") or 1)
131
- max_attempts = int(payload.get("max_attempts") or 1)
132
- if attempt >= max_attempts:
133
- return None
134
- retry_payload = dict(payload)
135
- retry_payload["attempt"] = attempt + 1
136
- due_at = datetime.now(timezone.utc) + timedelta(seconds=min(2 * attempt, 5))
137
- retry_id = store.add_scheduled_event(due_at.isoformat(), row["target"], row["kind"], retry_payload, owner_team_id=row.get("owner_team_id"))
138
- return {
139
- "retry_event_id": retry_id,
140
- "next_attempt": attempt + 1,
141
- "max_attempts": max_attempts,
142
- "retry_reason": result.get("reason") or result.get("error"),
143
- }
144
-
145
-
146
- def _detect_stuck_agents(
147
- workspace: Path,
148
- state: dict[str, Any],
149
- store: MessageStore,
150
- event_log: EventLog,
151
- ) -> list[str]:
152
- spec_path = Path(state.get("spec_path", workspace / "team.spec.yaml"))
153
- spec = load_spec(spec_path) if spec_path.exists() else {}
154
- runtime_cfg = spec.get("runtime", {})
155
- stuck_timeout = int(runtime_cfg.get("stuck_timeout_sec", 300))
156
- push_min_interval = int(runtime_cfg.get("push_min_interval_sec", 60))
157
- owner_team_id = team_state_key(state)
158
- health = store.agent_health(owner_team_id=owner_team_id)
159
- stuck: list[str] = []
160
- now = datetime.now(timezone.utc)
161
- for agent_id, row in health.items():
162
- if row.get("status") not in {"RUNNING", "WORKING"} or not row.get("last_output_at"):
163
- continue
164
- try:
165
- last = datetime.fromisoformat(row["last_output_at"])
166
- except ValueError:
167
- continue
168
- if last.tzinfo is None:
169
- last = last.replace(tzinfo=timezone.utc)
170
- if (now - last).total_seconds() < stuck_timeout:
171
- continue
172
- suppression = _active_alert_suppression(state, store, event_log, agent_id, "stuck")
173
- has_work, work_reason = _agent_has_stuck_relevant_work(state, store, agent_id)
174
- if not has_work:
175
- event_log.write("coordinator.agent_stuck_suppressed", agent_id=agent_id, reason="idle_no_work", last_output_at=row["last_output_at"])
176
- continue
177
- if suppression:
178
- continue
179
- progress_event = _recent_agent_progress_event(event_log, agent_id, last)
180
- if progress_event:
181
- event_log.write(
182
- "coordinator.agent_stuck_suppressed",
183
- agent_id=agent_id,
184
- reason="recent_progress_event",
185
- progress_event=progress_event.get("event"),
186
- progress_ts=progress_event.get("ts"),
187
- last_output_at=row["last_output_at"],
188
- work_reason=work_reason,
189
- )
190
- continue
191
- stuck.append(agent_id)
192
- state.setdefault("coordinator", {})
193
- push_key = f"last_stuck_push_at:{agent_id}"
194
- last_push_raw = state["coordinator"].get(push_key)
195
- should_push = True
196
- if last_push_raw:
197
- try:
198
- last_push = datetime.fromisoformat(last_push_raw)
199
- if last_push.tzinfo is None:
200
- last_push = last_push.replace(tzinfo=timezone.utc)
201
- should_push = (now - last_push).total_seconds() >= push_min_interval
202
- except ValueError:
203
- should_push = True
204
- event_log.write("coordinator.agent_stuck", agent_id=agent_id, last_output_at=row["last_output_at"], work_reason=work_reason)
205
- if should_push:
206
- state["coordinator"][push_key] = now.isoformat()
207
- try:
208
- send_message(
209
- workspace,
210
- "leader",
211
- f"agent {agent_id} appears stuck: no output for {stuck_timeout}s",
212
- sender="coordinator",
213
- requires_ack=False,
214
- wait_visible=False,
215
- team=owner_team_id,
216
- )
217
- except Exception as exc:
218
- event_log.write("coordinator.stuck_push_failed", agent_id=agent_id, error=str(exc))
219
- return stuck
220
-
221
-
222
- def stuck_list(workspace: Path) -> dict[str, Any]:
223
- state = load_runtime_state(workspace)
224
- suppressed = state.get("coordinator", {}).get("suppressed_idle_alerts", {})
225
- if _use_team_scoped_suppressions(state):
226
- from team_agent.state import _caller_identity_from_env
227
- caller = _caller_identity_from_env()
228
- candidates = team_state_candidates(state)
229
- caller_team = None
230
- if caller.get("pane_id"):
231
- for key, candidate in candidates.items():
232
- owner = candidate.get("team_owner") or {}
233
- if (
234
- caller["pane_id"] == (owner.get("pane_id") or "")
235
- and caller["provider"] == (owner.get("provider") or "")
236
- and caller["machine_fingerprint"] == (owner.get("machine_fingerprint") or "")
237
- ):
238
- caller_team = key
239
- break
240
- if caller_team is None:
241
- return {
242
- "ok": False,
243
- "status": "refused",
244
- "reason": "team_owner_unresolved",
245
- "action": "set TEAM_AGENT_LEADER_PANE_ID/PROVIDER/MACHINE_FINGERPRINT to your team's claimed identity, or use team-agent takeover --confirm",
246
- "candidates": sorted(list(candidates)),
247
- }
248
- return {"ok": True, "suppressed_idle_alerts": suppressed.get(caller_team, {}), "team": caller_team}
249
- known_team_keys = set(team_state_candidates(state).keys())
250
- has_team_keys = bool(known_team_keys & set(suppressed.keys()))
251
- if not has_team_keys and (
252
- len(suppressed) == 1
253
- and all(isinstance(value, dict) for value in suppressed.values())
254
- and not any(isinstance(value, dict) and set(value) & _ALERT_TYPES for value in suppressed.values())
255
- ):
256
- only = next(iter(suppressed.values()))
257
- if all(isinstance(value, dict) for value in only.values()):
258
- suppressed = only
259
- return {"ok": True, "suppressed_idle_alerts": suppressed}
260
-
261
-
262
- def stuck_cancel(
263
- workspace: Path,
264
- agent_id: str,
265
- alert_type: str = "stuck",
266
- suppressed_by: str = "leader",
267
- ) -> dict[str, Any]:
268
- if alert_type == "all":
269
- alert_types = sorted(_ALERT_TYPES)
270
- elif alert_type in _ALERT_TYPES:
271
- alert_types = [alert_type]
272
- else:
273
- return {"ok": False, "status": "refused", "reason": "invalid_alert_type", "alert_type": alert_type}
274
- state = load_runtime_state(workspace)
275
- gate = check_team_owner(state)
276
- if gate:
277
- return gate
278
- store = MessageStore(workspace)
279
- owner_team_id = team_state_key(state)
280
- coordinator = state.setdefault("coordinator", {})
281
- suppressed = coordinator.setdefault("suppressed_idle_alerts", {})
282
- team_suppressions = suppressed.setdefault(owner_team_id, {}) if _use_team_scoped_suppressions(state) else suppressed
283
- agent_suppressions = team_suppressions.setdefault(agent_id, {})
284
- now = datetime.now(timezone.utc).isoformat()
285
- snapshot = _agent_alert_snapshot(state, store, agent_id, owner_team_id)
286
- for item in alert_types:
287
- agent_suppressions[item] = {
288
- "suppressed_at": now,
289
- "suppressed_by": suppressed_by,
290
- "snapshot": snapshot,
291
- }
292
- save_runtime_state(workspace, state)
293
- EventLog(workspace).write("coordinator.idle_alert_suppressed", agent_id=agent_id, alert_types=alert_types, suppressed_by=suppressed_by)
294
- return {"ok": True, "agent_id": agent_id, "alert_types": alert_types, "suppressed": agent_suppressions}
295
-
296
-
297
- def _active_alert_suppression(
298
- state: dict[str, Any],
299
- store: MessageStore,
300
- event_log: EventLog,
301
- agent_id: str,
302
- alert_type: str,
303
- ) -> dict[str, Any] | None:
304
- owner_team_id = team_state_key(state)
305
- suppressed = state.get("coordinator", {}).get("suppressed_idle_alerts", {})
306
- entry = suppressed.get(owner_team_id, {}).get(agent_id, {}).get(alert_type)
307
- if not isinstance(entry, dict):
308
- entry = suppressed.get(agent_id, {}).get(alert_type)
309
- if not isinstance(entry, dict):
310
- return None
311
- cleared = _suppression_clear_reason(state, store, event_log, agent_id, entry)
312
- if cleared:
313
- _clear_alert_suppression(state, agent_id, alert_type, owner_team_id)
314
- event_log.write("coordinator.idle_alert_suppression_cleared", agent_id=agent_id, alert_type=alert_type, reason=cleared)
315
- return None
316
- return entry
317
-
318
-
319
- def _suppression_clear_reason(
320
- state: dict[str, Any],
321
- store: MessageStore,
322
- event_log: EventLog,
323
- agent_id: str,
324
- entry: dict[str, Any],
325
- ) -> str | None:
326
- if entry.get("manual_acknowledge"):
327
- try:
328
- expires_at = datetime.fromisoformat(str(entry.get("expires_at")))
329
- except ValueError:
330
- return "invalid_suppression_timestamp"
331
- if expires_at.tzinfo is None:
332
- expires_at = expires_at.replace(tzinfo=timezone.utc)
333
- if datetime.now(timezone.utc) < expires_at:
334
- return None
335
- return "manual_acknowledge_expired"
336
- previous = entry.get("snapshot") if isinstance(entry.get("snapshot"), dict) else {}
337
- current = _agent_alert_snapshot(state, store, agent_id)
338
- if current.get("assigned_task_ids") != previous.get("assigned_task_ids"):
339
- return "task_assignment_changed"
340
- if current.get("delivered_message_ids") != previous.get("delivered_message_ids"):
341
- return "inbound_delivery_changed"
342
- try:
343
- suppressed_at = datetime.fromisoformat(str(entry.get("suppressed_at")))
344
- except ValueError:
345
- return "invalid_suppression_timestamp"
346
- if suppressed_at.tzinfo is None:
347
- suppressed_at = suppressed_at.replace(tzinfo=timezone.utc)
348
- if _recent_agent_progress_event(event_log, agent_id, suppressed_at):
349
- return "progress_event"
350
- if _recent_restart_or_reset_event(event_log, agent_id, suppressed_at):
351
- return "restart_or_reset"
352
- return None
353
-
354
-
355
- def _clear_alert_suppression(state: dict[str, Any], agent_id: str, alert_type: str, owner_team_id: str | None = None) -> None:
356
- suppressed = state.get("coordinator", {}).get("suppressed_idle_alerts", {})
357
- if agent_id in suppressed:
358
- agent_suppressions = suppressed.get(agent_id, {})
359
- agent_suppressions.pop(alert_type, None)
360
- if not agent_suppressions:
361
- suppressed.pop(agent_id, None)
362
- return
363
- team_suppressions = suppressed.get(owner_team_id or team_state_key(state), {})
364
- agent_suppressions = team_suppressions.get(agent_id, {})
365
- agent_suppressions.pop(alert_type, None)
366
- if not agent_suppressions:
367
- team_suppressions.pop(agent_id, None)
368
- if not team_suppressions:
369
- suppressed.pop(owner_team_id or team_state_key(state), None)
370
-
371
-
372
- def _use_team_scoped_suppressions(state: dict[str, Any]) -> bool:
373
- return len(team_state_candidates(state)) > 1
374
-
375
-
376
- def _agent_alert_snapshot(state: dict[str, Any], store: MessageStore, agent_id: str, owner_team_id: str | None = None) -> dict[str, Any]:
377
- assigned_task_ids = sorted(str(task.get("id")) for task in state.get("tasks", []) if task.get("assignee") == agent_id)
378
- delivered_message_ids = sorted(
379
- str(message.get("message_id"))
380
- for message in store.messages(owner_team_id=owner_team_id or team_state_key(state))
381
- if message.get("recipient") == agent_id and message.get("status") in _DELIVERED_MESSAGE_STATUSES
382
- )
383
- return {"assigned_task_ids": assigned_task_ids, "delivered_message_ids": delivered_message_ids}
384
-
385
-
386
- def _agent_has_stuck_relevant_work(state: dict[str, Any], store: MessageStore, agent_id: str) -> tuple[bool, str]:
387
- for task in state.get("tasks", []):
388
- if task.get("assignee") == agent_id and task.get("status", "pending") in _ACTIVE_TASK_STATUSES:
389
- return True, "active_task"
390
- for message in store.messages(owner_team_id=team_state_key(state)):
391
- if message.get("recipient") == agent_id and message.get("status") in _INBOUND_WORK_STATUSES:
392
- return True, "inbound_message"
393
- return False, "idle_no_work"
394
-
395
-
396
- def _recent_agent_progress_event(event_log: EventLog, agent_id: str, since: datetime) -> dict[str, Any] | None:
397
- for event in reversed(event_log.tail(200)):
398
- if event.get("event") not in _PROGRESS_EVENTS:
399
- continue
400
- if not _event_mentions_agent(event, agent_id):
401
- continue
402
- try:
403
- ts = datetime.fromisoformat(str(event.get("ts")))
404
- except ValueError:
405
- continue
406
- if ts.tzinfo is None:
407
- ts = ts.replace(tzinfo=timezone.utc)
408
- if ts >= since:
409
- return event
410
- return None
411
-
412
-
413
- def _event_mentions_agent(event: dict[str, Any], agent_id: str) -> bool:
414
- if event.get("agent_id") == agent_id or event.get("sender") == agent_id or event.get("target") == agent_id:
415
- return True
416
- payload = event.get("payload")
417
- return isinstance(payload, dict) and (payload.get("from") == agent_id or payload.get("to") == agent_id)
418
-
419
-
420
- def _recent_restart_or_reset_event(event_log: EventLog, agent_id: str, since: datetime) -> dict[str, Any] | None:
421
- for event in reversed(event_log.tail(200)):
422
- if event.get("event") not in _RESTART_RESET_EVENTS:
423
- continue
424
- if event.get("agent_id") != agent_id:
425
- agents_field = event.get("agents") or []
426
- agent_ids: set[str] = set()
427
- for entry in agents_field:
428
- if isinstance(entry, str):
429
- agent_ids.add(entry)
430
- elif isinstance(entry, dict):
431
- aid = entry.get("agent_id")
432
- if isinstance(aid, str):
433
- agent_ids.add(aid)
434
- if agent_id not in agent_ids:
435
- continue
436
- try:
437
- ts = datetime.fromisoformat(str(event.get("ts")))
438
- except ValueError:
439
- continue
440
- if ts.tzinfo is None:
441
- ts = ts.replace(tzinfo=timezone.utc)
442
- if ts >= since:
443
- return event
444
- return None
445
-
446
-
447
- from team_agent.messaging.idle_alerts import (
448
- detect_cross_worker_deadlocks,
449
- detect_idle_fallbacks,
450
- )