@team-agent/installer 0.2.11 → 0.3.1

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 +1204 -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 +1207 -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 +557 -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 +1084 -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 +526 -0
  59. package/crates/team-agent/src/leader/rediscover.rs +1101 -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 +237 -0
  64. package/crates/team-agent/src/leader/tests/identity.rs +206 -0
  65. package/crates/team-agent/src/leader/tests/idle.rs +272 -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 +410 -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 +489 -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 +2109 -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 +985 -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 +710 -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 +187 -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 +468 -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 +743 -0
  110. package/crates/team-agent/src/messaging/helpers.rs +209 -0
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +329 -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 +553 -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 +578 -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 +659 -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 +765 -0
  172. package/crates/team-agent/src/tmux_backend.rs +810 -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 +118 -112
  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
@@ -1,352 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- from pathlib import Path
6
- from typing import Any
7
-
8
- from team_agent import runtime
9
- from team_agent.events import EventLog
10
- from team_agent.message_store import MessageStore
11
- from team_agent.state import load_runtime_state, save_runtime_state, write_team_state
12
-
13
- from team_agent.mcp_server.normalize import _compact_tool_result, _normalize_report_envelope, _text
14
-
15
-
16
- def _requires_ack_for_target(to: str | list[str]) -> bool:
17
- if isinstance(to, list):
18
- return any(target not in {"leader", "Leader"} for target in to)
19
- return to not in {"leader", "Leader"}
20
-
21
-
22
- def _is_worker_recipient(to: str | list[str]) -> bool:
23
- if not isinstance(to, str):
24
- return False
25
- if to in {"", "*", "leader", "Leader"}:
26
- return False
27
- return True
28
-
29
-
30
- def _merge_tasks_by_id(prefer: list[Any], fallback: list[Any]) -> list[dict[str, Any]]:
31
- """Build a deduped task list keyed by ``id``.
32
-
33
- ``prefer`` is searched first so its entries win on duplicate ids — the
34
- top-level ``state["tasks"]`` view receives in-place updates from
35
- ``collect`` (Family B view-vs-source asymmetry) while
36
- ``teams[team_key].tasks`` may have stayed pre-collect. Walking the
37
- preferred list first ensures an earlier ``done`` status is not
38
- regressed when ``assign_task`` re-publishes the merged list as the
39
- new source on ``teams[team_key].tasks``.
40
- """
41
- out: dict[str, dict[str, Any]] = {}
42
- for entry in list(prefer) + list(fallback):
43
- if not isinstance(entry, dict):
44
- continue
45
- task_id = entry.get("id")
46
- if not task_id:
47
- continue
48
- out.setdefault(str(task_id), entry)
49
- return list(out.values())
50
-
51
-
52
- def _latest_task_for_assignee(state: dict[str, Any], agent_id: str | None) -> str | None:
53
- """Return the most recently registered, non-terminal task id assigned
54
- to ``agent_id``. Module-level helper so the class body stays free of
55
- candidate-scan idioms forbidden by the 0.2.6 Family C contract."""
56
- if not agent_id:
57
- return None
58
- tasks = state.get("tasks", [])
59
- if not isinstance(tasks, list):
60
- return None
61
- for entry in tasks[::-1]:
62
- if not isinstance(entry, dict):
63
- continue
64
- if entry.get("assignee") != agent_id:
65
- continue
66
- if entry.get("status") in {"done", "failed"}:
67
- continue
68
- return str(entry.get("id") or "")
69
- return None
70
-
71
-
72
- class TeamOrchestratorTools:
73
- """0.2.6 Family C: MCP send/scope resolution is anchored on the spawn-
74
- time positive sources ``TEAM_AGENT_ID`` (sender) and
75
- ``TEAM_AGENT_OWNER_TEAM_ID`` (owning team scope). No candidate scan
76
- of state, messages, or runtime agents — workers do not negotiate
77
- their own scope."""
78
-
79
- def __init__(self, workspace: Path):
80
- self.workspace = workspace.resolve()
81
- self.agent_id = _text(os.environ.get("TEAM_AGENT_ID"))
82
- self.owner_team_id = _text(os.environ.get("TEAM_AGENT_OWNER_TEAM_ID"))
83
-
84
- def assign_task(self, task: dict[str, Any], message: str | None = None) -> dict[str, Any]:
85
- # 0.2.6 Family B (C8): the source of truth for tasks lives in
86
- # ``state.teams[team_key].tasks``; the top-level ``tasks`` field is
87
- # a derived view. Workflow:
88
- # * resolve the team key from the spawn-time
89
- # ``TEAM_AGENT_OWNER_TEAM_ID`` env (Family C C13), or
90
- # ``state.active_team_key`` for legacy single-team callers.
91
- # * reconcile teams[team_key].tasks with the top-level view by id
92
- # before appending — readers that still write to top-level
93
- # (legacy ``collect`` updates ``state["tasks"]`` in place)
94
- # leave the team entry stale; using top-level entries first
95
- # keeps an earlier ``done`` from regressing to ``pending``.
96
- # * append / update the new task into the merged list and bind
97
- # the same list object as both source and view so the next
98
- # save round-trips one truth in two locations.
99
- state = load_runtime_state(self.workspace)
100
- team_key = (
101
- self.owner_team_id
102
- or _text(state.get("active_team_key"))
103
- or ""
104
- )
105
- teams = state.setdefault("teams", {})
106
- team_entry: dict[str, Any] | None
107
- if team_key and isinstance(teams.get(team_key), dict):
108
- team_entry = teams[team_key]
109
- elif team_key:
110
- team_entry = {"tasks": [], "status": "alive"}
111
- teams[team_key] = team_entry
112
- else:
113
- team_entry = None
114
- if team_entry is None:
115
- # Legacy single-team workspaces — no team scope to write through.
116
- target_tasks = state.setdefault("tasks", [])
117
- else:
118
- top_view = state.get("tasks")
119
- team_tasks = team_entry.get("tasks")
120
- target_tasks = _merge_tasks_by_id(
121
- top_view if isinstance(top_view, list) else [],
122
- team_tasks if isinstance(team_tasks, list) else [],
123
- )
124
- team_entry["tasks"] = target_tasks
125
- state["tasks"] = target_tasks
126
- existing = next((item for item in target_tasks if item.get("id") == task.get("id")), None)
127
- if existing:
128
- existing.update(task)
129
- else:
130
- target_tasks.append(task)
131
- save_runtime_state(self.workspace, state)
132
- content = message or task.get("description") or task.get("title") or json.dumps(task)
133
- return _compact_tool_result(runtime.send_message(self.workspace, task.get("assignee"), content, task_id=task["id"]))
134
-
135
- def send_message(
136
- self,
137
- to: str | list[str],
138
- content: str,
139
- task_id: str | None = None,
140
- sender: str | None = None,
141
- requires_ack: bool | None = None,
142
- scope: str | None = None,
143
- ) -> dict[str, Any]:
144
- # 0.2.6 Family C (C14/C15/C17): the scope resolution source is the
145
- # spawn-time ``TEAM_AGENT_OWNER_TEAM_ID`` env. ``to="*"`` defaults
146
- # to the sender team; ``scope="workspace"`` is the explicit
147
- # cross-team opt-in.
148
- inferred_target = to if isinstance(to, str) else None
149
- effective_sender = sender or self.agent_id or self._sender_from_env(target=inferred_target) or "unknown"
150
- effective_requires_ack = requires_ack if requires_ack is not None else _requires_ack_for_target(to)
151
- # 0.2.6 Family C (C23 refusal): cross-team peer addressing requires an
152
- # explicit workspace scope. Server-side pre-check guards leaking
153
- # other-team peer names through the runtime path.
154
- refusal = self._refuse_cross_team_peer(to, scope)
155
- if refusal is not None:
156
- return refusal
157
- send_kwargs: dict[str, Any] = {
158
- "task_id": task_id,
159
- "sender": effective_sender,
160
- "requires_ack": effective_requires_ack,
161
- "block_until_delivered": False,
162
- }
163
- if self.owner_team_id:
164
- send_kwargs["team"] = self.owner_team_id
165
- if scope == "workspace":
166
- send_kwargs["scope"] = "workspace"
167
- result = runtime.send_message(self.workspace, to, content, **send_kwargs)
168
- EventLog(self.workspace).write(
169
- "mcp.scope_resolved",
170
- sender_team_id=self.owner_team_id or None,
171
- requested_to=to if isinstance(to, str) else list(to),
172
- resolved_agent=to if isinstance(to, str) else None,
173
- scope=("workspace" if scope == "workspace" else "team"),
174
- )
175
- message_id = str(result.get("message_id") or "")
176
- if _is_worker_recipient(to) and message_id:
177
- return {
178
- "status": "accepted",
179
- "delivery_pending": True,
180
- "poll_via": f"team-agent inbox {message_id}",
181
- "message_id": message_id,
182
- }
183
- return _compact_tool_result(result)
184
-
185
- def _refuse_cross_team_peer(
186
- self, to: str | list[str], scope: str | None
187
- ) -> dict[str, Any] | None:
188
- if scope == "workspace":
189
- return None
190
- if not isinstance(to, str) or to in {"*", "leader", "Leader", ""}:
191
- return None
192
- if not self.owner_team_id:
193
- return None
194
- visible = set(self.get_visible_peers().get("peers") or [])
195
- if to in visible or to == self.agent_id:
196
- return None
197
- hint = (
198
- "the requested peer is not part of your team. "
199
- "pass scope='workspace' to address peers in other teams."
200
- )
201
- EventLog(self.workspace).write(
202
- "mcp.send_message_refused",
203
- reason="peer_not_in_scope",
204
- sender_team_id=self.owner_team_id,
205
- scope="team",
206
- hint=hint,
207
- )
208
- return {
209
- "ok": False,
210
- "status": "refused",
211
- "reason": "peer_not_in_scope",
212
- "hint": hint,
213
- }
214
-
215
- def _sender_from_env(self, *, target: str | None) -> str | None:
216
- if self.agent_id:
217
- return self.agent_id
218
- EventLog(self.workspace).write(
219
- "mcp.identity_inference_failed",
220
- target=target,
221
- sender_team_id=self.owner_team_id or None,
222
- fallback="unknown",
223
- )
224
- return None
225
-
226
- def get_visible_peers(self) -> dict[str, Any]:
227
- """0.2.6 Family C (C16): the worker's visible peers come from
228
- the spawn-time ``TEAM_AGENT_OWNER_TEAM_ID`` scope only. Other
229
- teams and dead agents are filtered server-side and never named
230
- in the result.
231
- """
232
- state = load_runtime_state(self.workspace)
233
- scope_team = self.owner_team_id or ""
234
- teams = state.get("teams") if isinstance(state.get("teams"), dict) else {}
235
- team_entry = teams.get(scope_team) if scope_team else {}
236
- agents = team_entry.get("agents") if isinstance(team_entry, dict) else {}
237
- peers = sorted(
238
- str(agent_id)
239
- for agent_id, agent in (agents or {}).items()
240
- if isinstance(agent, dict) and str(agent.get("status") or "alive").lower() not in {"dead", "stopped"}
241
- or not isinstance(agent, dict)
242
- )
243
- return {
244
- "peers": peers,
245
- "sender_team_id": scope_team or None,
246
- "scope": "team",
247
- }
248
-
249
- def report_result(
250
- self,
251
- envelope: dict[str, Any] | None = None,
252
- summary: str | None = None,
253
- status: str = "success",
254
- changes: list[dict[str, Any]] | None = None,
255
- tests: list[dict[str, Any]] | None = None,
256
- risks: list[dict[str, Any]] | None = None,
257
- artifacts: list[dict[str, Any]] | None = None,
258
- next_actions: list[dict[str, Any]] | None = None,
259
- task_id: str | None = None,
260
- agent_id: str | None = None,
261
- ) -> dict[str, Any]:
262
- env = dict(envelope or {})
263
- effective_task = self._infer_task_id(
264
- agent_id or _text(env.get("agent_id")) or self.agent_id,
265
- task_id or _text(env.get("task_id")),
266
- )
267
- effective_agent = agent_id or _text(env.get("agent_id")) or self._infer_agent_id(task_id=effective_task) or "unknown"
268
- env.setdefault("schema_version", "result_envelope_v1")
269
- env.setdefault("agent_id", effective_agent)
270
- env.setdefault("task_id", effective_task)
271
- env.setdefault("status", status)
272
- env.setdefault("summary", summary or env.get("summary") or "completed")
273
- env.setdefault("changes", changes if changes is not None else [])
274
- env.setdefault("tests", tests if tests is not None else [])
275
- env.setdefault("risks", risks if risks is not None else [])
276
- env.setdefault("artifacts", artifacts if artifacts is not None else [])
277
- env.setdefault("next_actions", next_actions if next_actions is not None else [])
278
- env = _normalize_report_envelope(env)
279
- return _compact_tool_result(runtime.report_result(self.workspace, env))
280
-
281
- def _infer_agent_id(self, provided: str | None = None, task_id: str | None = None, target: str | None = None) -> str | None:
282
- # 0.2.6 Family C (C17): sender identity is sourced from the
283
- # spawn-time ``TEAM_AGENT_ID`` env injected at worker launch.
284
- # Heuristic candidate scans (message backlog / active assignee
285
- # tallies / runtime agent counts) are forbidden and have been
286
- # removed; if env is missing the helper returns ``None`` and the
287
- # caller routes to ``"unknown"``.
288
- if _text(provided):
289
- return _text(provided)
290
- if self.agent_id:
291
- return self.agent_id
292
- EventLog(self.workspace).write(
293
- "mcp.identity_inference_failed",
294
- target=target,
295
- task_id=task_id,
296
- sender_team_id=self.owner_team_id or None,
297
- fallback="unknown",
298
- )
299
- return None
300
-
301
- def _infer_task_id(self, agent_id: str | None, provided: str | None = None) -> str:
302
- if provided:
303
- return provided
304
- state = load_runtime_state(self.workspace)
305
- latest = _latest_task_for_assignee(state, agent_id)
306
- if latest:
307
- return latest
308
- EventLog(self.workspace).write("mcp.task_inference_failed", agent_id=agent_id, fallback="manual")
309
- return "manual"
310
-
311
- def _task_for_id(self, state: dict[str, Any], task_id: str | None) -> dict[str, Any] | None:
312
- if not task_id:
313
- return None
314
- return next((task for task in state.get("tasks", []) if task.get("id") == task_id), None)
315
-
316
- def update_state(self, note: str) -> dict[str, Any]:
317
- state = load_runtime_state(self.workspace)
318
- spec_path = Path(state.get("spec_path", self.workspace / "team.spec.yaml"))
319
- from team_agent.spec import load_spec
320
-
321
- spec = load_spec(spec_path)
322
- state.setdefault("notes", []).append(note)
323
- save_runtime_state(self.workspace, state)
324
- path = write_team_state(self.workspace, spec, state)
325
- return {"ok": True, "state_file": str(path)}
326
-
327
- def get_team_status(self) -> dict[str, Any]:
328
- return runtime.status(self.workspace, as_json=True, compact=True)
329
-
330
- def stop_agent(self, agent_id: str) -> dict[str, Any]:
331
- return _compact_tool_result(runtime.stop_agent(self.workspace, agent_id))
332
-
333
- def reset_agent(self, agent_id: str, discard_session: bool = False) -> dict[str, Any]:
334
- return _compact_tool_result(runtime.reset_agent(self.workspace, agent_id, discard_session=discard_session))
335
-
336
- def add_agent(self, new_agent_id: str, role_file_path: str) -> dict[str, Any]:
337
- return _compact_tool_result(runtime.add_agent(self.workspace, new_agent_id, role_file_path=role_file_path))
338
-
339
- def fork_agent(self, source_agent_id: str, as_agent_id: str, label: str | None = None) -> dict[str, Any]:
340
- return _compact_tool_result(runtime.fork_agent(self.workspace, source_agent_id, as_agent_id=as_agent_id, label=label))
341
-
342
- def request_human(self, question: str, task_id: str | None = None, agent_id: str | None = None) -> dict[str, Any]:
343
- store = MessageStore(self.workspace)
344
- sender = agent_id or self._infer_agent_id(task_id=task_id, target="leader") or "unknown"
345
- message_id = store.create_message(task_id, sender, "leader", question, requires_ack=True)
346
- return {"ok": True, "message_id": message_id, "status": "needs_human"}
347
-
348
- def stuck_list(self) -> dict[str, Any]:
349
- return runtime.stuck_list(self.workspace)
350
-
351
- def stuck_cancel(self, agent_id: str, alert_type: str = "stuck") -> dict[str, Any]:
352
- return runtime.stuck_cancel(self.workspace, agent_id, alert_type=alert_type, suppressed_by=self.agent_id or "leader")
@@ -1,23 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from team_agent.message_store.agent_health import delete_agent_health, gc_agent_health, upsert_agent_health
4
- from team_agent.message_store.core import MessageStore
5
- from team_agent.message_store.result_watchers import create_result_watcher, mark_result_watcher
6
- from team_agent.message_store.schema import SCHEMA_VERSION, initialize_schema, utcnow
7
-
8
- _REQUIRED_EXPORTS = (
9
- "MessageStore",
10
- "SCHEMA_VERSION",
11
- "initialize_schema",
12
- "utcnow",
13
- "upsert_agent_health",
14
- "delete_agent_health",
15
- "gc_agent_health",
16
- "create_result_watcher",
17
- "mark_result_watcher",
18
- )
19
- for _name in _REQUIRED_EXPORTS:
20
- if _name not in globals():
21
- raise ImportError(f"team_agent.message_store missing export: {_name}")
22
-
23
- __all__ = list(_REQUIRED_EXPORTS)
@@ -1,113 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from contextlib import closing
4
- from typing import Any
5
-
6
- from team_agent.message_store.schema_migration import MANAGED_TABLE_LAYOUTS
7
- from team_agent.message_store.schema import utcnow
8
-
9
-
10
- AGENT_HEALTH_SELECT = ", ".join(MANAGED_TABLE_LAYOUTS["agent_health"])
11
-
12
-
13
- def upsert_agent_health(
14
- self,
15
- agent_id: str,
16
- status: str,
17
- last_output_at: str | None = None,
18
- context_usage_pct: int | None = None,
19
- current_task_id: str | None = None,
20
- owner_team_id: str | None = None,
21
- ) -> None:
22
- now = utcnow()
23
- with closing(self.connect()) as conn:
24
- with conn:
25
- if owner_team_id is None:
26
- updated = conn.execute(
27
- """
28
- update agent_health
29
- set status = ?,
30
- last_output_at = coalesce(?, last_output_at),
31
- context_usage_pct = ?,
32
- current_task_id = ?,
33
- updated_at = ?
34
- where owner_team_id is null and agent_id = ?
35
- """,
36
- (status, last_output_at, context_usage_pct, current_task_id, now, agent_id),
37
- )
38
- if updated.rowcount:
39
- return
40
- conn.execute(
41
- """
42
- insert into agent_health(owner_team_id, agent_id, status, last_output_at, context_usage_pct, current_task_id, updated_at)
43
- values (?, ?, ?, ?, ?, ?, ?)
44
- on conflict(owner_team_id, agent_id) do update set
45
- status = excluded.status,
46
- last_output_at = coalesce(excluded.last_output_at, agent_health.last_output_at),
47
- context_usage_pct = excluded.context_usage_pct,
48
- current_task_id = excluded.current_task_id,
49
- updated_at = excluded.updated_at
50
- """,
51
- (owner_team_id, agent_id, status, last_output_at, context_usage_pct, current_task_id, now),
52
- )
53
-
54
- def agent_health(self, owner_team_id: str | None = None) -> dict[str, dict[str, Any]]:
55
- with closing(self.connect()) as conn:
56
- if owner_team_id is None:
57
- rows = conn.execute(f"select {AGENT_HEALTH_SELECT} from agent_health order by agent_id").fetchall()
58
- else:
59
- rows = conn.execute(
60
- f"select {AGENT_HEALTH_SELECT} from agent_health where owner_team_id = ? or owner_team_id is null order by agent_id",
61
- (owner_team_id,),
62
- ).fetchall()
63
- return {row["agent_id"]: dict(row) for row in rows}
64
-
65
- def delete_agent_health(self, agent_id: str, owner_team_id: str | None = None) -> bool:
66
- with closing(self.connect()) as conn:
67
- with conn:
68
- if owner_team_id is None:
69
- cur = conn.execute("delete from agent_health where agent_id = ?", (agent_id,))
70
- else:
71
- cur = conn.execute(
72
- "delete from agent_health where agent_id = ? and (owner_team_id = ? or owner_team_id is null)",
73
- (agent_id, owner_team_id),
74
- )
75
- return cur.rowcount > 0
76
-
77
- def gc_agent_health(self, valid_agent_ids: Any, owner_team_id: str | None = None) -> list[str]:
78
- # Caller must pass the workspace-wide set of live agent_ids across every
79
- # team sharing this team.db. Rows whose agent_id is not in the set are
80
- # deleted. If two teams share a workspace, the caller is responsible for
81
- # computing the union before invoking this helper; otherwise live agents
82
- # from a sibling team will be swept. Input is validated before any DB
83
- # mutation so a derivation bug that silently produces None or non-str
84
- # entries cannot delete sibling-team rows by accident.
85
- valid: set[str] = set()
86
- for entry in valid_agent_ids:
87
- if not isinstance(entry, str):
88
- raise TypeError(
89
- f"gc_agent_health requires str agent_ids; got {type(entry).__name__}"
90
- )
91
- if not entry:
92
- raise ValueError("gc_agent_health does not accept empty agent_ids")
93
- valid.add(entry)
94
- with closing(self.connect()) as conn:
95
- with conn:
96
- if owner_team_id is None:
97
- rows = conn.execute("select agent_id from agent_health").fetchall()
98
- else:
99
- rows = conn.execute(
100
- "select agent_id from agent_health where owner_team_id = ? or owner_team_id is null",
101
- (owner_team_id,),
102
- ).fetchall()
103
- stale = [row["agent_id"] for row in rows if row["agent_id"] not in valid]
104
- if stale:
105
- placeholders = ",".join("?" for _ in stale)
106
- if owner_team_id is None:
107
- conn.execute(f"delete from agent_health where agent_id in ({placeholders})", stale)
108
- else:
109
- conn.execute(
110
- f"delete from agent_health where agent_id in ({placeholders}) and (owner_team_id = ? or owner_team_id is null)",
111
- [*stale, owner_team_id],
112
- )
113
- return stale