@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,388 @@
1
+ //! step 14a · mcp_server::wire — stdio loop + JSON-RPC route + tool dispatch + contracts.
2
+
3
+ use std::io::{BufRead, Write};
4
+ use std::path::Path;
5
+
6
+ use serde_json::Value;
7
+
8
+ use crate::messaging::MessageTarget;
9
+
10
+ use super::helpers::{json_dumps_default, object_fields};
11
+ use super::normalize::normalize_result_status;
12
+ use super::tools::TeamOrchestratorTools;
13
+ use super::types::{
14
+ McpError, McpTool, RpcError, RpcId, RpcMethod, RpcResponse, Scope, SendOutcome, ServerRunReport,
15
+ ToolError, ToolErrorReason, ToolOk, ToolResult,
16
+ };
17
+
18
+ // ═══════════════════════════════════════════════════════════════════════════
19
+ // CONTRACTS — TOOLS wire list (contracts.py), derived from McpTool.
20
+ // ═══════════════════════════════════════════════════════════════════════════
21
+
22
+ /// `TOOLS` (`contracts.py:4`): the `tools/list` payload — name/description/inputSchema
23
+ /// per tool. **Byte-stable wire single-truth**, derived from [`McpTool`] so name and
24
+ /// schema cannot drift from the enum. Returned verbatim by `tools/list`.
25
+ pub fn tools_contract() -> Vec<Value> {
26
+ let tools = [
27
+ McpTool::AssignTask,
28
+ McpTool::SendMessage,
29
+ McpTool::ReportResult,
30
+ McpTool::UpdateState,
31
+ McpTool::GetTeamStatus,
32
+ McpTool::StopAgent,
33
+ McpTool::ResetAgent,
34
+ McpTool::AddAgent,
35
+ McpTool::ForkAgent,
36
+ McpTool::RequestHuman,
37
+ McpTool::StuckList,
38
+ McpTool::StuckCancel,
39
+ ];
40
+ tools
41
+ .into_iter()
42
+ .map(tool_contract)
43
+ .collect()
44
+ }
45
+
46
+ // ═══════════════════════════════════════════════════════════════════════════
47
+ // SERVER — stdio loop + JSON-RPC route. THE single external MCP entry surface
48
+ // (boundary tests lock handle_mcp / dispatch / TOOLS).
49
+ // ═══════════════════════════════════════════════════════════════════════════
50
+
51
+ /// `dispatch(tools, request)` (`server.py:16-43`): route a `{tool, arguments}` (or
52
+ /// `{method, params}`) request to the matching [`TeamOrchestratorTools`] handler.
53
+ /// Unknown tool → `Err(ToolError{reason: UnknownTool})`. Argument/runtime errors
54
+ /// from the handler propagate as the handler's own [`ToolResult`]; the
55
+ /// argument-vs-internal exception split happens in [`handle_mcp`].
56
+ pub fn dispatch(tools: &TeamOrchestratorTools, request: &Value) -> ToolResult {
57
+ let tool_value = request
58
+ .get("tool")
59
+ .filter(|v| !v.as_str().is_some_and(str::is_empty))
60
+ .or_else(|| request.get("name").filter(|v| !v.as_str().is_some_and(str::is_empty)))
61
+ .or_else(|| request.get("method"));
62
+ let name = tool_value.and_then(Value::as_str);
63
+ let args = request
64
+ .get("arguments")
65
+ .or_else(|| request.get("params"))
66
+ .unwrap_or(&Value::Null);
67
+ let Some(name) = name else {
68
+ return Err(ToolError::new(
69
+ ToolErrorReason::UnknownTool,
70
+ "unknown tool None",
71
+ "UnknownTool",
72
+ ));
73
+ };
74
+ let Some(tool) = McpTool::parse(name) else {
75
+ return Err(ToolError::new(
76
+ ToolErrorReason::UnknownTool,
77
+ format!("unknown tool {}", python_repr(name)),
78
+ "UnknownTool",
79
+ ));
80
+ };
81
+ dispatch_tool(tools, tool, args)
82
+ }
83
+
84
+ /// `handle_mcp(tools, request)` (`server.py:46-91`): the JSON-RPC router.
85
+ /// - `initialize` → serverInfo `team_orchestrator` v0.1.4 + echoed protocolVersion.
86
+ /// - `tools/list` → `{tools: TOOLS}`.
87
+ /// - `tools/call` → run [`dispatch`], wrap into [`ToolCallResult`] (`isError` =
88
+ /// dispatch returned `Err`); the arg-vs-runtime exception split (`server.py:
89
+ /// 69-72`) classifies a caught failure into `InvalidToolArguments` vs
90
+ /// `InternalRuntimeError`.
91
+ /// - `notifications/*` → `Ok(None)` (no frame; **must not** emit to stdout).
92
+ /// - unknown method → `-32601` error frame.
93
+ /// Returns `Ok(None)` only for the notifications path; every other branch yields a
94
+ /// frame.
95
+ ///
96
+ /// [`ToolCallResult`]: super::ToolCallResult
97
+ pub fn handle_mcp(tools: &TeamOrchestratorTools, request: &Value) -> Result<Option<RpcResponse>, McpError> {
98
+ let id = rpc_id_from_request(request);
99
+ let method = request.get("method").and_then(Value::as_str).unwrap_or("");
100
+ match RpcMethod::classify(method) {
101
+ RpcMethod::Initialize => {
102
+ let protocol = request
103
+ .get("params")
104
+ .and_then(|p| p.get("protocolVersion"))
105
+ .and_then(Value::as_str)
106
+ .unwrap_or("2024-11-05");
107
+ let mut result = serde_json::Map::new();
108
+ result.insert("protocolVersion".to_string(), Value::String(protocol.to_string()));
109
+ result.insert("capabilities".to_string(), serde_json::json!({"tools": {}}));
110
+ result.insert(
111
+ "serverInfo".to_string(),
112
+ serde_json::json!({"name": "team_orchestrator", "version": "0.1.4"}),
113
+ );
114
+ Ok(Some(RpcResponse {
115
+ jsonrpc: "2.0".to_string(),
116
+ id,
117
+ result: Some(Value::Object(result)),
118
+ error: None,
119
+ }))
120
+ }
121
+ RpcMethod::ToolsList => Ok(Some(RpcResponse {
122
+ jsonrpc: "2.0".to_string(),
123
+ id,
124
+ result: Some(serde_json::json!({ "tools": tools_contract() })),
125
+ error: None,
126
+ })),
127
+ RpcMethod::ToolsCall => {
128
+ let params = request.get("params").unwrap_or(&Value::Null);
129
+ let body = match dispatch(tools, params) {
130
+ Ok(ok) => {
131
+ let value = Value::Object(ok.fields);
132
+ tool_call_result_value(value.get("ok").and_then(Value::as_bool) == Some(false), &value)
133
+ }
134
+ Err(err) => tool_call_result_value(true, &err.to_envelope()),
135
+ };
136
+ Ok(Some(RpcResponse {
137
+ jsonrpc: "2.0".to_string(),
138
+ id,
139
+ result: Some(body),
140
+ error: None,
141
+ }))
142
+ }
143
+ RpcMethod::Notification(_) => Ok(None),
144
+ RpcMethod::Unknown(method) => Ok(Some(RpcResponse {
145
+ jsonrpc: "2.0".to_string(),
146
+ id,
147
+ result: None,
148
+ error: Some(RpcError {
149
+ code: -32601,
150
+ message: format!("unknown method '{method}'"),
151
+ }),
152
+ })),
153
+ }
154
+ }
155
+
156
+ /// `main(argv)` (`server.py:114-151`): the stdio process entry. Reads stdin line by
157
+ /// line; for `jsonrpc:"2.0"` lines routes via [`handle_mcp`] and writes the response
158
+ /// frame (skipping `None`); legacy `{tool,...}` lines go straight to [`dispatch`].
159
+ /// **All errors surface as JSON-RPC frames on stdout**; the loop never crashes the
160
+ /// process. Returns a [`ServerRunReport`] summarizing the session for tests/daemon.
161
+ pub fn main(workspace: &Path, argv: &[String]) -> Result<ServerRunReport, McpError> {
162
+ let _ = argv;
163
+ let stdin = std::io::stdin();
164
+ let stdout = std::io::stdout();
165
+ run_stdio_loop(workspace, stdin.lock(), stdout.lock())
166
+ }
167
+
168
+ fn run_stdio_loop<R: BufRead, W: Write>(
169
+ workspace: &Path,
170
+ reader: R,
171
+ mut writer: W,
172
+ ) -> Result<ServerRunReport, McpError> {
173
+ let tools = TeamOrchestratorTools::new(workspace);
174
+ let mut report = ServerRunReport::default();
175
+ for line in reader.lines() {
176
+ let line = line?;
177
+ report.requests_read = report.requests_read.saturating_add(1);
178
+ let frame = handle_stdin_line(&tools, &line, &mut report)?;
179
+ if let Some(value) = frame {
180
+ serde_json::to_writer(&mut writer, &value)?;
181
+ writer.write_all(b"\n")?;
182
+ writer.flush()?;
183
+ report.responses_written = report.responses_written.saturating_add(1);
184
+ }
185
+ }
186
+ report.clean_eof = true;
187
+ Ok(report)
188
+ }
189
+
190
+ fn handle_stdin_line(
191
+ tools: &TeamOrchestratorTools,
192
+ line: &str,
193
+ report: &mut ServerRunReport,
194
+ ) -> Result<Option<Value>, McpError> {
195
+ let request: Value = match serde_json::from_str(line) {
196
+ Ok(value) => value,
197
+ Err(err) => {
198
+ report.error_frames = report.error_frames.saturating_add(1);
199
+ return Ok(Some(error_response_value(
200
+ RpcId::Null,
201
+ -32700,
202
+ format!("parse error: {err}"),
203
+ )));
204
+ }
205
+ };
206
+ if request.get("jsonrpc").and_then(Value::as_str) == Some("2.0") {
207
+ match handle_mcp(tools, &request)? {
208
+ Some(response) => {
209
+ if response.error.is_some() {
210
+ report.error_frames = report.error_frames.saturating_add(1);
211
+ }
212
+ Ok(Some(serde_json::to_value(response)?))
213
+ }
214
+ None => {
215
+ report.notifications_skipped = report.notifications_skipped.saturating_add(1);
216
+ Ok(None)
217
+ }
218
+ }
219
+ } else {
220
+ let value = match dispatch(tools, &request) {
221
+ Ok(ok) => Value::Object(ok.fields),
222
+ Err(err) => {
223
+ report.error_frames = report.error_frames.saturating_add(1);
224
+ err.to_envelope()
225
+ }
226
+ };
227
+ Ok(Some(value))
228
+ }
229
+ }
230
+
231
+ fn rpc_id_from_request(request: &Value) -> RpcId {
232
+ match request.get("id") {
233
+ Some(Value::Number(n)) => n.as_i64().map_or_else(|| RpcId::Number(n.clone()), RpcId::Int),
234
+ Some(Value::String(s)) => RpcId::Str(s.clone()),
235
+ _ => RpcId::Null,
236
+ }
237
+ }
238
+
239
+ fn tool_call_result_value(is_error: bool, body: &Value) -> Value {
240
+ let text = json_dumps_default(body);
241
+ let mut content = serde_json::Map::new();
242
+ content.insert("type".to_string(), Value::String("text".to_string()));
243
+ content.insert("text".to_string(), Value::String(text));
244
+ serde_json::json!({
245
+ "content": [Value::Object(content)],
246
+ "isError": is_error
247
+ })
248
+ }
249
+
250
+ fn error_response_value(id: RpcId, code: i64, message: String) -> Value {
251
+ let response = RpcResponse {
252
+ jsonrpc: "2.0".to_string(),
253
+ id,
254
+ result: None,
255
+ error: Some(RpcError { code, message }),
256
+ };
257
+ match serde_json::to_value(response) {
258
+ Ok(value) => value,
259
+ Err(_) => serde_json::json!({
260
+ "jsonrpc": "2.0",
261
+ "id": null,
262
+ "error": {
263
+ "code": -32000,
264
+ "message": "internal runtime error"
265
+ }
266
+ }),
267
+ }
268
+ }
269
+
270
+ fn python_repr(value: &str) -> String {
271
+ if value.contains('\'') && !value.contains('"') {
272
+ format!("\"{}\"", value.replace('"', "\\\""))
273
+ } else {
274
+ format!("'{}'", value.replace('\'', "\\'"))
275
+ }
276
+ }
277
+
278
+ fn tool_contract(tool: McpTool) -> Value {
279
+ let (description, required) = match tool {
280
+ McpTool::SendMessage => (
281
+ "Send a message to a teammate, the leader, or '*' for all other team members. Provide only target and content; Team Agent fills sender, task id, ack policy, and delivery metadata.",
282
+ vec!["to", "content"],
283
+ ),
284
+ McpTool::AssignTask => ("Assign or update a task in the team graph and deliver it to its assignee.", vec!["task"]),
285
+ McpTool::ReportResult => ("Report task completion with a result envelope.", Vec::new()),
286
+ McpTool::UpdateState => ("Append a note to team state and rewrite team_state.md.", vec!["note"]),
287
+ McpTool::GetTeamStatus => ("Return machine-readable team status.", Vec::new()),
288
+ McpTool::StopAgent => ("Stop a running worker.", vec!["agent_id"]),
289
+ McpTool::ResetAgent => ("Reset one worker to a fresh session.", vec!["agent_id", "discard_session"]),
290
+ McpTool::AddAgent => ("Add a first-class worker from a role file.", vec!["new_agent_id", "role_file_path"]),
291
+ McpTool::ForkAgent => ("Fork a running worker.", vec!["source_agent_id", "as_agent_id"]),
292
+ McpTool::RequestHuman => ("Ask the leader or user for human input.", vec!["question"]),
293
+ McpTool::StuckList => ("List manually suppressed idle alerts.", Vec::new()),
294
+ McpTool::StuckCancel => ("Suppress repeated stuck or idle alerts.", vec!["agent_id"]),
295
+ };
296
+ serde_json::json!({
297
+ "name": tool.wire_name(),
298
+ "description": description,
299
+ "inputSchema": {
300
+ "type": "object",
301
+ "properties": {},
302
+ "required": required,
303
+ "additionalProperties": false
304
+ }
305
+ })
306
+ }
307
+
308
+ pub(crate) fn dispatch_tool(tools: &TeamOrchestratorTools, tool: McpTool, args: &Value) -> ToolResult {
309
+ match tool {
310
+ McpTool::AssignTask => tools.assign_task(args.get("task").unwrap_or(args), args.get("message").and_then(Value::as_str)),
311
+ McpTool::SendMessage => {
312
+ let target = message_target_from_value(args.get("to"));
313
+ let content = args.get("content").and_then(Value::as_str).unwrap_or("");
314
+ let scope = match args.get("scope").and_then(Value::as_str) {
315
+ Some("workspace") => Some(Scope::Workspace),
316
+ Some("team") => Some(Scope::Team),
317
+ _ => None,
318
+ };
319
+ let outcome = tools.send_message(
320
+ &target,
321
+ content,
322
+ args.get("task_id").and_then(Value::as_str),
323
+ args.get("sender").and_then(Value::as_str),
324
+ args.get("requires_ack").and_then(Value::as_bool),
325
+ scope,
326
+ )?;
327
+ match outcome {
328
+ SendOutcome::WorkerAccepted { .. } => Ok(ToolOk {
329
+ fields: object_fields(outcome.to_value()),
330
+ }),
331
+ SendOutcome::Direct(ok) => Ok(ok),
332
+ }
333
+ }
334
+ McpTool::ReportResult => tools.report_result(
335
+ args.get("envelope"),
336
+ args.get("summary").and_then(Value::as_str),
337
+ normalize_result_status(args.get("status").and_then(Value::as_str)),
338
+ args.get("changes").and_then(Value::as_array).map(Vec::as_slice),
339
+ args.get("tests").and_then(Value::as_array).map(Vec::as_slice),
340
+ args.get("risks").and_then(Value::as_array).map(Vec::as_slice),
341
+ args.get("artifacts").and_then(Value::as_array).map(Vec::as_slice),
342
+ args.get("next_actions").and_then(Value::as_array).map(Vec::as_slice),
343
+ args.get("task_id").and_then(Value::as_str),
344
+ args.get("agent_id").and_then(Value::as_str),
345
+ ),
346
+ McpTool::UpdateState => tools.update_state(args.get("note").and_then(Value::as_str).unwrap_or("")),
347
+ McpTool::GetTeamStatus => tools.get_team_status(),
348
+ McpTool::StopAgent => tools.stop_agent(args.get("agent_id").and_then(Value::as_str).unwrap_or("")),
349
+ McpTool::ResetAgent => tools.reset_agent(
350
+ args.get("agent_id").and_then(Value::as_str).unwrap_or(""),
351
+ args.get("discard_session").and_then(Value::as_bool).unwrap_or(false),
352
+ ),
353
+ McpTool::AddAgent => tools.add_agent(
354
+ args.get("new_agent_id").and_then(Value::as_str).unwrap_or(""),
355
+ args.get("role_file_path").and_then(Value::as_str).unwrap_or(""),
356
+ ),
357
+ McpTool::ForkAgent => tools.fork_agent(
358
+ args.get("source_agent_id").and_then(Value::as_str).unwrap_or(""),
359
+ args.get("as_agent_id").and_then(Value::as_str).unwrap_or(""),
360
+ args.get("label").and_then(Value::as_str),
361
+ ),
362
+ McpTool::RequestHuman => tools.request_human(
363
+ args.get("question").and_then(Value::as_str).unwrap_or(""),
364
+ args.get("task_id").and_then(Value::as_str),
365
+ args.get("agent_id").and_then(Value::as_str),
366
+ ),
367
+ McpTool::StuckList => tools.stuck_list(),
368
+ McpTool::StuckCancel => tools.stuck_cancel(
369
+ args.get("agent_id").and_then(Value::as_str).unwrap_or(""),
370
+ args.get("alert_type").and_then(Value::as_str).unwrap_or("all"),
371
+ ),
372
+ }
373
+ }
374
+
375
+ fn message_target_from_value(value: Option<&Value>) -> MessageTarget {
376
+ match value {
377
+ Some(Value::String(s)) if s == "*" => MessageTarget::Broadcast,
378
+ Some(Value::String(s)) => MessageTarget::Single(s.clone()),
379
+ Some(Value::Array(items)) => MessageTarget::Fanout(
380
+ items
381
+ .iter()
382
+ .filter_map(Value::as_str)
383
+ .map(ToString::to_string)
384
+ .collect(),
385
+ ),
386
+ _ => MessageTarget::Single(String::new()),
387
+ }
388
+ }