@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
@@ -0,0 +1,99 @@
1
+ //! step 10 · leader — ownership lease / leader-session binding / idle-takeover wiring
2
+ //! (ROUND-0 SKELETON: types + fn/method SURFACE only, all bodies `unimplemented!()`).
3
+ //!
4
+ //! Card: `docs/phase0/subsystems/10-leader.md`
5
+ //! (owned data/types table §16-37, public API §41-53, locks/rules §95-99).
6
+ //! Truth source (READ-ONLY snapshot `team-agent-public` @ v0.2.11 / `439bef8`):
7
+ //! - `leader/__init__.py` (926 lines) — the five lease paths
8
+ //! (attach / start / claim / autobind / readopt) + leader identity context
9
+ //! + dual-state write + audit events.
10
+ //! - `leader_binding.py` (183) — Family A positive-source owner binding
11
+ //! (`bind_owner_from_caller_pane` / `derive_leader_session_uuid` / `emit_owner_bound_event`).
12
+ //! - `idle_takeover.py` (facade re-export) / `idle_takeover_wiring.py`
13
+ //! (`build_idle_nodes` / `push_idle_reminder`) / `wake.py`
14
+ //! (`should_reread` / `on_file_changed` / `take_pending`).
15
+ //! - `messaging/leader_panes.py` (`_leader_command_looks_usable` /
16
+ //! `_leader_command_provider` / `_target_leader_session_uuid`).
17
+ //!
18
+ //! 职责(card §职责):拥有「谁是 leader、消息投到哪个 pane」这一身份事实,当成租约(lease)
19
+ //! 管理。pane id 即权威路由/授权身份,`owner_epoch` 做 CAS/去重,确定派生的
20
+ //! `leader_session_uuid` + 注入 env 作兼容/审计元数据。统一 attach/claim/takeover/autobind/
21
+ //! readopt 五条路径到同一把锁(`LEADER_OWNERSHIP_LOCK = "send"`)同一组安全门,双写
22
+ //! workspace-level + team-level 两份 state 不分叉,每个 acquire/rebind/refusal 写结构化审计。
23
+ //!
24
+ //! 铁律(card §bug/陷阱 §80-89):
25
+ //! - **unknown ≠ idle**:turn state 用 `provider::TurnState` 穷尽 match,`Unknown` 显式
26
+ //! block ping,**绝不** `_ => idle`。
27
+ //! - **bug-085 None 漏穿**:`rollout_path` 用 `Option<RolloutPath>`,`None` 走 `Unknown`
28
+ //! 分支并记 diagnostics;`_leader_node` path/provider 缺则省略 leader 节点而非猜 idle。
29
+ //! - **pane 即身份(C2/C10)**:授权是 pane id 等值,owner liveness 要求候选 pane 携带 owner
30
+ //! 的 `leader_session_uuid`,不只看「leader-looking 命令名」。cwd 比对两边 realpath 后子树
31
+ //! 包含,禁 basename/startswith/子串/反推。
32
+ //! - **死 owner 不锁活 caller(C4)**:liveness 探针失败 = 未确认活 → fail-safe 不据 stale
33
+ //! record 拒新 caller;模糊信号(命令错/cwd 错但 pane 在)要 `--confirm`。
34
+ //! - **TOCTOU epoch race(C3/C15)**:precheck 后在锁内 revalidate epoch + liveness,真 CAS。
35
+ //! - **双写不分叉(C17/C18)**:同一锁内写 workspace state.json + team/<session> snapshot;
36
+ //! 发现分叉写 `state_divergence_repaired`。多候选 claim 分支也必须双写。
37
+ //! - **claude_code 归一**:`provider::Provider` 单一归一,穷尽 match,编译期堵死。
38
+ //! - **wake 不轮询/不解析**:`wake` 层纯决策何时重读,provider-neutral。
39
+ //!
40
+ //! ROUND-0:仅类型 + fn/method 签名;fallible 操作返 `Result`/`Option`(§10 实现层禁
41
+ //! unwrap/expect/panic,故签名先 fallible)。daemon-path(tick/idle push)返 `Result`。
42
+ //! `#![deny(...)]` 由 leader 在 integration 时统一加,本骨架不加。
43
+
44
+ // ROUND-0 skeleton:fn body 全 unimplemented!() → import/field/method 暂未被用;P2 porter 落实现时移除。
45
+ #![allow(dead_code, unused_imports)]
46
+ // §10:lease / owner-bind / idle-takeover / tick 路径,实现层禁 unwrap/expect/panic(未 port 的
47
+ // unimplemented! stub 不算 —— 那些待各自 contract+port)。
48
+ #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
49
+
50
+ use std::collections::BTreeMap;
51
+ use std::path::{Path, PathBuf};
52
+
53
+ use serde::{Deserialize, Serialize};
54
+ use serde_json::{json, Value};
55
+ use thiserror::Error;
56
+
57
+ // ── REUSE: 既有 typed 地基(不重定义)──────────────────────────────────────────
58
+ // model::ids — leader 身份/owner epoch newtypes(§3)。
59
+ use crate::model::ids::{LeaderSessionUuid, OwnerEpoch, TeamKey};
60
+ // provider — turn state 穷尽枚举(unknown≠idle 命门)+ provider 归一 + bug-085 None 漏穿。
61
+ use crate::provider::{Provider, RolloutPath, TurnState};
62
+ // transport — pane 寻址 / 后端无关 PaneInfo(身份/rebind 地基)。
63
+ use crate::transport::{PaneId, SessionName, Target, WindowName};
64
+ // state — owner-gate caller 身份 + first-time binding + identity 派生 + projection。
65
+ use crate::state::owner_gate::CallerIdentity;
66
+ use crate::state::StateError;
67
+
68
+ // ── REUSE-by-reference(签名引用,实际复用既有自由函数,不在此重声明)──────────────
69
+ // state::identity::{apply_first_time_leader_binding, leader_env_exports,
70
+ // validate_leader_uuid_from_targets, caller_identity_from_env,
71
+ // identity_machine_fingerprint, identity_os_user}
72
+ // state::projection::{team_state_key, select_runtime_state}
73
+ // state::owner_gate::{check_team_owner, workspace_paths_match, PaneLivenessProbe}
74
+ // model::ids::LeaderSessionUuid::derive (== Python derive_leader_session_uuid)
75
+ // event_log::EventLog (write/tail)
76
+
77
+ // ── submodules(by responsibility) ──────────────────────────────────────────
78
+ mod helpers;
79
+ pub mod inject;
80
+ pub mod lease;
81
+ pub mod owner_bind;
82
+ pub mod rediscover;
83
+ pub mod start;
84
+ pub mod takeover;
85
+ pub mod types;
86
+
87
+ // ── RE-EXPORT INVARIANT:每个先前 root-visible 项原路径不变 ────────────────────
88
+ pub use inject::*;
89
+ pub use lease::*;
90
+ pub use owner_bind::*;
91
+ pub use rediscover::*;
92
+ pub use start::*;
93
+ pub use takeover::*;
94
+ pub use types::*;
95
+ // 私有 helper(原 module-private,现 pub(crate) 跨子模块复用)按原可达性再导出。
96
+ pub(crate) use helpers::*;
97
+
98
+ #[cfg(test)]
99
+ mod tests;
@@ -0,0 +1,292 @@
1
+ //! leader::owner_bind — Family A 正源 owner 绑定(bind_owner_from_caller_pane / emit_owner_bound_event)
2
+ //! + leader 身份上下文派生(override / state / derive)。
3
+
4
+ use std::path::Path;
5
+
6
+ use serde_json::{json, Value};
7
+
8
+ use crate::model::ids::{LeaderSessionUuid, OwnerEpoch, TeamKey};
9
+ use crate::provider::Provider;
10
+ use crate::tmux_backend::TmuxBackend;
11
+ use crate::transport::{PaneField, PaneId, Target, Transport};
12
+
13
+ use super::helpers::{get_path_str, now_ts, prefix, resolve_workspace_for_hash};
14
+ use super::{
15
+ ClaimedVia, LeaderError, LeaderEvent, LeaderIdentity, LeaderSessionUuidSource, LeaseReason,
16
+ OwnerBindResult, TeamOwner,
17
+ };
18
+
19
+ // ── leader::identity — leader_identity / 身份上下文 ──
20
+
21
+ /// `leader_identity`(card §47;`__init__.py:355`)。`team-agent identity` 入口。
22
+ /// 返回 uuid_prefix + 身份字段(JSON dict,CLI 直出)。
23
+ pub fn leader_identity(workspace: &Path, team: Option<&str>) -> Result<Value, LeaderError> {
24
+ let state = crate::state::persist::load_runtime_state(workspace)?;
25
+ let identity = leader_identity_context(workspace, team, Some(&state))?;
26
+ Ok(json!({
27
+ "ok": true,
28
+ "uuid_prefix": prefix(identity.leader_session_uuid.as_str(), 12),
29
+ "machine_fingerprint": identity.machine_fingerprint,
30
+ "workspace_abspath": identity.workspace_abspath.to_string_lossy(),
31
+ "os_user": identity.os_user,
32
+ "team_id": identity.team_id.as_str(),
33
+ "current_pane_id": std::env::var("TEAM_AGENT_LEADER_PANE_ID")
34
+ .ok()
35
+ .filter(|v| !v.is_empty())
36
+ .or_else(|| std::env::var("TMUX_PANE").ok().filter(|v| !v.is_empty())),
37
+ "last_seen_at": get_path_str(&state, &["leader_receiver", "attached_at"])
38
+ .or_else(|| get_path_str(&state, &["leader_receiver", "last_seen_at"])),
39
+ "source": serde_json::to_value(identity.leader_session_uuid_source)?,
40
+ }))
41
+ }
42
+
43
+ /// `_leader_identity_context`(`__init__.py:192`)。派生 leader 身份上下文(override / state / derive)。
44
+ pub fn leader_identity_context(
45
+ workspace: &Path,
46
+ team: Option<&str>,
47
+ state: Option<&Value>,
48
+ ) -> Result<LeaderIdentity, LeaderError> {
49
+ let team_id = TeamKey::new(match team {
50
+ Some(t) => t.to_string(),
51
+ None => state
52
+ .map(crate::state::projection::team_state_key)
53
+ .unwrap_or_else(|| "current".to_string()),
54
+ });
55
+ let workspace_abspath = resolve_workspace_for_hash(workspace);
56
+ let machine_fingerprint = state
57
+ .and_then(|s| get_path_str(s, &["team_owner", "machine_fingerprint"]))
58
+ .or_else(|| std::env::var("TEAM_AGENT_MACHINE_FINGERPRINT").ok())
59
+ .unwrap_or_default();
60
+ let os_user = std::env::var("USER")
61
+ .or_else(|_| std::env::var("USERNAME"))
62
+ .unwrap_or_default();
63
+ if let Ok(raw) = std::env::var("TEAM_AGENT_LEADER_SESSION_UUID_OVERRIDE") {
64
+ if !raw.is_empty() {
65
+ return Ok(LeaderIdentity {
66
+ leader_session_uuid: serde_json::from_value(Value::String(raw))?,
67
+ leader_session_uuid_source: LeaderSessionUuidSource::Override,
68
+ machine_fingerprint,
69
+ workspace_abspath,
70
+ os_user,
71
+ team_id,
72
+ });
73
+ }
74
+ }
75
+ if let Some(state_uuid) = state
76
+ .and_then(|s| get_path_str(s, &["team_owner", "leader_session_uuid"]))
77
+ .or_else(|| state.and_then(|s| get_path_str(s, &["leader_receiver", "leader_session_uuid"])))
78
+ {
79
+ return Ok(LeaderIdentity {
80
+ leader_session_uuid: serde_json::from_value(Value::String(state_uuid))?,
81
+ leader_session_uuid_source: LeaderSessionUuidSource::Derived,
82
+ machine_fingerprint,
83
+ workspace_abspath,
84
+ os_user,
85
+ team_id,
86
+ });
87
+ }
88
+ let leader_session_uuid = LeaderSessionUuid::derive(
89
+ &machine_fingerprint,
90
+ &workspace_abspath.to_string_lossy(),
91
+ &os_user,
92
+ team_id.as_str(),
93
+ )?;
94
+ Ok(LeaderIdentity {
95
+ leader_session_uuid,
96
+ leader_session_uuid_source: LeaderSessionUuidSource::Derived,
97
+ machine_fingerprint,
98
+ workspace_abspath,
99
+ os_user,
100
+ team_id,
101
+ })
102
+ }
103
+
104
+ // ── leader::binding — Family A 正源 owner 绑定 + derive_leader_session_uuid ──
105
+
106
+ /// `bind_owner_from_caller_pane`(card §49;`leader_binding.py:46`)。Family A 正源 owner 绑定:
107
+ /// 身份只来自 `$TMUX_PANE` + 一次定向 `tmux display-message` 查 `pane_current_command`。
108
+ /// 缺 `$TMUX_PANE` → refuse + `owner.bind_refused`(`reason=caller_pane_missing`)。
109
+ pub fn bind_owner_from_caller_pane(
110
+ workspace: &Path,
111
+ team_id: &TeamKey,
112
+ override_uuid: Option<&LeaderSessionUuid>,
113
+ ) -> Result<OwnerBindResult, LeaderError> {
114
+ let event_log = crate::event_log::EventLog::new(workspace);
115
+ let Some(pane) = std::env::var("TMUX_PANE").ok().filter(|p| !p.is_empty()) else {
116
+ let hint = "run team-agent from inside your leader pane (the tmux pane you want to own this team).";
117
+ event_log.write(
118
+ LeaderEvent::OwnerBindRefused.name(),
119
+ json!({
120
+ "reason": serde_json::to_value(LeaseReason::CallerPaneMissing)?,
121
+ "caller_pane_id": "",
122
+ "caller_current_command": "",
123
+ "team_id": team_id.as_str(),
124
+ "hint": hint,
125
+ }),
126
+ )?;
127
+ return Ok(OwnerBindResult {
128
+ ok: false,
129
+ owner: None,
130
+ caller_pane_id: PaneId::new(""),
131
+ caller_current_command: String::new(),
132
+ team_id: team_id.clone(),
133
+ reason: Some(LeaseReason::CallerPaneMissing),
134
+ hint: Some(hint.to_string()),
135
+ });
136
+ };
137
+ let caller_current_command = tmux_pane_current_command(workspace, &pane).unwrap_or_default();
138
+ let provider = bind_provider_from_env_or_command(&caller_current_command);
139
+ let machine_fingerprint = std::env::var("TEAM_AGENT_MACHINE_FINGERPRINT").unwrap_or_default();
140
+ let os_user = std::env::var("USER")
141
+ .or_else(|_| std::env::var("USERNAME"))
142
+ .unwrap_or_default();
143
+ let identity = family_a_identity(workspace, team_id, override_uuid, &machine_fingerprint, &os_user)?;
144
+ let owner = TeamOwner {
145
+ pane_id: PaneId::new(pane.clone()),
146
+ provider,
147
+ machine_fingerprint: machine_fingerprint.clone(),
148
+ leader_session_uuid: Some(identity.leader_session_uuid),
149
+ owner_epoch: OwnerEpoch::FIRST,
150
+ claimed_at: now_ts(),
151
+ claimed_via: ClaimedVia::ClaimLeader,
152
+ os_user: Some(os_user),
153
+ };
154
+ Ok(OwnerBindResult {
155
+ ok: true,
156
+ owner: Some(owner),
157
+ caller_pane_id: PaneId::new(pane),
158
+ caller_current_command,
159
+ team_id: team_id.clone(),
160
+ reason: None,
161
+ hint: None,
162
+ })
163
+ }
164
+
165
+ /// `emit_owner_bound_event`(`leader_binding.py:162`)。成功绑定后的审计 hook
166
+ /// (`owner.bound_from_caller_pane`;只写 uuid 短前缀,不泄全 uuid)。
167
+ pub fn emit_owner_bound_event(
168
+ workspace: &Path,
169
+ caller_pane_id: &PaneId,
170
+ caller_current_command: &str,
171
+ derived_leader_session_uuid: &LeaderSessionUuid,
172
+ team_id: &TeamKey,
173
+ old_leader_session_uuid: Option<&LeaderSessionUuid>,
174
+ ) -> Result<(), LeaderError> {
175
+ crate::event_log::EventLog::new(workspace).write(
176
+ LeaderEvent::OwnerBoundFromCallerPane.name(),
177
+ json!({
178
+ "caller_pane_id": caller_pane_id.as_str(),
179
+ "caller_current_command": caller_current_command,
180
+ "derived_uuid_prefix": prefix(derived_leader_session_uuid.as_str(), 12),
181
+ "old_uuid_prefix": old_leader_session_uuid.map_or("", |u| prefix(u.as_str(), 12)),
182
+ "team_id": team_id.as_str(),
183
+ }),
184
+ )?;
185
+ Ok(())
186
+ }
187
+
188
+ fn bind_provider_from_env_or_command(command: &str) -> Provider {
189
+ std::env::var("TEAM_AGENT_LEADER_PROVIDER")
190
+ .ok()
191
+ .and_then(|raw| super::helpers::parse_provider(&raw))
192
+ .unwrap_or_else(|| provider_from_command(command))
193
+ }
194
+
195
+ fn provider_from_command(command: &str) -> Provider {
196
+ match exact_command_name(command).as_deref() {
197
+ Some("claude") | Some("claude.exe") => Provider::Claude,
198
+ Some("codex") => Provider::Codex,
199
+ Some("fake") => Provider::Fake,
200
+ _ => Provider::Codex,
201
+ }
202
+ }
203
+
204
+ fn exact_command_name(command: &str) -> Option<String> {
205
+ let last = command
206
+ .split_whitespace()
207
+ .next()
208
+ .unwrap_or(command)
209
+ .rsplit(['/', '\\'])
210
+ .next()?;
211
+ let lower = last.to_ascii_lowercase();
212
+ if lower.is_empty() { None } else { Some(lower) }
213
+ }
214
+
215
+ pub fn owner_bind_provider_wire(command: &str) -> &'static str {
216
+ if let Ok(raw) = std::env::var("TEAM_AGENT_LEADER_PROVIDER") {
217
+ return match raw.as_str() {
218
+ "claude" => "claude",
219
+ "claude_code" => "claude_code",
220
+ "codex" => "codex",
221
+ "gemini_cli" => "gemini_cli",
222
+ "fake" => "fake",
223
+ _ => "",
224
+ };
225
+ }
226
+ match exact_command_name(command).as_deref() {
227
+ Some("claude") | Some("claude.exe") => "claude",
228
+ Some("codex") => "codex",
229
+ Some("fake") => "fake",
230
+ _ => "",
231
+ }
232
+ }
233
+
234
+ fn family_a_identity(
235
+ workspace: &Path,
236
+ team_id: &TeamKey,
237
+ override_uuid: Option<&LeaderSessionUuid>,
238
+ machine_fingerprint: &str,
239
+ os_user: &str,
240
+ ) -> Result<LeaderIdentity, LeaderError> {
241
+ if let Some(uuid) = override_uuid {
242
+ return Ok(LeaderIdentity {
243
+ leader_session_uuid: uuid.clone(),
244
+ leader_session_uuid_source: LeaderSessionUuidSource::Override,
245
+ machine_fingerprint: machine_fingerprint.to_string(),
246
+ workspace_abspath: resolve_workspace_for_hash(workspace),
247
+ os_user: os_user.to_string(),
248
+ team_id: team_id.clone(),
249
+ });
250
+ }
251
+ if let Ok(raw) = std::env::var("TEAM_AGENT_LEADER_SESSION_UUID_OVERRIDE") {
252
+ if !raw.is_empty() {
253
+ return Ok(LeaderIdentity {
254
+ leader_session_uuid: serde_json::from_value(Value::String(raw))?,
255
+ leader_session_uuid_source: LeaderSessionUuidSource::Override,
256
+ machine_fingerprint: machine_fingerprint.to_string(),
257
+ workspace_abspath: resolve_workspace_for_hash(workspace),
258
+ os_user: os_user.to_string(),
259
+ team_id: team_id.clone(),
260
+ });
261
+ }
262
+ }
263
+ let workspace_abspath = resolve_workspace_for_hash(workspace);
264
+ let leader_session_uuid = LeaderSessionUuid::derive(
265
+ machine_fingerprint,
266
+ &workspace_abspath.to_string_lossy(),
267
+ os_user,
268
+ team_id.as_str(),
269
+ )?;
270
+ Ok(LeaderIdentity {
271
+ leader_session_uuid,
272
+ leader_session_uuid_source: LeaderSessionUuidSource::Derived,
273
+ machine_fingerprint: machine_fingerprint.to_string(),
274
+ workspace_abspath,
275
+ os_user: os_user.to_string(),
276
+ team_id: team_id.clone(),
277
+ })
278
+ }
279
+
280
+ fn tmux_pane_current_command(workspace: &Path, pane: &str) -> Result<String, LeaderError> {
281
+ TmuxBackend::for_workspace(workspace)
282
+ .query(
283
+ &Target::Pane(PaneId::new(pane)),
284
+ PaneField::PaneCurrentCommand,
285
+ )
286
+ .map(|value| value.unwrap_or_default())
287
+ .map_err(|e| LeaderError::Tmux(e.to_string()))
288
+ }
289
+
290
+ // NOTE: `derive_leader_session_uuid`(`leader_binding.py:146`)已由
291
+ // `model::ids::LeaderSessionUuid::derive` 字节对齐实现(含 NUL 拒绝 + golden 测试)——
292
+ // 此 lane REUSE 之,不重声明。