@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,382 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import hashlib
4
- import re
5
- from concurrent.futures import ThreadPoolExecutor, as_completed
6
- from typing import Any
7
-
8
- from team_agent.events import EventLog
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.tiling import (
17
- DISPLAY_PANES_PER_WINDOW,
18
- display_pane_title,
19
- display_window_name,
20
- prepare_tmux_attached_panes,
21
- set_tmux_display_pane_title,
22
- tmux_attach_pane_command,
23
- tmux_stdout_last_line as _tmux_stdout_last_line,
24
- )
25
-
26
-
27
- GHOSTTY_WORKSPACE_PANES_PER_WINDOW = DISPLAY_PANES_PER_WINDOW
28
-
29
-
30
- def open_ghostty_workspace(
31
- workspace,
32
- session_name: str,
33
- jobs: list[tuple[str, dict[str, Any]]],
34
- event_log: EventLog,
35
- ) -> dict[str, dict[str, Any]]:
36
- from team_agent.runtime import run_cmd
37
- _ = workspace
38
- if not ghostty_app_exists():
39
- return ghostty_workspace_blocked(jobs, event_log, "ghostty_app_missing")
40
- aggregator_session = ghostty_workspace_aggregator_name(session_name)
41
- linked_results = prepare_ghostty_workspace_linked_sessions(session_name, jobs)
42
- displays: dict[str, dict[str, Any]] = {}
43
- linked_jobs: list[tuple[str, dict[str, Any], str]] = []
44
- for agent_id, agent in jobs:
45
- linked = linked_results.get(agent_id, {})
46
- linked_session = linked.get("linked_session") or ghostty_display_session_name(session_name, agent_id)
47
- if linked.get("ok"):
48
- linked_jobs.append((agent_id, agent, linked_session))
49
- continue
50
- displays.update(
51
- ghostty_workspace_blocked(
52
- [(agent_id, agent)],
53
- event_log,
54
- linked.get("reason", "display_session_create_failed"),
55
- aggregator_session=aggregator_session,
56
- linked_sessions={agent_id: linked_session},
57
- error=linked.get("error"),
58
- target=f"{session_name}:{agent_id}",
59
- )
60
- )
61
- if not linked_jobs:
62
- return displays
63
- prepared = prepare_ghostty_workspace_aggregator(aggregator_session, linked_jobs)
64
- if not prepared["ok"]:
65
- kill_ghostty_workspace_linked_sessions([linked_session for _agent_id, _agent, linked_session in linked_jobs])
66
- displays.update(
67
- ghostty_workspace_blocked(
68
- [(agent_id, agent) for agent_id, agent, _linked_session in linked_jobs],
69
- event_log,
70
- prepared["reason"],
71
- aggregator_session=aggregator_session,
72
- linked_sessions={agent_id: linked_session for agent_id, _agent, linked_session in linked_jobs},
73
- error=prepared.get("error"),
74
- target=prepared.get("target"),
75
- )
76
- )
77
- return displays
78
- title = f"team-agent:{session_name}:workspace"
79
- launch_args = ghostty_attach_args(aggregator_session, title)
80
- proc = run_cmd(launch_args, timeout=10)
81
- if proc.returncode != 0:
82
- run_cmd(["tmux", "kill-session", "-t", aggregator_session], timeout=10)
83
- kill_ghostty_workspace_linked_sessions([linked_session for _agent_id, _agent, linked_session in linked_jobs])
84
- displays.update(
85
- ghostty_workspace_blocked(
86
- [(agent_id, agent) for agent_id, agent, _linked_session in linked_jobs],
87
- event_log,
88
- "open Ghostty.app failed",
89
- aggregator_session=aggregator_session,
90
- linked_sessions={agent_id: linked_session for agent_id, _agent, linked_session in linked_jobs},
91
- error=proc.stderr.strip() or proc.stdout.strip(),
92
- )
93
- )
94
- return displays
95
- pids = ghostty_pids_by_title(title, wait_s=3.0)
96
- panes = {pane["agent_id"]: pane for pane in prepared["panes"]}
97
- for agent_id, agent, linked_session in linked_jobs:
98
- pane = panes.get(agent_id, {})
99
- display = {
100
- "backend": "ghostty_workspace",
101
- "status": "opened",
102
- "title": title,
103
- "pane_title": pane.get("title") or ghostty_workspace_pane_title(agent),
104
- "target": f"{session_name}:{agent_id}",
105
- "linked_session": linked_session,
106
- "aggregator_session": aggregator_session,
107
- "display_session": aggregator_session,
108
- "workspace_window": pane.get("window_name"),
109
- "pane_id": pane.get("pane_id"),
110
- "launch_args": launch_args,
111
- "pid": pids[0] if pids else None,
112
- "pids": pids,
113
- "tty": None,
114
- "fallback": "tmux_headless",
115
- "note": "Ghostty opens one aggregator tmux session; each pane attaches to a distinct linked session pinned to one base worker window, so runtime injection remains session:agent_id addressed.",
116
- }
117
- event_log.write("display.ghostty_workspace", agent_id=agent_id, **display)
118
- displays[agent_id] = display
119
- return displays
120
-
121
-
122
- def ghostty_workspace_blocked(
123
- jobs: list[tuple[str, dict[str, Any]]],
124
- event_log: EventLog,
125
- reason: str,
126
- aggregator_session: str | None = None,
127
- linked_sessions: dict[str, str] | None = None,
128
- error: str | None = None,
129
- target: str | None = None,
130
- ) -> dict[str, dict[str, Any]]:
131
- displays: dict[str, dict[str, Any]] = {}
132
- for agent_id, _agent in jobs:
133
- linked_session = (linked_sessions or {}).get(agent_id)
134
- display = {
135
- "backend": "ghostty_workspace",
136
- "status": "blocked",
137
- "reason": reason,
138
- "error": error,
139
- "target": target or f"{agent_id}",
140
- "linked_session": linked_session,
141
- "aggregator_session": aggregator_session,
142
- "display_session": aggregator_session,
143
- "fallback": "tmux_headless",
144
- }
145
- event_log.write("display.ghostty_workspace_blocked", agent_id=agent_id, **display)
146
- displays[agent_id] = display
147
- return displays
148
-
149
-
150
- def ghostty_workspace_aggregator_name(session_name: str) -> str:
151
- raw = f"{session_name}:workspace"
152
- digest = hashlib.sha1(raw.encode("utf-8")).hexdigest()[:8]
153
- safe_session = re.sub(r"[^A-Za-z0-9_.-]", "_", session_name)[:80].strip("._-") or "team"
154
- return f"{safe_session}__display__workspace__{digest}"
155
-
156
-
157
- def ghostty_workspace_window_name(index: int) -> str:
158
- return display_window_name(index)
159
-
160
-
161
- def ghostty_workspace_pane_command(linked_session: str) -> str:
162
- return tmux_attach_pane_command(linked_session)
163
-
164
-
165
- def ghostty_workspace_pane_title(agent: dict[str, Any]) -> str:
166
- return display_pane_title(agent)
167
-
168
-
169
- def prepare_ghostty_workspace_linked_sessions(
170
- session_name: str,
171
- jobs: list[tuple[str, dict[str, Any]]],
172
- ) -> dict[str, dict[str, Any]]:
173
- def prepare(agent_id: str) -> dict[str, Any]:
174
- linked_session = ghostty_display_session_name(session_name, agent_id)
175
- result = prepare_ghostty_display_session(session_name, agent_id, linked_session)
176
- result["linked_session"] = linked_session
177
- return result
178
-
179
- if len(jobs) == 1:
180
- agent_id, _agent = jobs[0]
181
- return {agent_id: prepare(agent_id)}
182
- results: dict[str, dict[str, Any]] = {}
183
- max_workers = min(4, len(jobs))
184
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
185
- futures = {executor.submit(prepare, agent_id): agent_id for agent_id, _agent in jobs}
186
- for future in as_completed(futures):
187
- agent_id = futures[future]
188
- try:
189
- results[agent_id] = future.result()
190
- except Exception as exc:
191
- results[agent_id] = {
192
- "ok": False,
193
- "reason": "display_session_create_exception",
194
- "error": str(exc),
195
- "linked_session": ghostty_display_session_name(session_name, agent_id),
196
- }
197
- return results
198
-
199
-
200
- def prepare_ghostty_workspace_aggregator(
201
- aggregator_session: str,
202
- linked_jobs: list[tuple[str, dict[str, Any], str]],
203
- ) -> dict[str, Any]:
204
- from team_agent.runtime import _tmux_session_exists, run_cmd
205
- if _tmux_session_exists(aggregator_session):
206
- proc = run_cmd(["tmux", "kill-session", "-t", aggregator_session], timeout=10)
207
- if proc.returncode != 0:
208
- return {"ok": False, "reason": "display_session_cleanup_failed", "error": proc.stderr.strip()}
209
- prepared = prepare_tmux_attached_panes(
210
- aggregator_session,
211
- linked_jobs,
212
- window_name_for_index=ghostty_workspace_window_name,
213
- create_first_as_session=True,
214
- panes_per_window=GHOSTTY_WORKSPACE_PANES_PER_WINDOW,
215
- cleanup_session=aggregator_session,
216
- enable_mouse=True,
217
- select_first_window=True,
218
- reason_map={
219
- "create_session": "display_session_create_failed",
220
- "create_window": "display_session_window_create_failed",
221
- "title": "display_session_pane_title_failed",
222
- "remain": "display_session_remain_on_exit_failed",
223
- "split": "display_session_split_failed",
224
- "layout": "display_session_layout_failed",
225
- "mouse": "display_session_mouse_failed",
226
- },
227
- )
228
- if prepared.get("ok"):
229
- prepared["aggregator_session"] = aggregator_session
230
- return prepared
231
-
232
-
233
- def set_ghostty_workspace_pane_title(pane_id: str, title: str) -> dict[str, Any]:
234
- return set_tmux_display_pane_title(pane_id, title, "display_session_pane_title_failed")
235
-
236
-
237
- def open_ghostty_workspace_agent_display(
238
- session_name: str,
239
- agent_id: str,
240
- agent: dict[str, Any],
241
- previous_display: dict[str, Any],
242
- event_log: EventLog,
243
- ) -> dict[str, Any]:
244
- from team_agent.runtime import _tmux_session_exists, run_cmd
245
- if not ghostty_app_exists():
246
- return ghostty_workspace_blocked(
247
- [(agent_id, agent)],
248
- event_log,
249
- "ghostty_app_missing",
250
- aggregator_session=ghostty_workspace_aggregator_name(session_name),
251
- linked_sessions={agent_id: ghostty_display_session_name(session_name, agent_id)},
252
- target=f"{session_name}:{agent_id}",
253
- )[agent_id]
254
- aggregator_session = str(
255
- previous_display.get("aggregator_session")
256
- or previous_display.get("display_session")
257
- or ghostty_workspace_aggregator_name(session_name)
258
- )
259
- linked_session = ghostty_display_session_name(session_name, agent_id)
260
- prepared = prepare_ghostty_display_session(session_name, agent_id, linked_session)
261
- if not prepared["ok"]:
262
- return ghostty_workspace_blocked(
263
- [(agent_id, agent)],
264
- event_log,
265
- prepared["reason"],
266
- aggregator_session=aggregator_session,
267
- linked_sessions={agent_id: linked_session},
268
- error=prepared.get("error"),
269
- target=f"{session_name}:{agent_id}",
270
- )[agent_id]
271
- if not _tmux_session_exists(aggregator_session):
272
- return ghostty_workspace_partial_update_display(
273
- session_name,
274
- agent_id,
275
- agent,
276
- event_log,
277
- reason="aggregator_session_missing",
278
- note="pane refresh requires full team restart",
279
- )
280
-
281
- pane_title = ghostty_workspace_pane_title(agent)
282
- command = ghostty_workspace_pane_command(linked_session)
283
- pane_id = str(previous_display.get("pane_id") or "")
284
- workspace_window = str(previous_display.get("workspace_window") or ghostty_workspace_window_name(0))
285
- refreshed = False
286
- if pane_id:
287
- proc = run_cmd(["tmux", "respawn-pane", "-k", "-t", pane_id, command], timeout=10)
288
- refreshed = proc.returncode == 0
289
- if not refreshed:
290
- proc = run_cmd(
291
- [
292
- "tmux",
293
- "split-window",
294
- "-t",
295
- f"{aggregator_session}:{workspace_window}",
296
- "-h",
297
- "-P",
298
- "-F",
299
- "#{pane_id}",
300
- command,
301
- ],
302
- timeout=10,
303
- )
304
- if proc.returncode != 0:
305
- return ghostty_workspace_partial_update_display(
306
- session_name,
307
- agent_id,
308
- agent,
309
- event_log,
310
- reason="aggregator_pane_refresh_failed",
311
- note=proc.stderr.strip() or "pane refresh requires full team restart",
312
- )
313
- pane_id = _tmux_stdout_last_line(proc.stdout) or pane_id
314
- title_result = set_ghostty_workspace_pane_title(pane_id, pane_title)
315
- if not title_result["ok"]:
316
- return ghostty_workspace_partial_update_display(
317
- session_name,
318
- agent_id,
319
- agent,
320
- event_log,
321
- reason=title_result["reason"],
322
- note=title_result.get("error") or "pane refresh requires full team restart",
323
- )
324
- run_cmd(["tmux", "select-layout", "-t", f"{aggregator_session}:{workspace_window}", "even-horizontal"], timeout=10)
325
- title = str(previous_display.get("title") or f"team-agent:{session_name}:workspace")
326
- pids = [int(pid) for pid in previous_display.get("pids", []) if str(pid).isdigit()]
327
- display = {
328
- "backend": "ghostty_workspace",
329
- "status": "opened",
330
- "title": title,
331
- "pane_title": pane_title,
332
- "target": f"{session_name}:{agent_id}",
333
- "linked_session": linked_session,
334
- "aggregator_session": aggregator_session,
335
- "display_session": aggregator_session,
336
- "workspace_window": workspace_window,
337
- "pane_id": pane_id,
338
- "pid": pids[0] if pids else None,
339
- "pids": pids,
340
- "tty": None,
341
- "fallback": "tmux_headless",
342
- "note": "Refreshed this worker's Ghostty workspace pane by respawning it against a distinct linked session.",
343
- }
344
- event_log.write("display.ghostty_workspace", agent_id=agent_id, **display)
345
- return display
346
-
347
-
348
- def ghostty_workspace_partial_update_display(
349
- session_name: str,
350
- agent_id: str,
351
- agent: dict[str, Any],
352
- event_log: EventLog,
353
- reason: str = "partial_update_requires_team_restart",
354
- note: str = "pane refresh requires full team restart",
355
- ) -> dict[str, Any]:
356
- aggregator_session = ghostty_workspace_aggregator_name(session_name)
357
- display = {
358
- "backend": "ghostty_workspace",
359
- "status": "blocked",
360
- "reason": reason,
361
- "target": f"{session_name}:{agent_id}",
362
- "linked_session": ghostty_display_session_name(session_name, agent_id),
363
- "aggregator_session": aggregator_session,
364
- "display_session": aggregator_session,
365
- "pane_title": ghostty_workspace_pane_title(agent),
366
- "fallback": "tmux_headless",
367
- "note": note,
368
- "action": "restart the team to rebuild the Ghostty workspace layout",
369
- }
370
- event_log.write("display.ghostty_workspace_partial_update", agent_id=agent_id, **display)
371
- return display
372
-
373
-
374
- def kill_ghostty_workspace_linked_sessions(linked_sessions: list[str]) -> list[str]:
375
- from team_agent.runtime import _tmux_session_exists, run_cmd
376
- killed: list[str] = []
377
- for linked_session in dict.fromkeys(linked_sessions):
378
- if _tmux_session_exists(linked_session):
379
- proc = run_cmd(["tmux", "kill-session", "-t", linked_session], timeout=10)
380
- if proc.returncode == 0:
381
- killed.append(linked_session)
382
- return killed
@@ -1,10 +0,0 @@
1
- class TeamAgentError(Exception):
2
- """Base exception for user-facing Team Agent errors."""
3
-
4
-
5
- class ValidationError(TeamAgentError):
6
- """Spec or result envelope validation failed."""
7
-
8
-
9
- class RuntimeError(TeamAgentError):
10
- """Runtime operation failed."""
@@ -1,84 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- from datetime import datetime, timezone
6
- from pathlib import Path
7
- from typing import Any
8
-
9
- from team_agent.paths import logs_dir
10
-
11
-
12
- # Stage 14 (Gap 36a) — bounded retention. 5 MB cap × 5 archives = 25 MB worst-case.
13
- # Mac mini 2026-05-26 evidence: events.jsonl grew to 28 MB / 128k lines in one day with
14
- # unbounded retention; coordinator's tick-time scan over the file was a ~22% CPU hot path.
15
- # Rotation keeps the current segment small so reads are cheap; archives preserve forensic
16
- # history but are NOT consulted by hot-path scans.
17
- EVENT_LOG_ROTATE_BYTES = 5 * 1024 * 1024
18
- EVENT_LOG_ARCHIVE_KEEP = 5
19
-
20
-
21
- class EventLog:
22
- def __init__(self, workspace: Path):
23
- self.workspace = workspace
24
- self.path = logs_dir(workspace) / "events.jsonl"
25
- self.path.parent.mkdir(parents=True, exist_ok=True)
26
-
27
- def write(self, event_type: str, **fields: Any) -> dict[str, Any]:
28
- event = {
29
- "ts": datetime.now(timezone.utc).isoformat(),
30
- "event": event_type,
31
- **fields,
32
- }
33
- self._maybe_rotate()
34
- with self.path.open("a", encoding="utf-8") as f:
35
- f.write(json.dumps(event, ensure_ascii=False, sort_keys=True) + "\n")
36
- return event
37
-
38
- def tail(self, limit: int = 20) -> list[dict[str, Any]]:
39
- # Hot-path scan reads only the current segment. Archives are forensic; if a
40
- # caller genuinely needs longer history it can iterate _archive_paths explicitly.
41
- if not self.path.exists():
42
- return []
43
- lines = self.path.read_text(encoding="utf-8").splitlines()[-limit:]
44
- out: list[dict[str, Any]] = []
45
- for line in lines:
46
- try:
47
- out.append(json.loads(line))
48
- except json.JSONDecodeError:
49
- out.append({"raw": line})
50
- return out
51
-
52
- def _maybe_rotate(self) -> None:
53
- try:
54
- size = self.path.stat().st_size
55
- except FileNotFoundError:
56
- return
57
- if size < EVENT_LOG_ROTATE_BYTES:
58
- return
59
- # Shift archives: events.jsonl.4 → .5, .3 → .4, …, .1 → .2, current → .1
60
- # Drop the oldest if it would overflow the keep budget.
61
- oldest = self._archive_path(EVENT_LOG_ARCHIVE_KEEP)
62
- if oldest.exists():
63
- try:
64
- oldest.unlink()
65
- except OSError:
66
- pass
67
- for idx in range(EVENT_LOG_ARCHIVE_KEEP - 1, 0, -1):
68
- src = self._archive_path(idx)
69
- dst = self._archive_path(idx + 1)
70
- if src.exists():
71
- try:
72
- os.replace(src, dst)
73
- except OSError:
74
- pass
75
- try:
76
- os.replace(self.path, self._archive_path(1))
77
- except OSError:
78
- pass
79
-
80
- def _archive_path(self, index: int) -> Path:
81
- return self.path.with_name(f"{self.path.name}.{index}")
82
-
83
- def _archive_paths(self) -> list[Path]:
84
- return [self._archive_path(i) for i in range(1, EVENT_LOG_ARCHIVE_KEEP + 1)]
@@ -1,80 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import argparse
4
- import json
5
- import re
6
- import sys
7
- from pathlib import Path
8
-
9
- from team_agent.runtime import report_result
10
-
11
-
12
- def main() -> None:
13
- parser = argparse.ArgumentParser()
14
- parser.add_argument("--workspace", required=True)
15
- parser.add_argument("--agent-id", required=True)
16
- args = parser.parse_args()
17
- workspace = Path(args.workspace)
18
- print(f"TEAM_AGENT_FAKE_READY agent={args.agent_id}", flush=True)
19
- rendered_message: list[str] | None = None
20
- for line in sys.stdin:
21
- line = line.strip()
22
- if not line:
23
- if rendered_message is not None:
24
- rendered_message.append("")
25
- continue
26
- print(f"TEAM_AGENT_FAKE_WORKING agent={args.agent_id}", flush=True)
27
- if line.startswith("TEAM_AGENT_MESSAGE "):
28
- payload = json.loads(line.removeprefix("TEAM_AGENT_MESSAGE "))
29
- _report_fake_result(workspace, args.agent_id, payload)
30
- elif line.startswith("Team Agent message from "):
31
- rendered_message = [line]
32
- elif rendered_message is not None:
33
- rendered_message.append(line)
34
- if line.startswith("[team-agent-token:"):
35
- payload = _parse_rendered_message(rendered_message)
36
- _report_fake_result(workspace, args.agent_id, payload)
37
- rendered_message = None
38
- print(f"TEAM_AGENT_FAKE_READY agent={args.agent_id}", flush=True)
39
-
40
-
41
- def _parse_rendered_message(lines: list[str]) -> dict[str, str | None]:
42
- header = lines[0]
43
- match = re.match(r"Team Agent message from (?P<sender>[^:]+?)(?: for (?P<task_id>[^:]+))?:$", header)
44
- token_line = next((line for line in lines if line.startswith("[team-agent-token:")), "[team-agent-token:manual]")
45
- token = token_line.removeprefix("[team-agent-token:").removesuffix("]")
46
- content_lines = [line for line in lines[1:] if not line.startswith("[team-agent-token:")]
47
- content = "\n".join(content_lines).strip()
48
- return {
49
- "message_id": token,
50
- "task_id": match.group("task_id") if match else None,
51
- "from": match.group("sender") if match else "leader",
52
- "content": content,
53
- }
54
-
55
-
56
- def _report_fake_result(workspace: Path, agent_id: str, payload: dict) -> None:
57
- task_id = payload.get("task_id") or "manual"
58
- envelope = {
59
- "schema_version": "result_envelope_v1",
60
- "task_id": task_id,
61
- "agent_id": agent_id,
62
- "status": "success",
63
- "summary": f"Fake worker handled message {payload['message_id']}",
64
- "changes": [],
65
- "tests": [{"command": "fake-provider", "status": "passed"}],
66
- "risks": [],
67
- "artifacts": [
68
- {
69
- "path": str(workspace / ".team" / "logs" / f"{agent_id}.scrollback"),
70
- "description": "tmux scrollback for fake worker",
71
- }
72
- ],
73
- "next_actions": [],
74
- }
75
- report_result(workspace, envelope)
76
- print(json.dumps(envelope), flush=True)
77
-
78
-
79
- if __name__ == "__main__":
80
- main()