@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,254 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- import re
5
- from typing import Any
6
-
7
- from team_agent.events import EventLog
8
- from team_agent.messaging.deps import datetime, save_runtime_state, team_state_key, timezone
9
-
10
- _COMPACTION_RESET_THRESHOLD_DEFAULT = 3
11
-
12
-
13
- def _compaction_reset_threshold(state: dict[str, Any]) -> int:
14
- from team_agent.messaging.deps import load_spec
15
- spec_path = state.get("spec_path")
16
- if spec_path:
17
- try:
18
- spec = load_spec(spec_path)
19
- except Exception:
20
- spec = {}
21
- runtime_cfg = spec.get("runtime", {}) if isinstance(spec, dict) else {}
22
- raw = runtime_cfg.get("compaction_reset_threshold")
23
- if raw is not None:
24
- try:
25
- value = int(raw)
26
- if value > 0:
27
- return value
28
- except (TypeError, ValueError):
29
- pass
30
- return _COMPACTION_RESET_THRESHOLD_DEFAULT
31
- _PROVIDER_COMMANDS = {"claude", "claude-code", "codex", "node", "node.exe", "claude.exe"}
32
- _COMPACTION_PATTERNS = (
33
- re.compile(r"context compacted", re.IGNORECASE),
34
- re.compile(r"compaction occurred", re.IGNORECASE),
35
- )
36
- _IDLE_PROMPT_PATTERNS = (
37
- re.compile(r"›\s*Find and fix a bug in @filename"),
38
- re.compile(r"─\s*for agents"),
39
- re.compile(r"^›[^\n]*\n(?:\s*\n){0,8}\s*gpt-[\w.-]+\s+\S+\s+·", re.MULTILINE),
40
- # Codex idle input prompt line (rotating hints like
41
- # "› Use /skills to list available skills"). Working lines start with a
42
- # spinner/✱ glyph, not "›". An optional leading "│ " tolerates a boxed
43
- # input frame.
44
- re.compile(r"^(?:│\s*)?›\s", re.MULTILINE),
45
- # Claude Code idle input prompt: an empty "❯" line (the box may render the
46
- # trailing space as U+00A0). Only the empty prompt is idle; a "❯ <command>"
47
- # line is a submitted turn, so the trailing-content form is deliberately
48
- # excluded to avoid false IDLE while Claude is still working.
49
- re.compile(r"^(?:│\s*)?❯[ \t\xa0]*$", re.MULTILINE),
50
- )
51
- # Substantive working indicators carry their own text ("Working", "Thinking",
52
- # "esc to interrupt", ...). The bare spinner glyph alone is only a pane-refresh
53
- # artifact, so it is kept separate: it still counts as working when nothing else
54
- # is present, but it must not override a fresh idle prompt (C14).
55
- _SUBSTANTIVE_WORKING_PATTERNS = (
56
- re.compile(r"\bWorking(?:\s*\((?P<working_seconds>\d+)s\))?", re.IGNORECASE),
57
- re.compile(r"\bReticulating\b", re.IGNORECASE),
58
- re.compile(r"\bBaked for (?P<baked_seconds>\d+)s\b", re.IGNORECASE),
59
- re.compile(r"\bThinking\b", re.IGNORECASE),
60
- re.compile(r"esc to interrupt", re.IGNORECASE),
61
- )
62
- _SPINNER_GLYPH_PATTERN = re.compile(r"[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]")
63
- _WORKING_PATTERNS = _SUBSTANTIVE_WORKING_PATTERNS + (_SPINNER_GLYPH_PATTERN,)
64
- # A live provider working footer is a bullet status line carrying a live
65
- # elapsed-time counter plus the "esc to interrupt" hint, e.g.
66
- # "• Working (35s • esc to interrupt) · 1 background terminal running"
67
- # "• Waiting for background terminal (1m 06s • esc to interrupt) · ..."
68
- # This is matched by the COMMON shape, not per verb (Working/Waiting/Baked/...):
69
- # a "•" line with a parenthesized elapsed counter in either "Ns" or "Nm NNs"
70
- # form, followed by "esc to interrupt" inside the same parentheses. That live
71
- # counter + interrupt hint is only rendered during an active interruptible turn
72
- # and is removed when the turn ends, so it never appears in prose/scrollback
73
- # history (unlike a bare "Working" word or an "esc to interrupt" mention). It is
74
- # the positive "provider is working right now" signal that the permanent input
75
- # box ("› ... gpt-" / "❯") rendered below it must not override.
76
- _LIVE_WORKING_PATTERNS = (
77
- re.compile(r"•\s*[^\n]*?\(\s*(?:\d+m\s*)?\d+s\b[^)\n]*esc to interrupt", re.IGNORECASE),
78
- )
79
-
80
-
81
- def _latest_live_working_footer(scrollback: str) -> str | None:
82
- best: tuple[int, str] | None = None
83
- for pattern in _LIVE_WORKING_PATTERNS:
84
- for match in pattern.finditer(scrollback):
85
- if best is None or match.start() > best[0]:
86
- best = (match.start(), match.group(0))
87
- return best[1] if best else None
88
-
89
-
90
- def classify_agent_activity(
91
- agent_id: str,
92
- provider: str,
93
- last_output_at: str | None,
94
- pane: dict[str, Any] | None,
95
- scrollback: str,
96
- *,
97
- now: datetime | None = None,
98
- stuck_timeout_sec: int = 300,
99
- active_task: bool = False,
100
- pane_delta_recent: bool = False,
101
- ) -> dict[str, Any]:
102
- _ = agent_id, provider
103
- now = now or datetime.now(timezone.utc)
104
- pane = pane or {}
105
- pane_in_mode = str(pane.get("pane_in_mode") or "0")
106
- if pane_in_mode != "0":
107
- return {"status": "uncertain", "confidence": 0.9, "rationale": f"pane_in_mode={pane_in_mode}"}
108
- command = str(pane.get("pane_current_command") or "").split("/")[-1]
109
- if command and command not in _PROVIDER_COMMANDS:
110
- return {"status": "uncertain", "confidence": 0.75, "rationale": f"unexpected pane current_command={command}"}
111
- working = _latest_working_match(scrollback)
112
- substantive = _latest_working_match(scrollback, _SUBSTANTIVE_WORKING_PATTERNS)
113
- idle_pos = _latest_idle_prompt_position(scrollback)
114
- # bug-071: a live provider working footer ("Working (Ns ...)") plus an active
115
- # task is an active turn. The provider input box ("› ... gpt-" / "❯") is
116
- # permanent UI rendered BELOW the footer, so the position-based idle-prompt
117
- # check would otherwise flip a working Codex turn to IDLE. Checked before the
118
- # idle-prompt rule. The seconds-counter form never appears in prose, so a
119
- # real idle prompt (no live footer) is unaffected (C14); gating on
120
- # active_task keeps task-less classifier cases on the existing logic.
121
- live_footer = _latest_live_working_footer(scrollback)
122
- if active_task and live_footer is not None:
123
- return {"status": "working", "confidence": 0.9, "rationale": f"live working footer '{live_footer}' with active task"}
124
- # C14: a fresh idle prompt is the strongest signal. Only a substantive
125
- # working indicator positioned after the prompt counts as newer work; a
126
- # trailing bare spinner glyph (pane refresh) or pane delta must not flip a
127
- # fresh idle prompt to WORKING.
128
- if idle_pos is not None and (substantive is None or idle_pos > substantive[0]):
129
- return {"status": "idle", "confidence": 0.9, "rationale": "provider idle prompt is the latest scrollback signal"}
130
- if working:
131
- _pos, label, elapsed = working
132
- if elapsed is not None and elapsed >= stuck_timeout_sec:
133
- return {"status": "stuck", "confidence": 0.85, "rationale": f"stale {label} indicator for {elapsed}s"}
134
- return {"status": "working", "confidence": 0.9, "rationale": f"{label} indicator is the latest scrollback signal"}
135
- # C15: an active task whose pane changed since the last sync is real work,
136
- # not idle. Placed after the idle-prompt check so a fresh idle prompt always
137
- # wins; without an active task this rule never fires and raw running may stay
138
- # IDLE.
139
- if active_task and pane_delta_recent and (not command or command in _PROVIDER_COMMANDS):
140
- return {"status": "working", "confidence": 0.9, "rationale": "active task with recent pane delta"}
141
- age = _last_output_age_seconds(last_output_at, now)
142
- if age is not None and age >= stuck_timeout_sec:
143
- return {"status": "stuck", "confidence": 0.85, "rationale": "last_output_at exceeded timeout with no idle prompt"}
144
- if age is not None and age <= 120 and (not command or command in _PROVIDER_COMMANDS):
145
- return {"status": "working", "confidence": 0.7, "rationale": "recent output from provider command"}
146
- return {"status": "uncertain", "confidence": 0.5, "rationale": "no decisive prompt or working signal"}
147
-
148
-
149
- def _latest_idle_prompt_position(scrollback: str) -> int | None:
150
- best: int | None = None
151
- for pattern in _IDLE_PROMPT_PATTERNS:
152
- for match in pattern.finditer(scrollback):
153
- if best is None or match.start() > best:
154
- best = match.start()
155
- return best
156
-
157
-
158
- def detect_compaction_degradation(
159
- workspace: Path,
160
- state: dict[str, Any],
161
- event_log: EventLog,
162
- *,
163
- agent_id: str,
164
- provider: str,
165
- scrollback: str,
166
- stuck_loop: bool = False,
167
- ) -> dict[str, Any]:
168
- count = _count_compaction_markers(scrollback)
169
- owner_team_id = team_state_key(state)
170
- team_counts = state.setdefault("coordinator", {}).setdefault("compaction_counts", {}).setdefault(owner_team_id, {})
171
- current = max(int(team_counts.get(agent_id) or 0), count)
172
- team_counts[agent_id] = current
173
- save_runtime_state(workspace, state)
174
- if current <= 0:
175
- return {"ok": True, "event": "compaction_threshold_crossed.none", "compaction_count": current}
176
- event_log.write(
177
- "coordinator.compaction_observed",
178
- agent_id=agent_id,
179
- provider=provider,
180
- team=owner_team_id,
181
- compaction_count=current,
182
- stuck_loop=stuck_loop,
183
- )
184
- if provider != "codex":
185
- event = "compaction_threshold_crossed.ignored_lossless_provider"
186
- event_log.write(event, agent_id=agent_id, provider=provider, team=owner_team_id, compaction_count=current)
187
- return {"ok": True, "event": event, "agent_id": agent_id, "provider": provider, "compaction_count": current}
188
- threshold = _compaction_reset_threshold(state)
189
- if current < threshold and not (current >= 1 and stuck_loop):
190
- return {"ok": True, "event": "compaction_threshold_crossed.below_threshold", "agent_id": agent_id, "compaction_count": current, "threshold": threshold}
191
- return _reset_or_recommend(workspace, state, event_log, agent_id, provider, owner_team_id, current, threshold)
192
-
193
-
194
- def _reset_or_recommend(
195
- workspace: Path,
196
- state: dict[str, Any],
197
- event_log: EventLog,
198
- agent_id: str,
199
- provider: str,
200
- owner_team_id: str,
201
- compaction_count: int,
202
- threshold: int,
203
- ) -> dict[str, Any]:
204
- from team_agent.runtime import reset_agent
205
- reset = reset_agent(workspace, agent_id, discard_session=True)
206
- if reset.get("ok"):
207
- team_counts = state.setdefault("coordinator", {}).setdefault("compaction_counts", {}).setdefault(owner_team_id, {})
208
- team_counts[agent_id] = 0
209
- save_runtime_state(workspace, state)
210
- event = "compaction_threshold_crossed.auto_reset"
211
- event_log.write(event, agent_id=agent_id, provider=provider, team=owner_team_id, compaction_count=compaction_count, threshold=threshold)
212
- return {"ok": True, "event": event, "agent_id": agent_id, "compaction_count": compaction_count, "threshold": threshold, "reset": reset}
213
- event = "compaction_threshold_crossed.recommend_reset"
214
- message = f"agent {agent_id} crossed Codex compaction threshold; run team-agent reset-agent {agent_id} --discard-session"
215
- event_log.write(
216
- event,
217
- agent_id=agent_id,
218
- provider=provider,
219
- team=owner_team_id,
220
- compaction_count=compaction_count,
221
- threshold=threshold,
222
- leader_visible_message=message,
223
- reset_error=reset.get("error") or reset.get("reason"),
224
- )
225
- return {"ok": True, "event": event, "agent_id": agent_id, "compaction_count": compaction_count, "threshold": threshold, "leader_visible_message": message, "reset": reset}
226
-
227
-
228
- def _latest_working_match(
229
- scrollback: str, patterns: tuple[re.Pattern[str], ...] = _WORKING_PATTERNS
230
- ) -> tuple[int, str, int | None] | None:
231
- best: tuple[int, str, int | None] | None = None
232
- for pattern in patterns:
233
- for match in pattern.finditer(scrollback):
234
- elapsed_raw = match.groupdict().get("working_seconds") or match.groupdict().get("baked_seconds")
235
- elapsed = int(elapsed_raw) if elapsed_raw else None
236
- if best is None or match.start() > best[0]:
237
- best = (match.start(), match.group(0), elapsed)
238
- return best
239
-
240
-
241
- def _last_output_age_seconds(last_output_at: str | None, now: datetime) -> float | None:
242
- if not last_output_at:
243
- return None
244
- try:
245
- last = datetime.fromisoformat(last_output_at)
246
- except ValueError:
247
- return None
248
- if last.tzinfo is None:
249
- last = last.replace(tzinfo=timezone.utc)
250
- return max(0.0, (now - last).total_seconds())
251
-
252
-
253
- def _count_compaction_markers(scrollback: str) -> int:
254
- return sum(len(pattern.findall(scrollback)) for pattern in _COMPACTION_PATTERNS)