@team-agent/installer 0.2.10 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/Cargo.lock +744 -0
  2. package/Cargo.toml +34 -0
  3. package/crates/team-agent/Cargo.toml +33 -0
  4. package/crates/team-agent/src/cli/adapters.rs +1343 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +554 -0
  6. package/crates/team-agent/src/cli/emit.rs +1077 -0
  7. package/crates/team-agent/src/cli/helpers.rs +88 -0
  8. package/crates/team-agent/src/cli/leader.rs +216 -0
  9. package/crates/team-agent/src/cli/mod.rs +1141 -0
  10. package/crates/team-agent/src/cli/profile.rs +306 -0
  11. package/crates/team-agent/src/cli/send.rs +215 -0
  12. package/crates/team-agent/src/cli/status.rs +179 -0
  13. package/crates/team-agent/src/cli/status_port.rs +502 -0
  14. package/crates/team-agent/src/cli/tests/base.rs +616 -0
  15. package/crates/team-agent/src/cli/tests/compile.rs +96 -0
  16. package/crates/team-agent/src/cli/tests/divergence.rs +509 -0
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +333 -0
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +395 -0
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +675 -0
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +390 -0
  21. package/crates/team-agent/src/cli/tests/mod.rs +97 -0
  22. package/crates/team-agent/src/cli/tests/peer_allow.rs +137 -0
  23. package/crates/team-agent/src/cli/tests/repair_state_byte_lock.rs +302 -0
  24. package/crates/team-agent/src/cli/tests/run_delegation.rs +305 -0
  25. package/crates/team-agent/src/cli/tests/status_send.rs +385 -0
  26. package/crates/team-agent/src/cli/tests/verb_profile.rs +182 -0
  27. package/crates/team-agent/src/cli/tests/verb_settle.rs +236 -0
  28. package/crates/team-agent/src/cli/tests/verb_validate.rs +184 -0
  29. package/crates/team-agent/src/cli/types.rs +605 -0
  30. package/crates/team-agent/src/compiler/tests.rs +701 -0
  31. package/crates/team-agent/src/compiler.rs +489 -0
  32. package/crates/team-agent/src/coordinator/backoff.rs +153 -0
  33. package/crates/team-agent/src/coordinator/health.rs +436 -0
  34. package/crates/team-agent/src/coordinator/mod.rs +80 -0
  35. package/crates/team-agent/src/coordinator/orphan.rs +179 -0
  36. package/crates/team-agent/src/coordinator/tests/abnormal.rs +255 -0
  37. package/crates/team-agent/src/coordinator/tests/basics.rs +262 -0
  38. package/crates/team-agent/src/coordinator/tests/daemon.rs +323 -0
  39. package/crates/team-agent/src/coordinator/tests/health_sync.rs +263 -0
  40. package/crates/team-agent/src/coordinator/tests/main_preserved.rs +136 -0
  41. package/crates/team-agent/src/coordinator/tests/mod.rs +310 -0
  42. package/crates/team-agent/src/coordinator/tests/spine.rs +261 -0
  43. package/crates/team-agent/src/coordinator/tests/takeover.rs +227 -0
  44. package/crates/team-agent/src/coordinator/tests/tick_core.rs +256 -0
  45. package/crates/team-agent/src/coordinator/tests/watch.rs +167 -0
  46. package/crates/team-agent/src/coordinator/tick.rs +2032 -0
  47. package/crates/team-agent/src/coordinator/types.rs +584 -0
  48. package/crates/team-agent/src/db/migration.rs +716 -0
  49. package/crates/team-agent/src/db/mod.rs +23 -0
  50. package/crates/team-agent/src/db/schema.rs +378 -0
  51. package/crates/team-agent/src/event_log.rs +375 -0
  52. package/crates/team-agent/src/fake_worker.rs +253 -0
  53. package/crates/team-agent/src/leader/helpers.rs +190 -0
  54. package/crates/team-agent/src/leader/inject.rs +33 -0
  55. package/crates/team-agent/src/leader/lease.rs +1063 -0
  56. package/crates/team-agent/src/leader/mod.rs +99 -0
  57. package/crates/team-agent/src/leader/owner_bind.rs +292 -0
  58. package/crates/team-agent/src/leader/rediscover/tests.rs +525 -0
  59. package/crates/team-agent/src/leader/rediscover.rs +1099 -0
  60. package/crates/team-agent/src/leader/start.rs +273 -0
  61. package/crates/team-agent/src/leader/takeover.rs +235 -0
  62. package/crates/team-agent/src/leader/tests/basics.rs +183 -0
  63. package/crates/team-agent/src/leader/tests/byte_findings.rs +234 -0
  64. package/crates/team-agent/src/leader/tests/identity.rs +206 -0
  65. package/crates/team-agent/src/leader/tests/idle.rs +271 -0
  66. package/crates/team-agent/src/leader/tests/lease_api.rs +225 -0
  67. package/crates/team-agent/src/leader/tests/lease_claim.rs +253 -0
  68. package/crates/team-agent/src/leader/tests/mod.rs +125 -0
  69. package/crates/team-agent/src/leader/tests/rediscover.rs +351 -0
  70. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +204 -0
  71. package/crates/team-agent/src/leader/types.rs +487 -0
  72. package/crates/team-agent/src/lib.rs +85 -0
  73. package/crates/team-agent/src/lifecycle/display.rs +228 -0
  74. package/crates/team-agent/src/lifecycle/helpers.rs +112 -0
  75. package/crates/team-agent/src/lifecycle/launch/plan.rs +227 -0
  76. package/crates/team-agent/src/lifecycle/launch.rs +1833 -0
  77. package/crates/team-agent/src/lifecycle/mod.rs +62 -0
  78. package/crates/team-agent/src/lifecycle/restart/agent.rs +533 -0
  79. package/crates/team-agent/src/lifecycle/restart/common.rs +517 -0
  80. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +41 -0
  81. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +268 -0
  82. package/crates/team-agent/src/lifecycle/restart/remove.rs +780 -0
  83. package/crates/team-agent/src/lifecycle/restart/selection.rs +208 -0
  84. package/crates/team-agent/src/lifecycle/restart/team_state.rs +242 -0
  85. package/crates/team-agent/src/lifecycle/restart.rs +76 -0
  86. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +455 -0
  87. package/crates/team-agent/src/lifecycle/tests/core.rs +989 -0
  88. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +583 -0
  89. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +933 -0
  90. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +265 -0
  91. package/crates/team-agent/src/lifecycle/tests.rs +27 -0
  92. package/crates/team-agent/src/lifecycle/types.rs +685 -0
  93. package/crates/team-agent/src/main.rs +41 -0
  94. package/crates/team-agent/src/mcp_server/helpers.rs +228 -0
  95. package/crates/team-agent/src/mcp_server/mod.rs +183 -0
  96. package/crates/team-agent/src/mcp_server/normalize.rs +312 -0
  97. package/crates/team-agent/src/mcp_server/tests/golden.rs +283 -0
  98. package/crates/team-agent/src/mcp_server/tests/normalize.rs +244 -0
  99. package/crates/team-agent/src/mcp_server/tests/scoped.rs +189 -0
  100. package/crates/team-agent/src/mcp_server/tests/send.rs +222 -0
  101. package/crates/team-agent/src/mcp_server/tests/tools.rs +158 -0
  102. package/crates/team-agent/src/mcp_server/tests/wire.rs +159 -0
  103. package/crates/team-agent/src/mcp_server/tests.rs +38 -0
  104. package/crates/team-agent/src/mcp_server/tools.rs +603 -0
  105. package/crates/team-agent/src/mcp_server/types.rs +421 -0
  106. package/crates/team-agent/src/mcp_server/wire.rs +388 -0
  107. package/crates/team-agent/src/message_store.rs +767 -0
  108. package/crates/team-agent/src/messaging/activity.rs +433 -0
  109. package/crates/team-agent/src/messaging/delivery.rs +542 -0
  110. package/crates/team-agent/src/messaging/helpers.rs +209 -0
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +340 -0
  112. package/crates/team-agent/src/messaging/mod.rs +147 -0
  113. package/crates/team-agent/src/messaging/peers.rs +32 -0
  114. package/crates/team-agent/src/messaging/results.rs +537 -0
  115. package/crates/team-agent/src/messaging/scheduler.rs +344 -0
  116. package/crates/team-agent/src/messaging/selftest.rs +100 -0
  117. package/crates/team-agent/src/messaging/send.rs +582 -0
  118. package/crates/team-agent/src/messaging/tests/basic.rs +357 -0
  119. package/crates/team-agent/src/messaging/tests/main_preserved.rs +122 -0
  120. package/crates/team-agent/src/messaging/tests/mod.rs +293 -0
  121. package/crates/team-agent/src/messaging/tests/runtime.rs +1422 -0
  122. package/crates/team-agent/src/messaging/tests/spine.rs +437 -0
  123. package/crates/team-agent/src/messaging/trust.rs +192 -0
  124. package/crates/team-agent/src/messaging/types.rs +355 -0
  125. package/crates/team-agent/src/messaging/watchers.rs +591 -0
  126. package/crates/team-agent/src/model/enums.rs +311 -0
  127. package/crates/team-agent/src/model/errors.rs +17 -0
  128. package/crates/team-agent/src/model/ids.rs +155 -0
  129. package/crates/team-agent/src/model/mod.rs +22 -0
  130. package/crates/team-agent/src/model/paths.rs +228 -0
  131. package/crates/team-agent/src/model/permissions.rs +567 -0
  132. package/crates/team-agent/src/model/routing.rs +340 -0
  133. package/crates/team-agent/src/model/spec.rs +680 -0
  134. package/crates/team-agent/src/model/task_graph.rs +380 -0
  135. package/crates/team-agent/src/model/testdata/fuzz.golden.yaml +43 -0
  136. package/crates/team-agent/src/model/testdata/fuzz.yaml +43 -0
  137. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +207 -0
  138. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +206 -0
  139. package/crates/team-agent/src/model/testdata/team.spec.yaml +206 -0
  140. package/crates/team-agent/src/model/yaml/tests.rs +288 -0
  141. package/crates/team-agent/src/model/yaml.rs +800 -0
  142. package/crates/team-agent/src/packaging/install.rs +305 -0
  143. package/crates/team-agent/src/packaging/migrate.rs +30 -0
  144. package/crates/team-agent/src/packaging/mod.rs +82 -0
  145. package/crates/team-agent/src/packaging/repair.rs +24 -0
  146. package/crates/team-agent/src/packaging/tests.rs +829 -0
  147. package/crates/team-agent/src/packaging/types.rs +369 -0
  148. package/crates/team-agent/src/provider/adapter.rs +801 -0
  149. package/crates/team-agent/src/provider/approvals/mod.rs +2 -0
  150. package/crates/team-agent/src/provider/approvals/parsing.rs +452 -0
  151. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +163 -0
  152. package/crates/team-agent/src/provider/classify.rs +456 -0
  153. package/crates/team-agent/src/provider/faults.rs +136 -0
  154. package/crates/team-agent/src/provider/helpers.rs +41 -0
  155. package/crates/team-agent/src/provider/mod.rs +53 -0
  156. package/crates/team-agent/src/provider/startup_prompt.rs +423 -0
  157. package/crates/team-agent/src/provider/tests/adapter.rs +239 -0
  158. package/crates/team-agent/src/provider/tests/classify.rs +240 -0
  159. package/crates/team-agent/src/provider/tests/faults.rs +120 -0
  160. package/crates/team-agent/src/provider/tests/idle.rs +208 -0
  161. package/crates/team-agent/src/provider/tests/wire.rs +213 -0
  162. package/crates/team-agent/src/provider/tests.rs +31 -0
  163. package/crates/team-agent/src/provider/types.rs +424 -0
  164. package/crates/team-agent/src/state/identity.rs +656 -0
  165. package/crates/team-agent/src/state/mod.rs +58 -0
  166. package/crates/team-agent/src/state/owner_gate.rs +423 -0
  167. package/crates/team-agent/src/state/persist.rs +712 -0
  168. package/crates/team-agent/src/state/projection.rs +657 -0
  169. package/crates/team-agent/src/state/selector.rs +105 -0
  170. package/crates/team-agent/src/state/testdata/state-rich.canonical.json +133 -0
  171. package/crates/team-agent/src/tmux_backend/tests.rs +586 -0
  172. package/crates/team-agent/src/tmux_backend.rs +758 -0
  173. package/crates/team-agent/src/transport/test_support.rs +252 -0
  174. package/crates/team-agent/src/transport/tests/behavior.rs +327 -0
  175. package/crates/team-agent/src/transport/tests/mod.rs +199 -0
  176. package/crates/team-agent/src/transport/tests/wire.rs +527 -0
  177. package/crates/team-agent/src/transport.rs +774 -0
  178. package/npm/install.mjs +90 -106
  179. package/package.json +15 -13
  180. package/crates/team-agent-core/Cargo.toml +0 -12
  181. package/crates/team-agent-core/src/lib.rs +0 -332
  182. package/crates/team-agent-core/src/main.rs +0 -152
  183. package/pyproject.toml +0 -18
  184. package/scripts/install.py +0 -88
  185. package/scripts/run_regression_tests.py +0 -83
  186. package/src/team_agent/__init__.py +0 -3
  187. package/src/team_agent/__main__.py +0 -5
  188. package/src/team_agent/_legacy_pane_discovery.py +0 -186
  189. package/src/team_agent/abnormal_track.py +0 -253
  190. package/src/team_agent/approvals/__init__.py +0 -65
  191. package/src/team_agent/approvals/constants.py +0 -6
  192. package/src/team_agent/approvals/parsing.py +0 -176
  193. package/src/team_agent/approvals/runtime_prompts.py +0 -171
  194. package/src/team_agent/approvals/status.py +0 -176
  195. package/src/team_agent/cli/__init__.py +0 -137
  196. package/src/team_agent/cli/commands.py +0 -481
  197. package/src/team_agent/cli/e2e.py +0 -202
  198. package/src/team_agent/cli/helpers.py +0 -226
  199. package/src/team_agent/cli/parser.py +0 -540
  200. package/src/team_agent/compiler.py +0 -334
  201. package/src/team_agent/coordinator/__init__.py +0 -53
  202. package/src/team_agent/coordinator/__main__.py +0 -83
  203. package/src/team_agent/coordinator/lifecycle.py +0 -363
  204. package/src/team_agent/coordinator/metadata.py +0 -61
  205. package/src/team_agent/coordinator/paths.py +0 -17
  206. package/src/team_agent/diagnose/__init__.py +0 -48
  207. package/src/team_agent/diagnose/checks.py +0 -101
  208. package/src/team_agent/diagnose/comms.py +0 -213
  209. package/src/team_agent/diagnose/health.py +0 -241
  210. package/src/team_agent/diagnose/orphan_cleanup.py +0 -364
  211. package/src/team_agent/diagnose/preflight.py +0 -194
  212. package/src/team_agent/diagnose/quick_start.py +0 -324
  213. package/src/team_agent/display/__init__.py +0 -92
  214. package/src/team_agent/display/adaptive.py +0 -511
  215. package/src/team_agent/display/backend.py +0 -46
  216. package/src/team_agent/display/close.py +0 -154
  217. package/src/team_agent/display/ghostty.py +0 -77
  218. package/src/team_agent/display/rebuild.py +0 -102
  219. package/src/team_agent/display/tiling.py +0 -156
  220. package/src/team_agent/display/worker_window.py +0 -114
  221. package/src/team_agent/display/workspace.py +0 -382
  222. package/src/team_agent/errors.py +0 -10
  223. package/src/team_agent/events.py +0 -84
  224. package/src/team_agent/fake_worker.py +0 -80
  225. package/src/team_agent/idle_predicate.py +0 -200
  226. package/src/team_agent/idle_takeover.py +0 -59
  227. package/src/team_agent/idle_takeover_wiring.py +0 -111
  228. package/src/team_agent/launch/__init__.py +0 -41
  229. package/src/team_agent/launch/bootstrap.py +0 -85
  230. package/src/team_agent/launch/config.py +0 -106
  231. package/src/team_agent/launch/core.py +0 -301
  232. package/src/team_agent/launch/requirements.py +0 -57
  233. package/src/team_agent/leader/__init__.py +0 -926
  234. package/src/team_agent/leader_binding.py +0 -183
  235. package/src/team_agent/lifecycle/__init__.py +0 -5
  236. package/src/team_agent/lifecycle/agents.py +0 -278
  237. package/src/team_agent/lifecycle/operations.py +0 -411
  238. package/src/team_agent/lifecycle/paste_buffer_hygiene.py +0 -39
  239. package/src/team_agent/lifecycle/start.py +0 -363
  240. package/src/team_agent/mcp_server/__init__.py +0 -42
  241. package/src/team_agent/mcp_server/__main__.py +0 -7
  242. package/src/team_agent/mcp_server/contracts.py +0 -148
  243. package/src/team_agent/mcp_server/normalize.py +0 -257
  244. package/src/team_agent/mcp_server/server.py +0 -150
  245. package/src/team_agent/mcp_server/tools.py +0 -352
  246. package/src/team_agent/message_store/__init__.py +0 -23
  247. package/src/team_agent/message_store/agent_health.py +0 -113
  248. package/src/team_agent/message_store/core.py +0 -497
  249. package/src/team_agent/message_store/leader_notification_log.py +0 -198
  250. package/src/team_agent/message_store/result_watchers.py +0 -251
  251. package/src/team_agent/message_store/schema.py +0 -308
  252. package/src/team_agent/message_store/schema_migration.py +0 -448
  253. package/src/team_agent/messaging/__init__.py +0 -1
  254. package/src/team_agent/messaging/activity_detector.py +0 -254
  255. package/src/team_agent/messaging/delivery.py +0 -473
  256. package/src/team_agent/messaging/deps.py +0 -247
  257. package/src/team_agent/messaging/idle_alerts.py +0 -423
  258. package/src/team_agent/messaging/internal_delivery.py +0 -46
  259. package/src/team_agent/messaging/leader.py +0 -497
  260. package/src/team_agent/messaging/leader_api_errors.py +0 -216
  261. package/src/team_agent/messaging/leader_panes.py +0 -673
  262. package/src/team_agent/messaging/owner_bypass.py +0 -29
  263. package/src/team_agent/messaging/result_delivery.py +0 -539
  264. package/src/team_agent/messaging/results.py +0 -447
  265. package/src/team_agent/messaging/scheduler.py +0 -450
  266. package/src/team_agent/messaging/send.py +0 -532
  267. package/src/team_agent/messaging/session_drift.py +0 -94
  268. package/src/team_agent/messaging/tmux_io.py +0 -506
  269. package/src/team_agent/messaging/tmux_prompt.py +0 -338
  270. package/src/team_agent/messaging/trust_auto_answer.py +0 -52
  271. package/src/team_agent/orchestrator/__init__.py +0 -376
  272. package/src/team_agent/orchestrator/plan.py +0 -122
  273. package/src/team_agent/orchestrator/state.py +0 -128
  274. package/src/team_agent/paths.py +0 -45
  275. package/src/team_agent/permissions.py +0 -123
  276. package/src/team_agent/profiles/__init__.py +0 -82
  277. package/src/team_agent/profiles/constants.py +0 -19
  278. package/src/team_agent/profiles/core.py +0 -407
  279. package/src/team_agent/profiles/helpers.py +0 -69
  280. package/src/team_agent/profiles/provider_env.py +0 -188
  281. package/src/team_agent/profiles/smoke.py +0 -201
  282. package/src/team_agent/provider_cli/__init__.py +0 -43
  283. package/src/team_agent/provider_cli/adapter.py +0 -172
  284. package/src/team_agent/provider_cli/base.py +0 -48
  285. package/src/team_agent/provider_cli/claude.py +0 -457
  286. package/src/team_agent/provider_cli/codex.py +0 -336
  287. package/src/team_agent/provider_cli/copilot.py +0 -8
  288. package/src/team_agent/provider_cli/fake.py +0 -39
  289. package/src/team_agent/provider_cli/gemini.py +0 -95
  290. package/src/team_agent/provider_cli/opencode.py +0 -8
  291. package/src/team_agent/provider_cli/prompt.py +0 -62
  292. package/src/team_agent/provider_cli/registry.py +0 -18
  293. package/src/team_agent/provider_cli/unsupported.py +0 -32
  294. package/src/team_agent/provider_state/README.md +0 -78
  295. package/src/team_agent/provider_state/__init__.py +0 -86
  296. package/src/team_agent/provider_state/claude.py +0 -86
  297. package/src/team_agent/provider_state/codex.py +0 -84
  298. package/src/team_agent/provider_state/common.py +0 -207
  299. package/src/team_agent/provider_state/registry.py +0 -118
  300. package/src/team_agent/providers.py +0 -163
  301. package/src/team_agent/quality_gates.py +0 -104
  302. package/src/team_agent/restart/__init__.py +0 -34
  303. package/src/team_agent/restart/orchestration.py +0 -554
  304. package/src/team_agent/restart/selection.py +0 -89
  305. package/src/team_agent/restart/snapshot.py +0 -70
  306. package/src/team_agent/routing.py +0 -84
  307. package/src/team_agent/runtime.py +0 -1239
  308. package/src/team_agent/rust_core.py +0 -327
  309. package/src/team_agent/sessions/__init__.py +0 -25
  310. package/src/team_agent/sessions/capture.py +0 -143
  311. package/src/team_agent/sessions/inventory.py +0 -44
  312. package/src/team_agent/sessions/resume.py +0 -135
  313. package/src/team_agent/simple_yaml.py +0 -236
  314. package/src/team_agent/spec.py +0 -370
  315. package/src/team_agent/state.py +0 -602
  316. package/src/team_agent/status/__init__.py +0 -63
  317. package/src/team_agent/status/approvals.py +0 -52
  318. package/src/team_agent/status/compact.py +0 -158
  319. package/src/team_agent/status/constants.py +0 -18
  320. package/src/team_agent/status/inbox.py +0 -58
  321. package/src/team_agent/status/peek.py +0 -117
  322. package/src/team_agent/status/queries.py +0 -199
  323. package/src/team_agent/task_graph.py +0 -80
  324. package/src/team_agent/terminal.py +0 -57
  325. package/src/team_agent/wake.py +0 -58
  326. package/src/team_agent/watch/__init__.py +0 -145
