@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,511 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import os
4
- import platform as platform_module
5
- from pathlib import Path
6
- from typing import Any
7
-
8
- from team_agent.display.ghostty import ghostty_display_session_name
9
- from team_agent.display.tiling import (
10
- display_pane_title,
11
- prepare_tmux_attached_panes,
12
- set_tmux_display_pane_title,
13
- team_scoped_display_window_name,
14
- )
15
- from team_agent.display.workspace import (
16
- kill_ghostty_workspace_linked_sessions,
17
- )
18
- from team_agent.events import EventLog
19
-
20
-
21
- ADAPTIVE_BLOCK_REASONS = {
22
- "leader_not_in_tmux",
23
- "split_failed",
24
- "window_create_failed",
25
- "worker_session_missing",
26
- "not_implemented_this_platform",
27
- "aggregator_rebuild_failed",
28
- }
29
-
30
-
31
- def probe_display_capabilities(
32
- env: dict[str, str] | None = None,
33
- platform: str | None = None,
34
- tmux: Any | None = None,
35
- ) -> dict[str, Any]:
36
- env_map = dict({} if env is None else env)
37
- platform_name = _display_platform(platform, env_map)
38
- unsupported = platform_name.startswith("win") or platform_name in {"windows", "wsl"}
39
- tmux_info = _current_tmux_info(tmux, env_map) if tmux is not None else {}
40
- leader_session = tmux_info.get("leader_session") or env_map.get("TEAM_AGENT_LEADER_SESSION_NAME")
41
- leader_pane = tmux_info.get("leader_pane") or env_map.get("TMUX_PANE") or env_map.get("TEAM_AGENT_LEADER_PANE_ID")
42
- in_tmux = bool(env_map.get("TMUX") or env_map.get("TMUX_PANE") or tmux_info.get("ok")) and not unsupported
43
- caps = {
44
- "tmux_append_windows": bool(in_tmux and not unsupported),
45
- "adaptive_display": bool(in_tmux and not unsupported),
46
- }
47
- return {
48
- "in_tmux": in_tmux,
49
- "platform": platform_name,
50
- "leader_session": leader_session,
51
- "leader_pane": leader_pane,
52
- "caps": caps,
53
- "adaptive_status": "not_implemented_this_platform" if unsupported else ("available" if in_tmux else "leader_not_in_tmux"),
54
- "reason": "not_implemented_this_platform" if unsupported else (None if in_tmux else "leader_not_in_tmux"),
55
- }
56
-
57
-
58
- def open_adaptive_display(
59
- workspace: Path,
60
- session_name: str,
61
- jobs: list[tuple[str, dict[str, Any]]],
62
- event_log: EventLog,
63
- capability_probe: dict[str, Any] | None = None,
64
- ) -> dict[str, dict[str, Any]]:
65
- from team_agent.runtime import run_cmd
66
- _ = workspace
67
- probe = capability_probe or probe_display_capabilities(env=dict(os.environ), tmux=run_cmd)
68
- if probe.get("reason") == "not_implemented_this_platform":
69
- return adaptive_blocked(jobs, event_log, "not_implemented_this_platform", platform=probe.get("platform"))
70
- leader_session = str(probe.get("leader_session") or _state_leader_session(workspace) or "")
71
- if not probe.get("in_tmux") or not leader_session:
72
- return adaptive_blocked(jobs, event_log, "leader_not_in_tmux", platform=probe.get("platform"))
73
-
74
- linked_results = prepare_adaptive_linked_sessions(session_name, jobs)
75
- displays: dict[str, dict[str, Any]] = {}
76
- linked_jobs: list[tuple[str, dict[str, Any], str]] = []
77
- for agent_id, agent in jobs:
78
- linked = linked_results.get(agent_id, {})
79
- linked_session = linked.get("linked_session") or ghostty_display_session_name(session_name, agent_id)
80
- if linked.get("ok"):
81
- linked_jobs.append((agent_id, agent, linked_session))
82
- continue
83
- displays.update(
84
- adaptive_blocked(
85
- [(agent_id, agent)],
86
- event_log,
87
- "worker_session_missing",
88
- leader_session=leader_session,
89
- linked_sessions={agent_id: linked_session},
90
- error=linked.get("error") or linked.get("reason"),
91
- target=f"{session_name}:{agent_id}",
92
- )
93
- )
94
- if displays:
95
- kill_ghostty_workspace_linked_sessions([linked_session for _agent_id, _agent, linked_session in linked_jobs])
96
- return adaptive_blocked(
97
- jobs,
98
- event_log,
99
- "worker_session_missing",
100
- leader_session=leader_session,
101
- linked_sessions={agent_id: linked.get("linked_session") for agent_id, linked in linked_results.items()},
102
- error=next((display.get("error") for display in displays.values() if display.get("error")), None),
103
- )
104
- if not linked_jobs:
105
- return displays
106
-
107
- close_adaptive_windows(leader_session, session_name, event_log)
108
- prepared = prepare_adaptive_windows(leader_session, session_name, linked_jobs)
109
- if not prepared["ok"]:
110
- close_adaptive_windows(leader_session, session_name, event_log)
111
- kill_ghostty_workspace_linked_sessions([linked_session for _agent_id, _agent, linked_session in linked_jobs])
112
- displays.update(
113
- adaptive_blocked(
114
- [(agent_id, agent) for agent_id, agent, _linked_session in linked_jobs],
115
- event_log,
116
- prepared["reason"],
117
- leader_session=leader_session,
118
- linked_sessions={agent_id: linked_session for agent_id, _agent, linked_session in linked_jobs},
119
- error=prepared.get("error"),
120
- target=prepared.get("target"),
121
- )
122
- )
123
- return displays
124
-
125
- panes = {pane["agent_id"]: pane for pane in prepared["panes"]}
126
- for agent_id, agent, linked_session in linked_jobs:
127
- pane = panes.get(agent_id, {})
128
- display = {
129
- "backend": "adaptive",
130
- "status": "opened",
131
- "window": pane.get("window_name"),
132
- "workspace_window": pane.get("window_name"),
133
- "pane_id": pane.get("pane_id"),
134
- "pane_title": pane.get("title") or display_pane_title(agent),
135
- "target": f"{session_name}:{agent_id}",
136
- "target_worker_session": f"{session_name}:{agent_id}",
137
- "linked_session": linked_session,
138
- "leader_session": leader_session,
139
- "display_session": leader_session,
140
- "fallback": "tmux_headless",
141
- "note": "Adaptive display appends tagged tmux windows to the leader session; each pane attaches to a linked worker session.",
142
- }
143
- event_log.write("display.adaptive_opened", agent_id=agent_id, worker_id=agent_id, **display)
144
- displays[agent_id] = display
145
- return displays
146
-
147
-
148
- def prepare_adaptive_windows(
149
- leader_session: str,
150
- session_name: str,
151
- linked_jobs: list[tuple[str, dict[str, Any], str]],
152
- ) -> dict[str, Any]:
153
- prepared = prepare_tmux_attached_panes(
154
- leader_session,
155
- linked_jobs,
156
- window_name_for_index=lambda index: team_scoped_display_window_name(session_name, index),
157
- create_first_as_session=False,
158
- reason_map={
159
- "create_window": "window_create_failed",
160
- "title": "aggregator_rebuild_failed",
161
- "remain": "aggregator_rebuild_failed",
162
- "split": "split_failed",
163
- "layout": "aggregator_rebuild_failed",
164
- },
165
- stderr_reason_allowlist=ADAPTIVE_BLOCK_REASONS,
166
- )
167
- if prepared.get("ok"):
168
- prepared["leader_session"] = leader_session
169
- return prepared
170
-
171
-
172
- def prepare_adaptive_linked_sessions(
173
- session_name: str,
174
- jobs: list[tuple[str, dict[str, Any]]],
175
- ) -> dict[str, dict[str, Any]]:
176
- from team_agent.runtime import _tmux_session_exists, run_cmd
177
- results: dict[str, dict[str, Any]] = {}
178
- for agent_id, _agent in jobs:
179
- linked_session = ghostty_display_session_name(session_name, agent_id)
180
- if linked_session == session_name:
181
- results[agent_id] = {"ok": False, "reason": "worker_session_missing", "linked_session": linked_session}
182
- continue
183
- if _tmux_session_exists(linked_session):
184
- cleanup = run_cmd(["tmux", "kill-session", "-t", linked_session], timeout=10)
185
- if cleanup.returncode != 0:
186
- results[agent_id] = {
187
- "ok": False,
188
- "reason": "worker_session_missing",
189
- "error": cleanup.stderr.strip(),
190
- "linked_session": linked_session,
191
- }
192
- continue
193
- created = run_cmd(["tmux", "new-session", "-d", "-t", session_name, "-s", linked_session], timeout=10)
194
- if created.returncode != 0:
195
- results[agent_id] = {
196
- "ok": False,
197
- "reason": "worker_session_missing",
198
- "error": created.stderr.strip() or created.stdout.strip(),
199
- "linked_session": linked_session,
200
- }
201
- continue
202
- selected = run_cmd(["tmux", "select-window", "-t", f"{linked_session}:{agent_id}"], timeout=10)
203
- if selected.returncode != 0:
204
- run_cmd(["tmux", "kill-session", "-t", linked_session], timeout=10)
205
- results[agent_id] = {
206
- "ok": False,
207
- "reason": "worker_session_missing",
208
- "error": selected.stderr.strip() or selected.stdout.strip(),
209
- "linked_session": linked_session,
210
- }
211
- continue
212
- results[agent_id] = {"ok": True, "linked_session": linked_session}
213
- return results
214
-
215
-
216
- def adaptive_blocked(
217
- jobs: list[tuple[str, dict[str, Any]]],
218
- event_log: EventLog,
219
- reason: str,
220
- leader_session: str | None = None,
221
- linked_sessions: dict[str, str] | None = None,
222
- error: str | None = None,
223
- target: str | None = None,
224
- platform: str | None = None,
225
- ) -> dict[str, dict[str, Any]]:
226
- reason = reason if reason in ADAPTIVE_BLOCK_REASONS else "aggregator_rebuild_failed"
227
- displays: dict[str, dict[str, Any]] = {}
228
- for agent_id, _agent in jobs:
229
- display = {
230
- "backend": "adaptive",
231
- "status": "blocked",
232
- "reason": reason,
233
- "error": error,
234
- "target": target or f"{agent_id}",
235
- "target_worker_session": target or f"{agent_id}",
236
- "leader_session": leader_session,
237
- "linked_session": (linked_sessions or {}).get(agent_id),
238
- "display_session": leader_session,
239
- "fallback": "tmux_headless",
240
- "hint": "Start the leader inside tmux to enable adaptive team display." if reason == "leader_not_in_tmux" else None,
241
- "platform": platform,
242
- }
243
- event_log.write("display.adaptive_blocked", agent_id=agent_id, worker_id=agent_id, **display)
244
- displays[agent_id] = display
245
- return displays
246
-
247
-
248
- def close_adaptive_display(state: dict[str, Any], event_log: EventLog) -> dict[str, Any]:
249
- displays = [
250
- (agent_id, agent_state.get("display") or {})
251
- for agent_id, agent_state in state.get("agents", {}).items()
252
- if (agent_state.get("display") or {}).get("backend") == "adaptive"
253
- ]
254
- if not displays:
255
- return {"windows": [], "linked_sessions": [], "orphans_detected": {}}
256
- killed_windows: list[str] = []
257
- linked_sessions: list[str] = []
258
- session_name = str(state.get("session_name") or "")
259
- leader_session = _adaptive_leader_session(state, displays)
260
- needs_named_fallback = False
261
- for _agent_id, display in displays:
262
- linked = display.get("linked_session")
263
- if linked:
264
- linked_sessions.append(str(linked))
265
- if not linked or not display.get("leader_session") or not (display.get("workspace_window") or display.get("window")):
266
- needs_named_fallback = True
267
- seen_targets: set[str] = set()
268
- for _agent_id, display in displays:
269
- display_leader_session = str(display.get("leader_session") or "")
270
- window_name = str(display.get("workspace_window") or display.get("window") or "")
271
- if not display_leader_session or not window_name:
272
- continue
273
- target = f"{display_leader_session}:{window_name}"
274
- if target in seen_targets:
275
- continue
276
- seen_targets.add(target)
277
- if kill_adaptive_window(target):
278
- killed_windows.append(target)
279
- removed_orphans: dict[str, list[str]] = {}
280
- if needs_named_fallback and leader_session and session_name:
281
- named_windows = close_adaptive_windows(leader_session, session_name, event_log)
282
- killed_windows.extend(named_windows)
283
- linked_closed = kill_ghostty_workspace_linked_sessions(linked_sessions)
284
- named_closed, named_failed = _kill_adaptive_named_display_sessions(session_name, [agent_id for agent_id, _display in displays])
285
- linked_closed.extend(named_closed)
286
- removed_orphans = _adaptive_orphan_summary(named_closed, named_windows)
287
- else:
288
- named_failed = []
289
- linked_closed = kill_ghostty_workspace_linked_sessions(linked_sessions)
290
- orphans = _adaptive_orphans(session_name, leader_session, [agent_id for agent_id, _display in displays], named_failed) if needs_named_fallback else {}
291
- event_log.write("display.adaptive_closed", windows=killed_windows, linked_sessions=linked_closed, orphans_detected=orphans, orphans_removed=removed_orphans)
292
- return {"windows": killed_windows, "linked_sessions": linked_closed, "orphans_detected": orphans, "orphans_removed": removed_orphans}
293
-
294
-
295
- def close_adaptive_windows(leader_session: str, session_name: str, event_log: EventLog | None = None) -> list[str]:
296
- from team_agent.runtime import run_cmd
297
- prefix = f"team-agent:{session_name}:overview"
298
- proc = run_cmd(["tmux", "list-windows", "-t", leader_session, "-F", "#{window_name}"], timeout=10)
299
- if proc.returncode != 0:
300
- return []
301
- killed: list[str] = []
302
- for window_name in proc.stdout.splitlines():
303
- if window_name != prefix and not window_name.startswith(f"{prefix}-"):
304
- continue
305
- target = f"{leader_session}:{window_name}"
306
- if kill_adaptive_window(target):
307
- killed.append(target)
308
- if event_log is not None and killed:
309
- event_log.write("display.adaptive_stale_windows_closed", leader_session=leader_session, windows=killed)
310
- return killed
311
-
312
-
313
- def _adaptive_leader_session(state: dict[str, Any], displays: list[tuple[str, dict[str, Any]]]) -> str:
314
- for _agent_id, display in displays:
315
- if display.get("leader_session"):
316
- return str(display["leader_session"])
317
- receiver = state.get("leader_receiver") if isinstance(state.get("leader_receiver"), dict) else {}
318
- return str(receiver.get("session_name") or "")
319
-
320
-
321
- def _adaptive_named_display_sessions(session_name: str, agent_ids: list[str], fallback_exact: bool = True) -> list[str]:
322
- from team_agent.runtime import run_cmd
323
- if not session_name or not agent_ids:
324
- return []
325
- exact = [ghostty_display_session_name(session_name, agent_id) for agent_id in agent_ids]
326
- proc = run_cmd(["tmux", "list-sessions", "-F", "#{session_name}"], timeout=10)
327
- if proc.returncode != 0:
328
- return exact if fallback_exact else []
329
- prefixes = [ghostty_display_session_name(session_name, agent_id).rsplit("__", 1)[0] + "__" for agent_id in agent_ids]
330
- matched = [name for name in proc.stdout.splitlines() if any(name.startswith(prefix) for prefix in prefixes)]
331
- return matched or (exact if fallback_exact else [])
332
-
333
-
334
- def _kill_adaptive_named_display_sessions(session_name: str, agent_ids: list[str]) -> tuple[list[str], list[str]]:
335
- from team_agent.runtime import run_cmd
336
- killed: list[str] = []
337
- failed: list[str] = []
338
- for display_session in _adaptive_named_display_sessions(session_name, agent_ids):
339
- proc = run_cmd(["tmux", "kill-session", "-t", display_session], timeout=10)
340
- if proc.returncode == 0:
341
- killed.append(display_session)
342
- else:
343
- failed.append(display_session)
344
- return killed, failed
345
-
346
-
347
- def _adaptive_orphans(session_name: str, leader_session: str, agent_ids: list[str], failed_sessions: list[str]) -> dict[str, list[str]]:
348
- display_sessions = sorted(set([*_adaptive_named_display_sessions(session_name, agent_ids, fallback_exact=False), *failed_sessions]))
349
- windows: list[str] = []
350
- if leader_session and session_name:
351
- windows = _adaptive_window_orphans(leader_session, session_name)
352
- if not display_sessions and not windows:
353
- return {}
354
- return {
355
- "adaptive_display_sessions": sorted(set(display_sessions)),
356
- "adaptive_overview_windows": sorted(set(windows)),
357
- }
358
-
359
-
360
- def _adaptive_orphan_summary(display_sessions: list[str], windows: list[str]) -> dict[str, list[str]]:
361
- if not display_sessions and not windows:
362
- return {}
363
- return {
364
- "adaptive_display_sessions": sorted(set(display_sessions)),
365
- "adaptive_overview_windows": sorted(set(windows)),
366
- }
367
-
368
-
369
- def _adaptive_window_orphans(leader_session: str, session_name: str) -> list[str]:
370
- from team_agent.runtime import run_cmd
371
- prefix = f"team-agent:{session_name}:overview"
372
- proc = run_cmd(["tmux", "list-windows", "-t", leader_session, "-F", "#{window_name}"], timeout=10)
373
- if proc.returncode != 0:
374
- return []
375
- return [
376
- f"{leader_session}:{window_name}"
377
- for window_name in proc.stdout.splitlines()
378
- if window_name == prefix or window_name.startswith(f"{prefix}-")
379
- ]
380
-
381
-
382
- def kill_adaptive_window(target: str) -> bool:
383
- from team_agent.runtime import run_cmd
384
- proc = run_cmd(["tmux", "kill-window", "-t", target], timeout=10)
385
- return proc.returncode == 0
386
-
387
-
388
- def set_adaptive_pane_title(pane_id: str, title: str) -> dict[str, Any]:
389
- return set_tmux_display_pane_title(pane_id, title, "aggregator_rebuild_failed")
390
-
391
-
392
- def _display_platform(value: str | None, env: dict[str, str]) -> str:
393
- if value:
394
- return value.lower()
395
- if env.get("WSL_DISTRO_NAME") or env.get("WSL_INTEROP"):
396
- return "wsl"
397
- return platform_module.system().lower()
398
-
399
-
400
- def _current_tmux_info(tmux: Any, env: dict[str, str]) -> dict[str, Any]:
401
- pane = env.get("TMUX_PANE") or ""
402
- commands: list[list[str]] = []
403
- if pane:
404
- commands.insert(0, ["tmux", "display-message", "-p", "-t", pane, "-F", "#{session_name}\t#{pane_id}"])
405
- commands.insert(1, ["tmux", "display-message", "-p", "-t", pane, "-F", "#{session_name}"])
406
- commands.insert(2, ["tmux", "display-message", "-p", "-t", pane, "#{session_name}"])
407
- if env.get("TMUX"):
408
- commands.extend(
409
- [
410
- ["tmux", "display-message", "-p", "-F", "#{session_name}\t#{pane_id}"],
411
- ["tmux", "display-message", "-p", "-F", "#{session_name}"],
412
- ["tmux", "display-message", "-p", "#{session_name}\t#{pane_id}"],
413
- ["tmux", "display-message", "-p", "#{session_name}"],
414
- ]
415
- )
416
- for command in commands:
417
- proc = _call_tmux(tmux, command)
418
- parsed = _parse_tmux_session_pane(proc)
419
- if parsed:
420
- return parsed
421
- if pane:
422
- listed = _leader_from_tmux_panes(tmux, pane)
423
- if listed:
424
- return listed
425
- session = _first_tmux_session(tmux)
426
- if session:
427
- return {"ok": True, "leader_session": session, "leader_pane": pane}
428
- return {"ok": False}
429
-
430
-
431
- def _call_tmux(tmux: Any, args: list[str]) -> Any | None:
432
- try:
433
- if callable(tmux):
434
- try:
435
- return tmux(args, timeout=5)
436
- except TypeError:
437
- return tmux(args)
438
- if hasattr(tmux, "run_cmd"):
439
- return tmux.run_cmd(args)
440
- except Exception:
441
- return None
442
- return None
443
-
444
-
445
- def _parse_tmux_session_pane(proc: Any | None) -> dict[str, Any] | None:
446
- if not proc or getattr(proc, "returncode", 1) != 0:
447
- return None
448
- parts = str(getattr(proc, "stdout", "")).strip().split("\t")
449
- if len(parts) >= 2 and parts[0].startswith("%") and parts[1]:
450
- return {"ok": True, "leader_session": parts[1], "leader_pane": parts[0]}
451
- if len(parts) >= 2 and parts[0]:
452
- return {"ok": True, "leader_session": parts[0], "leader_pane": parts[1]}
453
- if len(parts) == 1 and parts[0] and not parts[0].startswith("%"):
454
- return {"ok": True, "leader_session": parts[0], "leader_pane": None}
455
- return None
456
-
457
-
458
- def _leader_from_tmux_panes(tmux: Any, pane: str) -> dict[str, Any] | None:
459
- proc = _call_tmux(
460
- tmux,
461
- [
462
- "tmux",
463
- "list-panes",
464
- "-a",
465
- "-F",
466
- "#{pane_id}\t#{session_name}\t#{pane_current_command}\t#{pane_active}",
467
- ],
468
- )
469
- if not proc or getattr(proc, "returncode", 1) != 0:
470
- return None
471
- rows = [line.split("\t") for line in str(getattr(proc, "stdout", "")).splitlines() if line.strip()]
472
- if pane:
473
- for row in rows:
474
- if len(row) >= 2 and row[0] == pane:
475
- return {"ok": True, "leader_session": row[1], "leader_pane": row[0]}
476
- for row in rows:
477
- if len(row) >= 3 and _leader_shaped_command(row[2]):
478
- return {"ok": True, "leader_session": row[1], "leader_pane": row[0]}
479
- if rows and len(rows[0]) >= 2:
480
- return {"ok": True, "leader_session": rows[0][1], "leader_pane": rows[0][0]}
481
- return None
482
-
483
-
484
- def _leader_shaped_command(command: str) -> bool:
485
- lowered = command.lower()
486
- return any(token in lowered for token in ("claude", "codex", "fake"))
487
-
488
-
489
- def _first_tmux_session(tmux: Any) -> str | None:
490
- for command in (
491
- ["tmux", "list-clients", "-F", "#{client_session}"],
492
- ["tmux", "list-sessions", "-F", "#{session_name}"],
493
- ):
494
- proc = _call_tmux(tmux, command)
495
- if not proc or getattr(proc, "returncode", 1) != 0:
496
- continue
497
- for line in str(getattr(proc, "stdout", "")).splitlines():
498
- if line.strip():
499
- return line.strip()
500
- return None
501
-
502
-
503
- def _state_leader_session(workspace: Path) -> str | None:
504
- try:
505
- from team_agent.state import load_runtime_state
506
- state = load_runtime_state(workspace)
507
- except Exception:
508
- return None
509
- receiver = state.get("leader_receiver") if isinstance(state.get("leader_receiver"), dict) else {}
510
- session_name = receiver.get("session_name")
511
- return str(session_name) if session_name else None
@@ -1,46 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
-
6
- ADAPTIVE_DISPLAY_BACKEND = "adaptive"
7
- GHOSTTY_DISPLAY_BACKENDS = {"ghostty", "ghostty_window", "ghostty_workspace"}
8
- DISPLAY_BACKENDS_WITH_WORKER_VIEWS = GHOSTTY_DISPLAY_BACKENDS | {ADAPTIVE_DISPLAY_BACKEND}
9
- VALID_DISPLAY_BACKENDS = {"none", "tmux_attach", "iterm"} | DISPLAY_BACKENDS_WITH_WORKER_VIEWS
10
-
11
-
12
- def resolve_display_backend(
13
- requested: str | None,
14
- *,
15
- recorded: str | None = None,
16
- event_log: Any | None = None,
17
- source: str,
18
- ) -> str:
19
- resolved = requested or recorded or ADAPTIVE_DISPLAY_BACKEND
20
- reason = "explicit" if requested else ("recorded" if recorded else "default")
21
- if event_log is not None and reason == "default":
22
- event_log.write(
23
- "display.backend_resolved",
24
- requested=None,
25
- resolved=resolved,
26
- reason=reason,
27
- source=source,
28
- )
29
- return resolved
30
-
31
-
32
- def resolve_restart_display_backend(spec: dict[str, Any], state: dict[str, Any], event_log: Any) -> str:
33
- return resolve_display_backend(
34
- spec.get("runtime", {}).get("display_backend"),
35
- recorded=state.get("display_backend"),
36
- event_log=event_log,
37
- source="restart",
38
- )
39
-
40
-
41
- def display_backend_has_worker_views(display_backend: str) -> bool:
42
- return display_backend in DISPLAY_BACKENDS_WITH_WORKER_VIEWS
43
-
44
-
45
- def display_backend_opens_before_leader_rebind(display_backend: str) -> bool:
46
- return display_backend_has_worker_views(display_backend) and display_backend != ADAPTIVE_DISPLAY_BACKEND