@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,69 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- import urllib.parse
5
- from typing import Any
6
-
7
- from team_agent.rust_core import redact_text
8
- from team_agent.profiles.constants import SECRET_KEYS
9
-
10
-
11
- def _format_profile_check_failure(check: dict[str, Any]) -> str:
12
- agent_id = check.get("agent_id") or "unknown"
13
- profile = check.get("profile") or "-"
14
- reason = check.get("reason") or "profile_invalid"
15
- suggestion = check.get("suggestion") or f"Inspect safely with `team-agent profile show {profile} --workspace . --json`."
16
- return f"profile validation failed for {agent_id} profile {profile}: {reason}. {suggestion}"
17
-
18
- def _alternate_value(values: dict[str, str], key: str) -> str | None:
19
- alternates = {
20
- "BASE_URL": ["ANTHROPIC_BASE_URL", "OPENAI_BASE_URL"],
21
- "API_KEY": ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "AUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY"],
22
- }
23
- for candidate in alternates.get(key, []):
24
- if values.get(candidate):
25
- return values[candidate]
26
- return None
27
-
28
- def _strip_env_value(value: str) -> str:
29
- if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
30
- return value[1:-1]
31
- return value
32
-
33
- def _safe_codex_provider_id(value: str) -> bool:
34
- return re.fullmatch(r"[A-Za-z0-9_-]+", value) is not None
35
-
36
- def _is_secret_key(key: str) -> bool:
37
- upper = key.upper()
38
- return upper in SECRET_KEYS or "KEY" in upper or "TOKEN" in upper or "SECRET" in upper
39
-
40
- def _safe_profile_value(key: str, value: str) -> dict[str, Any]:
41
- if _is_secret_key(key):
42
- return {"present": bool(value), "redacted": True}
43
- return {"present": bool(value), "redacted": False, "value": _safe_plain_profile_value(value)}
44
-
45
- def _common_missing_values(auth_mode: str | None, values: dict[str, str]) -> list[str]:
46
- if auth_mode == "compatible_api":
47
- required = ["BASE_URL", "API_KEY", "MODEL"]
48
- elif auth_mode == "official_api":
49
- required = ["API_KEY"]
50
- else:
51
- required = []
52
- missing = []
53
- for key in required:
54
- if key == "MODEL":
55
- if not (values.get("MODEL") or values.get("ANTHROPIC_MODEL")):
56
- missing.append(key)
57
- continue
58
- if not values.get(key) and not _alternate_value(values, key):
59
- missing.append(key)
60
- return missing
61
-
62
- def _safe_plain_profile_value(value: str) -> str:
63
- parsed = urllib.parse.urlparse(value)
64
- if parsed.scheme and parsed.netloc:
65
- host = parsed.hostname or ""
66
- port = f":{parsed.port}" if parsed.port else ""
67
- auth = "[redacted]@" if parsed.username or parsed.password else ""
68
- value = urllib.parse.urlunparse((parsed.scheme, f"{auth}{host}{port}", parsed.path, "", "", ""))
69
- return str(redact_text(value).get("text") or "")
@@ -1,188 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import shlex
5
- from pathlib import Path
6
- from typing import Any
7
-
8
- from team_agent.profiles.constants import COMPATIBLE_API_NETWORK_ENV_KEYS
9
- from team_agent.profiles.helpers import _safe_codex_provider_id
10
-
11
-
12
- def _provider_env_exports(provider: str, auth_mode: str, values: dict[str, str]) -> dict[str, str]:
13
- if auth_mode == "subscription":
14
- return {}
15
- if provider in {"claude", "claude_code"}:
16
- exports: dict[str, str] = {}
17
- base_url = values.get("ANTHROPIC_BASE_URL") or values.get("BASE_URL")
18
- api_key = values.get("ANTHROPIC_API_KEY") or values.get("API_KEY")
19
- auth_token = values.get("ANTHROPIC_AUTH_TOKEN") or values.get("AUTH_TOKEN")
20
- model = values.get("ANTHROPIC_MODEL") or values.get("MODEL")
21
- if base_url:
22
- exports["ANTHROPIC_BASE_URL"] = base_url
23
- if auth_mode == "official_api" and api_key:
24
- exports["ANTHROPIC_API_KEY"] = api_key
25
- if auth_token or (auth_mode == "compatible_api" and api_key):
26
- exports["ANTHROPIC_AUTH_TOKEN"] = auth_token or api_key
27
- if model:
28
- exports["ANTHROPIC_MODEL"] = model
29
- return exports
30
- if provider == "codex":
31
- exports = {}
32
- api_key = values.get("OPENAI_API_KEY") or values.get("API_KEY")
33
- if api_key:
34
- exports["TEAM_AGENT_PROVIDER_API_KEY"] = api_key
35
- exports.setdefault("OPENAI_API_KEY", api_key)
36
- if values.get("BASE_URL"):
37
- exports["OPENAI_BASE_URL"] = values["BASE_URL"]
38
- return exports
39
- if provider == "gemini_cli":
40
- api_key = values.get("GEMINI_API_KEY") or values.get("API_KEY")
41
- return {"GEMINI_API_KEY": api_key} if api_key else {}
42
- return {}
43
-
44
- def _provider_env_unsets(provider: str, auth_mode: str) -> list[str]:
45
- unsets: list[str] = []
46
- if provider in {"claude", "claude_code"}:
47
- if auth_mode == "compatible_api":
48
- unsets.append("ANTHROPIC_API_KEY")
49
- if auth_mode == "official_api":
50
- unsets.append("ANTHROPIC_AUTH_TOKEN")
51
- if provider == "codex" and auth_mode == "compatible_api":
52
- unsets.extend(["OPENAI_API_KEY", "OPENAI_BASE_URL"])
53
- if provider == "gemini_cli" and auth_mode == "compatible_api":
54
- unsets.append("GEMINI_API_KEY")
55
- return sorted(set(unsets))
56
-
57
- def _provider_command_overrides(provider: str, auth_mode: str, values: dict[str, str], agent: dict[str, Any]) -> dict[str, Any]:
58
- overrides: dict[str, Any] = {}
59
- model = agent.get("model") or values.get("MODEL") or values.get("ANTHROPIC_MODEL")
60
- if model:
61
- overrides["model"] = str(model)
62
- if provider == "codex":
63
- codex_profile = values.get("CODEX_PROFILE") or values.get("NATIVE_PROFILE")
64
- if codex_profile:
65
- overrides["codex_profile"] = codex_profile
66
- configs: list[str] = []
67
- model_provider = values.get("MODEL_PROVIDER")
68
- base_url = values.get("BASE_URL")
69
- if auth_mode == "compatible_api" and model_provider and base_url and _safe_codex_provider_id(model_provider):
70
- configs.append(f'model_provider="{model_provider}"')
71
- prefix = f"model_providers.{model_provider}"
72
- configs.append(f'{prefix}.base_url="{base_url}"')
73
- configs.append(f'{prefix}.env_key="TEAM_AGENT_PROVIDER_API_KEY"')
74
- if values.get("WIRE_API"):
75
- configs.append(f'{prefix}.wire_api="{values["WIRE_API"]}"')
76
- if values.get("PROVIDER_NAME"):
77
- configs.append(f'{prefix}.name="{values["PROVIDER_NAME"]}"')
78
- if configs:
79
- overrides["codex_config"] = configs
80
- return overrides
81
-
82
- def _write_runtime_env_file(workspace: Path, agent_id: str, exports: dict[str, str], unsets: list[str]) -> Path:
83
- directory = workspace / ".team" / "runtime" / "provider-env"
84
- directory.mkdir(parents=True, exist_ok=True)
85
- path = directory / f"{agent_id}.env"
86
- lines = [f"unset {key}" for key in sorted(unsets)]
87
- lines.extend(f"export {key}={shlex.quote(value)}" for key, value in sorted(exports.items()))
88
- path.write_text("\n".join(lines) + "\n", encoding="utf-8")
89
- try:
90
- path.chmod(0o600)
91
- except OSError:
92
- pass
93
- return path
94
-
95
- def _compatible_claude_config_dir(workspace: Path, agent_id: str) -> Path:
96
- directory = workspace / ".team" / "runtime" / "provider-config" / agent_id / "claude"
97
- directory.mkdir(parents=True, exist_ok=True)
98
- _ensure_compatible_claude_config(directory, workspace)
99
- return directory
100
-
101
- def ensure_compatible_claude_mcp_config(workspace: Path, agent_id: str, mcp_config: dict[str, Any]) -> None:
102
- if not mcp_config:
103
- return
104
- directory = _compatible_claude_config_dir(workspace, agent_id)
105
- state_path = directory / ".claude.json"
106
- state = _read_json_object(state_path)
107
- projects = state.setdefault("projects", {})
108
- if not isinstance(projects, dict):
109
- projects = {}
110
- state["projects"] = projects
111
- for project_key in _claude_project_keys(workspace):
112
- project = projects.setdefault(project_key, {})
113
- if not isinstance(project, dict):
114
- project = {}
115
- projects[project_key] = project
116
- project["hasTrustDialogAccepted"] = True
117
- project.setdefault("projectOnboardingSeenCount", 1)
118
- project.setdefault("allowedTools", [])
119
- project.setdefault("mcpContextUris", [])
120
- project.setdefault("enabledMcpjsonServers", [])
121
- project.setdefault("disabledMcpjsonServers", [])
122
- project.setdefault("hasClaudeMdExternalIncludesApproved", False)
123
- project.setdefault("hasClaudeMdExternalIncludesWarningShown", False)
124
- servers = project.setdefault("mcpServers", {})
125
- if not isinstance(servers, dict):
126
- servers = {}
127
- project["mcpServers"] = servers
128
- servers.update(mcp_config)
129
- _write_json(state_path, state)
130
-
131
- def _ensure_compatible_claude_config(directory: Path, workspace: Path) -> None:
132
- settings_path = directory / "settings.json"
133
- settings = _read_json_object(settings_path)
134
- settings.setdefault("theme", "auto")
135
- settings.setdefault("skipDangerousModePermissionPrompt", True)
136
- _write_json(settings_path, settings)
137
-
138
- state_path = directory / ".claude.json"
139
- state = _read_json_object(state_path)
140
- state["hasCompletedOnboarding"] = True
141
- state.setdefault("lastOnboardingVersion", "2.1.0")
142
- state.setdefault("firstStartTime", "1970-01-01T00:00:00.000Z")
143
- state.setdefault("numStartups", 0)
144
- projects = state.get("projects")
145
- if not isinstance(projects, dict):
146
- projects = {}
147
- state["projects"] = projects
148
- for project_key in _claude_project_keys(workspace):
149
- project = projects.get(project_key)
150
- if not isinstance(project, dict):
151
- project = {}
152
- projects[project_key] = project
153
- project["hasTrustDialogAccepted"] = True
154
- project.setdefault("projectOnboardingSeenCount", 1)
155
- _write_json(state_path, state)
156
-
157
- def _claude_project_keys(workspace: Path) -> list[str]:
158
- keys = [str(workspace)]
159
- try:
160
- resolved = str(workspace.resolve())
161
- except OSError:
162
- resolved = None
163
- if resolved and resolved not in keys:
164
- keys.append(resolved)
165
- return keys
166
-
167
- def _read_json_object(path: Path) -> dict[str, Any]:
168
- try:
169
- data = json.loads(path.read_text(encoding="utf-8"))
170
- except (OSError, json.JSONDecodeError):
171
- return {}
172
- return data if isinstance(data, dict) else {}
173
-
174
- def _write_json(path: Path, data: dict[str, Any]) -> None:
175
- path.parent.mkdir(parents=True, exist_ok=True)
176
- path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
177
- try:
178
- path.chmod(0o600)
179
- except OSError:
180
- pass
181
-
182
- def _compatible_api_network_exports(auth_mode: str, values: dict[str, str]) -> dict[str, str]:
183
- if auth_mode != "compatible_api" or _profile_proxy_mode(values) == "direct":
184
- return {}
185
- return {key: values[key] for key in COMPATIBLE_API_NETWORK_ENV_KEYS if values.get(key)}
186
-
187
- def _profile_proxy_mode(values: dict[str, str]) -> str:
188
- return str(values.get("PROXY_MODE") or values.get("NETWORK_MODE") or "inherit").strip().lower()
@@ -1,201 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from contextlib import contextmanager
4
- import json
5
- import os
6
- import urllib.error
7
- import urllib.parse
8
- import urllib.request
9
- from typing import Any
10
-
11
- from team_agent.profiles.constants import COMPATIBLE_API_NETWORK_ENV_KEYS
12
- from team_agent.profiles.provider_env import _compatible_api_network_exports, _profile_proxy_mode
13
- from team_agent.rust_core import redact_text
14
-
15
-
16
- def _anthropic_compatible_smoke(
17
- values: dict[str, str],
18
- model: str | None,
19
- base_result: dict[str, Any],
20
- timeout: float,
21
- ) -> dict[str, Any]:
22
- base_url = values.get("ANTHROPIC_BASE_URL") or values.get("BASE_URL")
23
- api_key = values.get("ANTHROPIC_API_KEY") or values.get("API_KEY")
24
- auth_token = values.get("ANTHROPIC_AUTH_TOKEN") or values.get("AUTH_TOKEN")
25
- if not base_url or not (api_key or auth_token) or not model:
26
- return {**base_result, "ok": False, "status": "smoke_failed", "reason": "missing_base_url_api_key_or_model"}
27
- endpoint = _anthropic_messages_url(base_url)
28
- payload = {
29
- "model": model,
30
- "max_tokens": 1,
31
- "messages": [{"role": "user", "content": "ping"}],
32
- }
33
- headers = {
34
- "content-type": "application/json",
35
- "anthropic-version": values.get("ANTHROPIC_VERSION") or "2023-06-01",
36
- }
37
- headers["authorization"] = f"Bearer {auth_token or api_key}"
38
- return _http_json_smoke(endpoint, payload, headers, values, base_result, timeout)
39
-
40
- def _openai_compatible_smoke(
41
- values: dict[str, str],
42
- model: str | None,
43
- base_result: dict[str, Any],
44
- timeout: float,
45
- ) -> dict[str, Any]:
46
- base_url = values.get("OPENAI_BASE_URL") or values.get("BASE_URL")
47
- api_key = values.get("OPENAI_API_KEY") or values.get("API_KEY")
48
- if not base_url or not api_key or not model:
49
- return {**base_result, "ok": False, "status": "smoke_failed", "reason": "missing_base_url_api_key_or_model"}
50
- endpoint = _openai_chat_url(base_url)
51
- payload = {
52
- "model": model,
53
- "max_tokens": 1,
54
- "messages": [{"role": "user", "content": "ping"}],
55
- }
56
- headers = {"content-type": "application/json", "authorization": f"Bearer {api_key}"}
57
- return _http_json_smoke(endpoint, payload, headers, values, base_result, timeout)
58
-
59
- def _http_json_smoke(
60
- endpoint: str,
61
- payload: dict[str, Any],
62
- headers: dict[str, str],
63
- values: dict[str, str],
64
- base_result: dict[str, Any],
65
- timeout: float,
66
- ) -> dict[str, Any]:
67
- proxy_info = _proxy_info_for_endpoint(endpoint, values)
68
- request = urllib.request.Request(
69
- endpoint,
70
- data=json.dumps(payload).encode("utf-8"),
71
- headers=headers,
72
- method="POST",
73
- )
74
- try:
75
- with _temporary_profile_network_env(values):
76
- response_ctx = urllib.request.urlopen(request, timeout=timeout)
77
- with response_ctx as response:
78
- status = int(getattr(response, "status", 200))
79
- body = response.read(1024).decode("utf-8", errors="replace")
80
- except urllib.error.HTTPError as exc:
81
- body = exc.read(4096).decode("utf-8", errors="replace")
82
- return {
83
- **base_result,
84
- **proxy_info,
85
- "ok": False,
86
- "status": "smoke_failed",
87
- "reason": "http_error",
88
- "http_status": exc.code,
89
- "endpoint": _redacted_endpoint(endpoint),
90
- "error": redact_text(body or str(exc)).get("text"),
91
- }
92
- except Exception as exc:
93
- reason = "proxy_connectivity_failed" if proxy_info.get("proxy_configured") else "request_failed"
94
- return {
95
- **base_result,
96
- **proxy_info,
97
- "ok": False,
98
- "status": "smoke_failed",
99
- "reason": reason,
100
- "endpoint": _redacted_endpoint(endpoint),
101
- "error": redact_text(str(exc)).get("text"),
102
- "suggestion": (
103
- "Proxy is configured for this request; allow the profile BASE_URL through the proxy or disable the proxy for Team Agent startup."
104
- if proxy_info.get("proxy_configured")
105
- else "Check BASE_URL network connectivity from this machine."
106
- ),
107
- }
108
- if 200 <= status < 300:
109
- return {
110
- **base_result,
111
- **proxy_info,
112
- "ok": True,
113
- "status": "smoke_passed",
114
- "http_status": status,
115
- "endpoint": _redacted_endpoint(endpoint),
116
- }
117
- return {
118
- **base_result,
119
- **proxy_info,
120
- "ok": False,
121
- "status": "smoke_failed",
122
- "reason": "unexpected_status",
123
- "http_status": status,
124
- "endpoint": _redacted_endpoint(endpoint),
125
- "error": redact_text(body).get("text"),
126
- }
127
-
128
- def _anthropic_messages_url(base_url: str) -> str:
129
- base = base_url.rstrip("/")
130
- if base.endswith("/messages"):
131
- return base
132
- if base.endswith("/v1"):
133
- return f"{base}/messages"
134
- return f"{base}/v1/messages"
135
-
136
- def _openai_chat_url(base_url: str) -> str:
137
- base = base_url.rstrip("/")
138
- if base.endswith("/chat/completions"):
139
- return base
140
- if base.endswith("/v1"):
141
- return f"{base}/chat/completions"
142
- return f"{base}/v1/chat/completions"
143
-
144
- def _redacted_endpoint(endpoint: str) -> str:
145
- return endpoint.split("?", 1)[0]
146
-
147
- def _proxy_info_for_endpoint(endpoint: str, values: dict[str, str]) -> dict[str, Any]:
148
- parsed = urllib.parse.urlparse(endpoint)
149
- if _profile_proxy_mode(values) == "direct":
150
- return {"proxy_configured": False, "proxy_mode": "direct"}
151
- profile_env = _compatible_api_network_exports("compatible_api", values)
152
- proxy_url = _proxy_url_from_env(parsed.scheme, profile_env)
153
- if proxy_url:
154
- return {
155
- "proxy_configured": True,
156
- "proxy_scheme": parsed.scheme,
157
- "proxy_url": _redact_proxy_url(proxy_url),
158
- "proxy_source": "profile",
159
- }
160
- ambient_proxy_url = _proxy_url_from_env(parsed.scheme, os.environ)
161
- if ambient_proxy_url:
162
- return {
163
- "proxy_configured": True,
164
- "proxy_scheme": parsed.scheme,
165
- "proxy_url": _redact_proxy_url(ambient_proxy_url),
166
- "proxy_source": "ambient",
167
- }
168
- return {"proxy_configured": False}
169
-
170
- def _proxy_url_from_env(scheme: str, env: Any) -> str | None:
171
- upper = f"{scheme.upper()}_PROXY"
172
- lower = f"{scheme.lower()}_proxy"
173
- return env.get(upper) or env.get(lower) or env.get("ALL_PROXY") or env.get("all_proxy")
174
-
175
- @contextmanager
176
- def _temporary_profile_network_env(values: dict[str, str]) -> Any:
177
- profile_env = _compatible_api_network_exports("compatible_api", values)
178
- direct = _profile_proxy_mode(values) == "direct"
179
- touched_keys = COMPATIBLE_API_NETWORK_ENV_KEYS if direct else tuple(profile_env)
180
- saved = {key: os.environ.get(key) for key in touched_keys}
181
- try:
182
- if direct:
183
- for key in COMPATIBLE_API_NETWORK_ENV_KEYS:
184
- os.environ.pop(key, None)
185
- os.environ.update(profile_env)
186
- yield
187
- finally:
188
- for key, value in saved.items():
189
- if value is None:
190
- os.environ.pop(key, None)
191
- else:
192
- os.environ[key] = value
193
-
194
- def _redact_proxy_url(proxy_url: str) -> str:
195
- parsed = urllib.parse.urlparse(proxy_url)
196
- if not parsed.netloc:
197
- return proxy_url
198
- host = parsed.hostname or ""
199
- port = f":{parsed.port}" if parsed.port else ""
200
- auth = "[redacted]@" if parsed.username or parsed.password else ""
201
- return urllib.parse.urlunparse((parsed.scheme, f"{auth}{host}{port}", parsed.path, "", "", ""))
@@ -1,43 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from team_agent.provider_cli.adapter import (
4
- ProviderAdapter,
5
- ResumeUnavailable,
6
- agent_model,
7
- parse_time,
8
- read_json_object,
9
- )
10
- from team_agent.provider_cli.base import (
11
- ProviderCapabilityError,
12
- ProviderCliSocket,
13
- ProviderStartupInput,
14
- )
15
- from team_agent.provider_cli.claude import ClaudeCodeAdapter
16
- from team_agent.provider_cli.codex import CodexAdapter
17
- from team_agent.provider_cli.copilot import CopilotCliPlug
18
- from team_agent.provider_cli.fake import FakeAdapter
19
- from team_agent.provider_cli.gemini import GeminiCliAdapter
20
- from team_agent.provider_cli.opencode import OpenCodeCliPlug
21
- from team_agent.provider_cli.prompt import TEAMMATE_SYSTEM_PROMPT, compile_system_prompt
22
- from team_agent.provider_cli.registry import PLUG_TYPES, build_plug
23
-
24
- __all__ = [
25
- "ClaudeCodeAdapter",
26
- "CodexAdapter",
27
- "CopilotCliPlug",
28
- "FakeAdapter",
29
- "GeminiCliAdapter",
30
- "OpenCodeCliPlug",
31
- "PLUG_TYPES",
32
- "ProviderAdapter",
33
- "ProviderCapabilityError",
34
- "ProviderCliSocket",
35
- "ProviderStartupInput",
36
- "ResumeUnavailable",
37
- "TEAMMATE_SYSTEM_PROMPT",
38
- "agent_model",
39
- "build_plug",
40
- "compile_system_prompt",
41
- "parse_time",
42
- "read_json_object",
43
- ]
@@ -1,172 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import shutil
5
- import subprocess
6
- import sys
7
- from datetime import datetime, timezone
8
- from pathlib import Path
9
- from typing import Any
10
-
11
- from team_agent.paths import repo_root
12
-
13
-
14
- class ResumeUnavailable(RuntimeError):
15
- pass
16
-
17
-
18
- class ProviderAdapter:
19
- provider = ""
20
- command_name = ""
21
-
22
- def is_installed(self) -> bool:
23
- return shutil.which(self.command_name) is not None
24
-
25
- def version(self) -> str | None:
26
- if not self.is_installed():
27
- return None
28
- for args in ([self.command_name, "--version"], [self.command_name, "version"]):
29
- try:
30
- proc = subprocess.run(args, text=True, capture_output=True, timeout=8, check=False)
31
- except (OSError, subprocess.TimeoutExpired):
32
- continue
33
- text = (proc.stdout or proc.stderr).strip()
34
- if text:
35
- return text.splitlines()[0]
36
- return "installed"
37
-
38
- def auth_hint(self) -> dict[str, Any]:
39
- return {"status": "unknown", "detail": "adapter cannot verify auth without starting CLI"}
40
-
41
- def build_command(self, agent: dict[str, Any], workspace: Path, mcp_config: dict[str, Any]) -> list[str]:
42
- raise NotImplementedError
43
-
44
- def capture_session_id(
45
- self,
46
- agent_id: str,
47
- spawn_context: dict[str, Any],
48
- timeout_s: float = 3.0,
49
- ) -> dict[str, Any] | None:
50
- _ = agent_id, spawn_context, timeout_s
51
- return None
52
-
53
- def build_resume_command(
54
- self,
55
- agent_state: dict[str, Any],
56
- workspace: Path,
57
- mcp_config: dict[str, Any] | None = None,
58
- ) -> list[str]:
59
- _ = workspace, mcp_config
60
- session_id = agent_state.get("session_id")
61
- if not session_id:
62
- raise ResumeUnavailable("session_id is required to resume")
63
- raise ResumeUnavailable(f"{self.provider} does not support resume")
64
-
65
- def supports_session_fork(self, agent: dict[str, Any] | None = None) -> bool:
66
- _ = agent
67
- return False
68
-
69
- def build_fork_command(
70
- self,
71
- agent: dict[str, Any],
72
- source_session_id: str,
73
- workspace: Path,
74
- mcp_config: dict[str, Any],
75
- ) -> list[str]:
76
- _ = agent, source_session_id, workspace, mcp_config
77
- raise ResumeUnavailable(f"{self.provider} does not support native session fork")
78
-
79
- def session_is_resumable(self, agent_state: dict[str, Any], workspace: Path) -> bool:
80
- _ = workspace
81
- return bool(agent_state.get("session_id"))
82
-
83
- def recover_session_id(
84
- self,
85
- agent_id: str,
86
- agent_state: dict[str, Any],
87
- workspace: Path,
88
- exclude_session_ids: set[str] | None = None,
89
- ) -> dict[str, Any] | None:
90
- _ = agent_id, agent_state, workspace, exclude_session_ids
91
- return None
92
-
93
- def mcp_config(self, workspace: Path, agent_id: str, team_id: str | None = None) -> dict[str, Any]:
94
- # 0.2.6 Family C (C13): worker spawn env always carries the owning
95
- # team id so the MCP server can scope sender requests without
96
- # asking the worker which team it belongs to.
97
- env = {
98
- "TEAM_AGENT_ID": agent_id,
99
- "TEAM_AGENT_OWNER_TEAM_ID": str(team_id or ""),
100
- "PYTHONPATH": str(repo_root() / "src"),
101
- }
102
- return {
103
- "team_orchestrator": {
104
- "type": "stdio",
105
- "command": sys.executable,
106
- "args": ["-m", "team_agent.mcp_server", "--workspace", str(workspace)],
107
- "env": env,
108
- }
109
- }
110
-
111
- def install_mcp(self, workspace: Path, agent_id: str, config: dict[str, Any]) -> Path:
112
- path = workspace / ".team" / "runtime" / "mcp" / f"{agent_id}.json"
113
- path.parent.mkdir(parents=True, exist_ok=True)
114
- path.write_text(json.dumps({"mcpServers": config}, indent=2), encoding="utf-8")
115
- return path
116
-
117
- def cleanup_mcp(self, workspace: Path, agent_id: str, mcp_path: Path | None = None) -> None:
118
- return None
119
-
120
- def status_patterns(self) -> dict[str, str]:
121
- return {"idle": "", "processing": "", "error": "Error|Traceback|panic"}
122
-
123
- def exit_text(self) -> str:
124
- return "/exit"
125
-
126
- def handle_startup_prompts(
127
- self,
128
- session_name: str,
129
- window_name: str,
130
- checks: int = 30,
131
- sleep_s: float = 0.5,
132
- ) -> list[dict[str, Any]]:
133
- return []
134
-
135
- def handle_runtime_prompts(self, session_name: str, window_name: str) -> list[dict[str, Any]]:
136
- return []
137
-
138
- def validate_model(self, model: str | None) -> dict[str, Any]:
139
- return {"ok": True, "status": "not_checked", "provider": self.provider, "model": model}
140
-
141
-
142
- def agent_model(agent: dict[str, Any]) -> str | None:
143
- if agent.get("model"):
144
- return str(agent["model"])
145
- profile_overrides = agent.get("_provider_profile", {}).get("command_overrides", {})
146
- if profile_overrides.get("model"):
147
- return str(profile_overrides["model"])
148
- return None
149
-
150
-
151
- def read_json_object(path: Path) -> dict[str, Any]:
152
- if not path.exists():
153
- return {}
154
- data = json.loads(path.read_text(encoding="utf-8"))
155
- if not isinstance(data, dict):
156
- raise ValueError(f"{path}: expected a JSON object")
157
- return data
158
-
159
-
160
- def parse_time(value: Any) -> datetime | None:
161
- if isinstance(value, datetime):
162
- return value if value.tzinfo else value.replace(tzinfo=timezone.utc)
163
- if not value:
164
- return None
165
- text = str(value)
166
- if text.endswith("Z"):
167
- text = text[:-1] + "+00:00"
168
- try:
169
- dt = datetime.fromisoformat(text)
170
- except ValueError:
171
- return None
172
- return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)