@@ -1,154 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- from team_agent.events import EventLog
6
- from team_agent.display.adaptive import close_adaptive_display
7
- from team_agent.display.ghostty import ghostty_pids_by_title
8
- from team_agent.display.workspace import kill_ghostty_workspace_linked_sessions
9
-
10
-
11
- def close_team_display_backends(state: dict[str, Any], event_log: EventLog) -> dict[str, Any]:
12
- result = close_adaptive_display(state, event_log)
13
- close_ghostty_workspace(state, event_log)
14
- return result
15
-
16
-
17
- def close_ghostty_display(
18
- agent_id: str,
19
- agent_state: dict[str, Any],
20
- event_log: EventLog,
21
- ) -> None:
22
- from team_agent.runtime import _tmux_session_exists, run_cmd
23
- display = agent_state.get("display") or {}
24
- if display.get("backend") != "ghostty_window":
25
- return
26
- display_session = display.get("display_session")
27
- pids = [str(pid) for pid in display.get("pids", []) if str(pid).isdigit()]
28
- title = display.get("title")
29
- if not pids and title:
30
- pids = [str(pid) for pid in ghostty_pids_by_title(str(title))]
31
- killed: list[str] = []
32
- for pid in pids:
33
- proc = run_cmd(["kill", pid], timeout=5)
34
- if proc.returncode == 0:
35
- killed.append(pid)
36
- if killed:
37
- event_log.write("display.ghostty_closed", agent_id=agent_id, pids=killed, title=title)
38
- if display_session and _tmux_session_exists(str(display_session)):
39
- proc = run_cmd(["tmux", "kill-session", "-t", str(display_session)], timeout=10)
40
- if proc.returncode == 0:
41
- event_log.write("display.ghostty_display_session_closed", agent_id=agent_id, display_session=display_session)
42
- else:
43
- event_log.write(
44
- "display.ghostty_display_session_close_failed",
45
- agent_id=agent_id,
46
- display_session=display_session,
47
- error=proc.stderr.strip(),
48
- )
49
-
50
-
51
- def close_ghostty_workspace_slot(
52
- agent_id: str,
53
- display: dict[str, Any],
54
- event_log: EventLog,
55
- ) -> None:
56
- from team_agent.runtime import _tmux_session_exists, run_cmd
57
- pane_id = display.get("pane_id")
58
- linked_session = display.get("linked_session")
59
- stopped_title = f"stopped: {agent_id}"
60
- relabeled = False
61
- if pane_id:
62
- proc = run_cmd(["tmux", "select-pane", "-t", str(pane_id), "-T", stopped_title], timeout=10)
63
- if proc.returncode == 0:
64
- relabeled = True
65
- else:
66
- event_log.write(
67
- "display.ghostty_workspace_slot_relabel_failed",
68
- agent_id=agent_id,
69
- pane_id=pane_id,
70
- error=proc.stderr.strip(),
71
- )
72
- linked_session_closed = False
73
- if linked_session and _tmux_session_exists(str(linked_session)):
74
- proc = run_cmd(["tmux", "kill-session", "-t", str(linked_session)], timeout=10)
75
- if proc.returncode == 0:
76
- linked_session_closed = True
77
- else:
78
- event_log.write(
79
- "display.ghostty_workspace_slot_linked_session_close_failed",
80
- agent_id=agent_id,
81
- linked_session=linked_session,
82
- error=proc.stderr.strip(),
83
- )
84
- display["status"] = "stopped"
85
- display["pane_title"] = stopped_title
86
- event_log.write(
87
- "display.ghostty_workspace_slot_closed",
88
- agent_id=agent_id,
89
- pane_id=pane_id,
90
- linked_session=linked_session,
91
- relabeled=relabeled,
92
- linked_session_closed=linked_session_closed,
93
- )
94
-
95
-
96
- def close_ghostty_workspace(state: dict[str, Any], event_log: EventLog) -> None:
97
- from team_agent.runtime import _tmux_session_exists, run_cmd
98
- displays = [
99
- (agent_id, agent_state.get("display") or {})
100
- for agent_id, agent_state in state.get("agents", {}).items()
101
- if (agent_state.get("display") or {}).get("backend") == "ghostty_workspace"
102
- ]
103
- if not displays:
104
- return
105
- aggregator_session = next(
106
- (
107
- str(display.get("aggregator_session") or display.get("display_session"))
108
- for _agent_id, display in displays
109
- if display.get("aggregator_session") or display.get("display_session")
110
- ),
111
- None,
112
- )
113
- title = next((str(display.get("title")) for _agent_id, display in displays if display.get("title")), None)
114
- pids = {
115
- str(pid)
116
- for _agent_id, display in displays
117
- for pid in display.get("pids", [])
118
- if str(pid).isdigit()
119
- }
120
- if not pids and title:
121
- pids = {str(pid) for pid in ghostty_pids_by_title(str(title))}
122
-
123
- aggregator_closed = False
124
- if aggregator_session and _tmux_session_exists(aggregator_session):
125
- proc = run_cmd(["tmux", "kill-session", "-t", aggregator_session], timeout=10)
126
- if proc.returncode == 0:
127
- aggregator_closed = True
128
- else:
129
- event_log.write(
130
- "display.ghostty_workspace_close_failed",
131
- aggregator_session=aggregator_session,
132
- error=proc.stderr.strip(),
133
- )
134
-
135
- linked_sessions = [
136
- str(display.get("linked_session"))
137
- for _agent_id, display in displays
138
- if display.get("linked_session")
139
- ]
140
- linked_closed = kill_ghostty_workspace_linked_sessions(linked_sessions)
141
-
142
- killed: list[str] = []
143
- for pid in sorted(pids):
144
- proc = run_cmd(["kill", pid], timeout=5)
145
- if proc.returncode == 0:
146
- killed.append(pid)
147
- event_log.write(
148
- "display.ghostty_workspace_closed",
149
- pids=killed,
150
- title=title,
151
- aggregator_session=aggregator_session,
152
- linked_sessions=linked_closed,
153
- aggregator_closed=aggregator_closed,
154
- )
@@ -1,77 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import hashlib
4
- import re
5
- import time
6
- from pathlib import Path
7
- from typing import Any
8
-
9
-
10
- def ghostty_command() -> str | None:
11
- from team_agent.runtime import shutil_which
12
- return shutil_which("ghostty") or (
13
- "/Applications/Ghostty.app/Contents/MacOS/ghostty"
14
- if Path("/Applications/Ghostty.app/Contents/MacOS/ghostty").exists()
15
- else None
16
- )
17
-
18
-
19
- def ghostty_app_exists() -> bool:
20
- return Path("/Applications/Ghostty.app").exists()
21
-
22
-
23
- def ghostty_pids_by_title(title: str, wait_s: float = 0.0) -> list[int]:
24
- from team_agent.runtime import run_cmd
25
- deadline = time.monotonic() + max(wait_s, 0.0)
26
- while True:
27
- pgrep = run_cmd(["pgrep", "-f", f"--title={title}"], timeout=5)
28
- if pgrep.returncode == 0:
29
- pids = [int(pid) for pid in pgrep.stdout.split() if pid.isdigit()]
30
- if pids:
31
- return pids
32
- if time.monotonic() >= deadline:
33
- return []
34
- time.sleep(0.2)
35
-
36
-
37
- def ghostty_attach_args(display_session: str, title: str) -> list[str]:
38
- return [
39
- "open",
40
- "-na",
41
- "Ghostty.app",
42
- "--args",
43
- f"--title={title}",
44
- "-e",
45
- "tmux",
46
- "attach-session",
47
- "-t",
48
- display_session,
49
- ]
50
-
51
-
52
- def ghostty_display_session_name(session_name: str, window_name: str) -> str:
53
- raw = f"{session_name}:{window_name}"
54
- digest = hashlib.sha1(raw.encode("utf-8")).hexdigest()[:8]
55
- safe_session = re.sub(r"[^A-Za-z0-9_.-]", "_", session_name)[:80].strip("._-") or "team"
56
- safe_window = re.sub(r"[^A-Za-z0-9_.-]", "_", window_name)[:40].strip("._-") or "agent"
57
- return f"{safe_session}__display__{safe_window}__{digest}"
58
-
59
-
60
- def prepare_ghostty_display_session(session_name: str, window_name: str, display_session: str) -> dict[str, Any]:
61
- from team_agent.runtime import _tmux_session_exists, _tmux_window_exists, run_cmd
62
- if not _tmux_window_exists(session_name, window_name):
63
- return {"ok": False, "reason": "tmux_target_missing"}
64
- if display_session == session_name:
65
- return {"ok": False, "reason": "display_session_conflicts_with_base_session"}
66
- if _tmux_session_exists(display_session):
67
- proc = run_cmd(["tmux", "kill-session", "-t", display_session], timeout=10)
68
- if proc.returncode != 0:
69
- return {"ok": False, "reason": "display_session_cleanup_failed", "error": proc.stderr.strip()}
70
- proc = run_cmd(["tmux", "new-session", "-d", "-t", session_name, "-s", display_session], timeout=10)
71
- if proc.returncode != 0:
72
- return {"ok": False, "reason": "display_session_create_failed", "error": proc.stderr.strip()}
73
- proc = run_cmd(["tmux", "select-window", "-t", f"{display_session}:{window_name}"], timeout=10)
74
- if proc.returncode != 0:
75
- run_cmd(["tmux", "kill-session", "-t", display_session], timeout=10)
76
- return {"ok": False, "reason": "display_session_select_window_failed", "error": proc.stderr.strip()}
77
- return {"ok": True, "display_session": display_session}
@@ -1,102 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- from typing import Any
5
-
6
- from team_agent.events import EventLog
7
-
8
-
9
- def rebuild_restart_display_after_rebind(
10
- display_backend: str,
11
- workspace: Path,
12
- session_name: str,
13
- spec: dict[str, Any],
14
- event_log: EventLog,
15
- restarted: list[dict[str, Any]],
16
- receiver: dict[str, Any] | None = None,
17
- ) -> dict[str, Any]:
18
- if display_backend != "adaptive":
19
- return {}
20
- from team_agent.restart.snapshot import save_team_runtime_snapshot
21
- from team_agent.state import load_runtime_state, save_runtime_state, write_team_state
22
- state = load_runtime_state(workspace)
23
- state, display_results = rebuild_adaptive_display_after_rebind(
24
- workspace,
25
- session_name,
26
- spec,
27
- state,
28
- event_log,
29
- save_runtime_state,
30
- save_team_runtime_snapshot,
31
- write_team_state,
32
- receiver=receiver,
33
- )
34
- for item in restarted:
35
- display = display_results.get(item["agent_id"])
36
- if display:
37
- item["display_target"] = display
38
- return state
39
-
40
-
41
- def rebuild_adaptive_display_after_rebind(
42
- workspace: Path,
43
- session_name: str,
44
- spec: dict[str, Any],
45
- state: dict[str, Any],
46
- event_log: EventLog,
47
- save_state: Any,
48
- save_snapshot: Any,
49
- write_team_state: Any,
50
- receiver: dict[str, Any] | None = None,
51
- ) -> tuple[dict[str, Any], dict[str, dict[str, Any]]]:
52
- if receiver:
53
- state["leader_receiver"] = receiver
54
- receiver = receiver or (state.get("leader_receiver") if isinstance(state.get("leader_receiver"), dict) else {})
55
- rebind_session = latest_rebind_session(event_log)
56
- if rebind_session:
57
- receiver = {**receiver, "session_name": rebind_session}
58
- jobs = [
59
- (agent["id"], agent)
60
- for agent in spec.get("agents", [])
61
- if agent["id"] in state.get("agents", {}) and state["agents"][agent["id"]].get("status") == "running"
62
- ]
63
- from team_agent.runtime import _open_worker_displays
64
- display_results = _open_worker_displays(
65
- workspace,
66
- session_name,
67
- jobs,
68
- event_log,
69
- "adaptive",
70
- capability_probe={
71
- "in_tmux": bool(receiver.get("session_name")),
72
- "leader_session": receiver.get("session_name"),
73
- "leader_pane": receiver.get("pane_id"),
74
- "platform": None,
75
- "caps": {"adaptive_display": bool(receiver.get("session_name"))},
76
- "reason": None if receiver.get("session_name") else "leader_not_in_tmux",
77
- },
78
- )
79
- for agent_id, display in display_results.items():
80
- if agent_id in state.get("agents", {}):
81
- state["agents"][agent_id]["display"] = display
82
- event_log.write(
83
- "display.adaptive_rebuilt",
84
- session=session_name,
85
- workers=sorted(display_results),
86
- leader_session=next((display.get("leader_session") for display in display_results.values()), None),
87
- stale_windows_recreated=True,
88
- )
89
- save_state(workspace, state)
90
- save_snapshot(workspace, state)
91
- write_team_state(workspace, spec, state)
92
- return state, display_results
93
-
94
-
95
- def latest_rebind_session(event_log: EventLog) -> str | None:
96
- for event in reversed(event_log.tail(50)):
97
- if event.get("event") != "leader_receiver.rebind_applied":
98
- continue
99
- session = event.get("new_session_name") or event.get("session_name")
100
- if session:
101
- return str(session)
102
- return None
@@ -1,156 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import shlex
4
- from typing import Any, Callable
5
-
6
-
7
- DISPLAY_PANES_PER_WINDOW = 3
8
-
9
-
10
- def display_window_name(index: int) -> str:
11
- return "overview" if index == 0 else f"overview-{index + 1}"
12
-
13
-
14
- def grouped_display_jobs(
15
- jobs: list[tuple[str, dict[str, Any], str]],
16
- panes_per_window: int = DISPLAY_PANES_PER_WINDOW,
17
- ) -> list[tuple[int, str, list[tuple[str, dict[str, Any], str]]]]:
18
- groups: list[tuple[int, str, list[tuple[str, dict[str, Any], str]]]] = []
19
- for window_index, start in enumerate(range(0, len(jobs), panes_per_window)):
20
- groups.append((window_index, display_window_name(window_index), jobs[start : start + panes_per_window]))
21
- return groups
22
-
23
-
24
- def team_scoped_display_window_name(session_name: str, index: int) -> str:
25
- return f"team-agent:{session_name}:{display_window_name(index)}"
26
-
27
-
28
- def tmux_stdout_last_line(stdout: str) -> str | None:
29
- lines = [line.strip() for line in stdout.splitlines() if line.strip()]
30
- return lines[-1] if lines else None
31
-
32
-
33
- def tmux_attach_pane_command(linked_session: str) -> str:
34
- return f"TMUX= tmux attach-session -t {shlex.quote(linked_session)}"
35
-
36
-
37
- def display_pane_title(agent: dict[str, Any]) -> str:
38
- return f"team-agent:{agent['id']}:{agent.get('role', '')}"
39
-
40
-
41
- def set_tmux_display_pane_title(pane_id: str, title: str, reason: str) -> dict[str, Any]:
42
- from team_agent.runtime import run_cmd
43
- proc = run_cmd(["tmux", "select-pane", "-t", pane_id, "-T", title], timeout=10)
44
- if proc.returncode != 0:
45
- return {"ok": False, "reason": reason, "error": proc.stderr.strip()}
46
- return {"ok": True}
47
-
48
-
49
- def prepare_tmux_attached_panes(
50
- host_session: str,
51
- linked_jobs: list[tuple[str, dict[str, Any], str]],
52
- *,
53
- window_name_for_index: Callable[[int], str],
54
- create_first_as_session: bool,
55
- panes_per_window: int = DISPLAY_PANES_PER_WINDOW,
56
- reason_map: dict[str, str] | None = None,
57
- stderr_reason_allowlist: set[str] | None = None,
58
- cleanup_session: str | None = None,
59
- enable_mouse: bool = False,
60
- select_first_window: bool = False,
61
- ) -> dict[str, Any]:
62
- from team_agent.runtime import run_cmd
63
-
64
- reasons = reason_map or {}
65
-
66
- def reason(key: str) -> str:
67
- return reasons.get(key, key)
68
-
69
- def fail(key: str, proc: Any | None = None, target: str | None = None) -> dict[str, Any]:
70
- if cleanup_session:
71
- run_cmd(["tmux", "kill-session", "-t", cleanup_session], timeout=10)
72
- result = {"ok": False, "reason": reason(key)}
73
- if proc is not None:
74
- detail = (proc.stderr or proc.stdout or "").strip()
75
- if stderr_reason_allowlist and detail in stderr_reason_allowlist:
76
- result["reason"] = detail
77
- result["error"] = detail
78
- if target:
79
- result["target"] = target
80
- return result
81
-
82
- panes: list[dict[str, Any]] = []
83
- for window_index, _base_window_name, window_jobs in grouped_display_jobs(linked_jobs, panes_per_window):
84
- window_name = window_name_for_index(window_index)
85
- first_agent_id, first_agent, first_linked_session = window_jobs[0]
86
- if create_first_as_session and window_index == 0:
87
- command = [
88
- "tmux", "new-session", "-d", "-P", "-F", "#{pane_id}",
89
- "-s", host_session, "-n", window_name, tmux_attach_pane_command(first_linked_session),
90
- ]
91
- fail_key = "create_session"
92
- else:
93
- command = [
94
- "tmux", "new-window", "-t", host_session, "-n", window_name,
95
- "-P", "-F", "#{pane_id}", tmux_attach_pane_command(first_linked_session),
96
- ]
97
- fail_key = "create_window"
98
- proc = run_cmd(command, timeout=10)
99
- if proc.returncode != 0:
100
- return fail(fail_key, proc, f"{host_session}:{window_name}")
101
-
102
- first_pane_id = tmux_stdout_last_line(proc.stdout) or f"{host_session}:{window_name}.0"
103
- title = display_pane_title(first_agent)
104
- title_result = set_tmux_display_pane_title(first_pane_id, title, reason("title"))
105
- if not title_result["ok"]:
106
- return fail(title_result["reason"], target=first_pane_id)
107
- panes.append(
108
- {
109
- "agent_id": first_agent_id,
110
- "pane_id": first_pane_id,
111
- "title": title,
112
- "linked_session": first_linked_session,
113
- "window_name": window_name,
114
- }
115
- )
116
-
117
- proc = run_cmd(["tmux", "set-window-option", "-t", f"{host_session}:{window_name}", "remain-on-exit", "on"], timeout=10)
118
- if proc.returncode != 0:
119
- return fail("remain", proc, f"{host_session}:{window_name}")
120
-
121
- for index, (agent_id, agent, linked_session) in enumerate(window_jobs[1:], start=1):
122
- proc = run_cmd(
123
- [
124
- "tmux", "split-window", "-t", f"{host_session}:{window_name}",
125
- "-h", "-P", "-F", "#{pane_id}", tmux_attach_pane_command(linked_session),
126
- ],
127
- timeout=10,
128
- )
129
- if proc.returncode != 0:
130
- return fail("split", proc, f"{host_session}:{window_name}")
131
- pane_id = tmux_stdout_last_line(proc.stdout) or f"{host_session}:{window_name}.{index}"
132
- title = display_pane_title(agent)
133
- title_result = set_tmux_display_pane_title(pane_id, title, reason("title"))
134
- if not title_result["ok"]:
135
- return fail(title_result["reason"], target=pane_id)
136
- panes.append(
137
- {
138
- "agent_id": agent_id,
139
- "pane_id": pane_id,
140
- "title": title,
141
- "linked_session": linked_session,
142
- "window_name": window_name,
143
- }
144
- )
145
-
146
- proc = run_cmd(["tmux", "select-layout", "-t", f"{host_session}:{window_name}", "even-horizontal"], timeout=10)
147
- if proc.returncode != 0:
148
- return fail("layout", proc, f"{host_session}:{window_name}")
149
-
150
- if enable_mouse:
151
- proc = run_cmd(["tmux", "set-option", "-t", host_session, "mouse", "on"], timeout=10)
152
- if proc.returncode != 0:
153
- return fail("mouse", proc)
154
- if select_first_window:
155
- run_cmd(["tmux", "select-window", "-t", f"{host_session}:{window_name_for_index(0)}"], timeout=10)
156
- return {"ok": True, "host_session": host_session, "panes": panes}
@@ -1,114 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from concurrent.futures import ThreadPoolExecutor, as_completed
4
- from pathlib import Path
5
- from typing import Any
6
-
7
- from team_agent.events import EventLog
8
- from team_agent.display.adaptive import open_adaptive_display
9
- from team_agent.display.ghostty import (
10
- ghostty_app_exists,
11
- ghostty_attach_args,
12
- ghostty_display_session_name,
13
- ghostty_pids_by_title,
14
- prepare_ghostty_display_session,
15
- )
16
- from team_agent.display.workspace import open_ghostty_workspace
17
-
18
-
19
- def open_worker_displays(
20
- workspace: Path,
21
- session_name: str,
22
- jobs: list[tuple[str, dict[str, Any]]],
23
- event_log: EventLog,
24
- display_backend: str = "adaptive",
25
- capability_probe: dict[str, Any] | None = None,
26
- ) -> dict[str, dict[str, Any]]:
27
- if not jobs:
28
- return {}
29
- if display_backend == "adaptive":
30
- return open_adaptive_display(workspace, session_name, jobs, event_log, capability_probe=capability_probe)
31
- if display_backend == "ghostty_workspace":
32
- return open_ghostty_workspace(workspace, session_name, jobs, event_log)
33
- if len(jobs) == 1:
34
- agent_id, agent = jobs[0]
35
- return {agent_id: open_ghostty_worker_window(workspace, session_name, agent_id, agent, event_log)}
36
- results: dict[str, dict[str, Any]] = {}
37
- max_workers = min(4, len(jobs))
38
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
39
- futures = {
40
- executor.submit(open_ghostty_worker_window, workspace, session_name, agent_id, agent, event_log): agent_id
41
- for agent_id, agent in jobs
42
- }
43
- for future in as_completed(futures):
44
- agent_id = futures[future]
45
- try:
46
- results[agent_id] = future.result()
47
- except Exception as exc:
48
- display = {
49
- "backend": "ghostty_window",
50
- "status": "blocked",
51
- "reason": "display_open_exception",
52
- "error": str(exc),
53
- "fallback": "tmux_headless",
54
- }
55
- event_log.write("display.ghostty_blocked", agent_id=agent_id, **display)
56
- results[agent_id] = display
57
- return results
58
-
59
-
60
- def open_ghostty_worker_window(
61
- workspace: Path,
62
- session_name: str,
63
- window_name: str,
64
- agent: dict[str, Any],
65
- event_log: EventLog,
66
- ) -> dict[str, Any]:
67
- from team_agent.runtime import run_cmd
68
- _ = workspace
69
- if not ghostty_app_exists():
70
- blocker = {
71
- "backend": "ghostty_window",
72
- "status": "blocked",
73
- "reason": "ghostty_app_missing",
74
- "fallback": "tmux_headless",
75
- }
76
- event_log.write("display.ghostty_blocked", agent_id=agent["id"], **blocker)
77
- return blocker
78
- title = f"team-agent:{agent['id']}:{agent.get('role', '')}"
79
- display_session = ghostty_display_session_name(session_name, window_name)
80
- prepared = prepare_ghostty_display_session(session_name, window_name, display_session)
81
- if not prepared["ok"]:
82
- blocker = {
83
- "backend": "ghostty_window",
84
- "status": "blocked",
85
- "reason": prepared["reason"],
86
- "error": prepared.get("error"),
87
- "target": f"{session_name}:{window_name}",
88
- "display_session": display_session,
89
- "fallback": "tmux_headless",
90
- }
91
- event_log.write("display.ghostty_blocked", agent_id=agent["id"], **blocker)
92
- return blocker
93
- launch_args = ghostty_attach_args(display_session, title)
94
- proc = run_cmd(launch_args, timeout=10)
95
- display = {
96
- "backend": "ghostty_window",
97
- "status": "opened" if proc.returncode == 0 else "blocked",
98
- "title": title,
99
- "target": f"{session_name}:{window_name}",
100
- "display_session": display_session,
101
- "launch_args": launch_args,
102
- "pid": None,
103
- "pids": [],
104
- "tty": None,
105
- "fallback": "tmux_headless",
106
- "note": "Ghostty opens a dedicated linked tmux session per worker so each display has an independent active window; runtime injection remains tmux-backed.",
107
- }
108
- if proc.returncode != 0:
109
- display["reason"] = proc.stderr.strip() or proc.stdout.strip() or "open Ghostty.app failed"
110
- else:
111
- display["pids"] = ghostty_pids_by_title(title, wait_s=3.0)
112
- display["pid"] = display["pids"][0] if display["pids"] else None
113
- event_log.write("display.ghostty_window", agent_id=agent["id"], **display)
114
- return display