@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,123 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- CANONICAL_TOOLS = {
6
- "fs_read",
7
- "fs_write",
8
- "fs_list",
9
- "execute_bash",
10
- "git_diff",
11
- "network",
12
- "mcp_team",
13
- "provider_builtin",
14
- }
15
-
16
- ROLE_DEFAULTS = {
17
- "leader": ["fs_read", "fs_list", "mcp_team", "provider_builtin"],
18
- "supervisor": ["fs_read", "fs_list", "mcp_team", "provider_builtin"],
19
- "implementation_engineer": [
20
- "fs_read",
21
- "fs_write",
22
- "fs_list",
23
- "execute_bash",
24
- "git_diff",
25
- "mcp_team",
26
- "provider_builtin",
27
- ],
28
- "developer": [
29
- "fs_read",
30
- "fs_write",
31
- "fs_list",
32
- "execute_bash",
33
- "git_diff",
34
- "mcp_team",
35
- "provider_builtin",
36
- ],
37
- "researcher": ["fs_read", "fs_list", "network", "mcp_team", "provider_builtin"],
38
- "reviewer": ["fs_read", "fs_list", "git_diff", "mcp_team", "provider_builtin"],
39
- "code_reviewer": ["fs_read", "fs_list", "git_diff", "mcp_team", "provider_builtin"],
40
- }
41
-
42
- PROVIDER_ENFORCEMENT = {
43
- "claude_code": {
44
- "fs_read": "hard",
45
- "fs_write": "hard",
46
- "fs_list": "hard",
47
- "execute_bash": "hard",
48
- "git_diff": "hard",
49
- "network": "prompt_only",
50
- "mcp_team": "hard",
51
- "provider_builtin": "hard",
52
- },
53
- "codex": {tool: "prompt_only" for tool in CANONICAL_TOOLS},
54
- "gemini_cli": {
55
- "fs_read": "hard",
56
- "fs_write": "hard",
57
- "fs_list": "hard",
58
- "execute_bash": "hard",
59
- "git_diff": "hard",
60
- "network": "prompt_only",
61
- "mcp_team": "prompt_only",
62
- "provider_builtin": "hard",
63
- },
64
- "fake": {tool: "hard" for tool in CANONICAL_TOOLS},
65
- }
66
-
67
-
68
- def expand_tools(tools: list[str]) -> list[str]:
69
- expanded: list[str] = []
70
- for tool in tools:
71
- if tool == "fs_*":
72
- expanded.extend(["fs_read", "fs_write", "fs_list"])
73
- elif tool == "@builtin":
74
- expanded.append("provider_builtin")
75
- elif tool in {"@team-orchestrator", "@cao-mcp-server"}:
76
- expanded.append("mcp_team")
77
- elif tool == "*":
78
- expanded.extend(sorted(CANONICAL_TOOLS))
79
- else:
80
- expanded.append(tool)
81
- return sorted(set(expanded))
82
-
83
-
84
- def default_tools_for_role(role: str) -> list[str]:
85
- return list(ROLE_DEFAULTS.get(role, ROLE_DEFAULTS["developer"]))
86
-
87
-
88
- def resolve_permissions(agent: dict[str, Any]) -> dict[str, Any]:
89
- provider = agent["provider"]
90
- tools = agent.get("tools") or default_tools_for_role(agent.get("role", "developer"))
91
- resolved = expand_tools(tools)
92
- enforcement_map = PROVIDER_ENFORCEMENT.get(provider, {})
93
- entries = [
94
- {
95
- "tool": tool,
96
- "enforcement": enforcement_map.get(tool, "prompt_only"),
97
- }
98
- for tool in resolved
99
- ]
100
- return {
101
- "agent_id": agent.get("id"),
102
- "provider": provider,
103
- "tools": resolved,
104
- "resolved_tools": entries,
105
- "has_prompt_only": any(e["enforcement"] == "prompt_only" for e in entries),
106
- }
107
-
108
-
109
- def task_required_tools(task: dict[str, Any]) -> list[str]:
110
- required = list(task.get("requires_tools", []))
111
- task_type = task.get("type")
112
- if task_type in {"implementation", "bug_fix", "test"}:
113
- required.extend(["fs_write", "execute_bash"])
114
- if task_type in {"review", "risk_check"}:
115
- required.extend(["fs_read", "git_diff"])
116
- if task_type in {"research", "architecture"}:
117
- required.extend(["fs_read"])
118
- return expand_tools(required)
119
-
120
-
121
- def missing_tools(agent: dict[str, Any], task: dict[str, Any]) -> list[str]:
122
- allowed = set(resolve_permissions(agent)["tools"])
123
- return [tool for tool in task_required_tools(task) if tool not in allowed]
@@ -1,82 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import urllib
4
-
5
- from team_agent.profiles.constants import *
6
- from team_agent.profiles.core import (
7
- _agent_profile_dir,
8
- _profile_lookup_dir,
9
- _safe_init_command,
10
- _safe_inspection_command,
11
- compact_profile_check,
12
- doctor_profile,
13
- effective_model,
14
- ensure_profile_secret_boundary,
15
- ensure_profile_secret_boundary_dir,
16
- gitignore_patterns,
17
- init_profile,
18
- known_profiles,
19
- load_profile,
20
- parse_env_file,
21
- prepare_agent_profile_launch,
22
- profile_dir,
23
- required_profile_keys,
24
- show_profile,
25
- smoke_check_agent_profile,
26
- validate_agent_profile,
27
- )
28
- from team_agent.profiles.helpers import (
29
- _alternate_value,
30
- _common_missing_values,
31
- _format_profile_check_failure,
32
- _is_secret_key,
33
- _safe_codex_provider_id,
34
- _safe_plain_profile_value,
35
- _safe_profile_value,
36
- _strip_env_value,
37
- )
38
- from team_agent.profiles.provider_env import (
39
- _claude_project_keys,
40
- _compatible_api_network_exports,
41
- _compatible_claude_config_dir,
42
- _ensure_compatible_claude_config,
43
- _profile_proxy_mode,
44
- _provider_command_overrides,
45
- _provider_env_exports,
46
- _provider_env_unsets,
47
- _read_json_object,
48
- _write_json,
49
- _write_runtime_env_file,
50
- ensure_compatible_claude_mcp_config,
51
- )
52
- from team_agent.profiles.smoke import (
53
- _anthropic_compatible_smoke,
54
- _anthropic_messages_url,
55
- _http_json_smoke,
56
- _openai_chat_url,
57
- _openai_compatible_smoke,
58
- _proxy_info_for_endpoint,
59
- _proxy_url_from_env,
60
- _redact_proxy_url,
61
- _redacted_endpoint,
62
- _temporary_profile_network_env,
63
- )
64
-
65
- _REQUIRED_EXPORTS = (
66
- "profile_dir",
67
- "init_profile",
68
- "doctor_profile",
69
- "show_profile",
70
- "load_profile",
71
- "parse_env_file",
72
- "validate_agent_profile",
73
- "smoke_check_agent_profile",
74
- "prepare_agent_profile_launch",
75
- "effective_model",
76
- "ensure_compatible_claude_mcp_config",
77
- )
78
- for _name in _REQUIRED_EXPORTS:
79
- if _name not in globals():
80
- raise ImportError(f"team_agent.profiles missing export: {_name}")
81
-
82
- __all__ = [name for name in globals() if not name.startswith("__")]
@@ -1,19 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
-
5
-
6
- AUTH_MODES = {"subscription", "official_api", "compatible_api"}
7
- PROFILE_KEY_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
8
- SECRET_KEYS = {"API_KEY", "AUTH_TOKEN", "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY"}
9
- PROXY_ENV_KEYS = ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", "https_proxy", "http_proxy", "all_proxy", "NO_PROXY", "no_proxy")
10
- CA_ENV_KEYS = ("NODE_EXTRA_CA_CERTS", "SSL_CERT_FILE", "REQUESTS_CA_BUNDLE")
11
- COMPATIBLE_API_NETWORK_ENV_KEYS = PROXY_ENV_KEYS + CA_ENV_KEYS
12
- PROFILE_SECRET_BOUNDARY_TEXT = """# Team Agent Profile Secret Boundary
13
-
14
- Do not read, print, grep, cat, sed, copy, summarize, or open raw `*.env` files in this directory.
15
- These files may contain API keys or auth tokens and must stay out of agent context.
16
-
17
- Use `team-agent profile show <name> --workspace . --json` or `team-agent profile doctor <name> --workspace . --json`
18
- for redacted status. If a required value is missing, ask the human user to edit the local profile file.
19
- """
@@ -1,407 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- from typing import Any
5
-
6
- from team_agent.profiles.constants import AUTH_MODES, PROFILE_KEY_RE, PROFILE_SECRET_BOUNDARY_TEXT
7
- from team_agent.profiles.helpers import (
8
- _alternate_value,
9
- _common_missing_values,
10
- _format_profile_check_failure,
11
- _is_secret_key,
12
- _safe_profile_value,
13
- _strip_env_value,
14
- )
15
- from team_agent.rust_core import redact_text
16
-
17
-
18
- def profile_dir(workspace: Path) -> Path:
19
- return workspace / ".team" / "current" / "profiles"
20
-
21
- def _profile_lookup_dir(workspace: Path, profiles_dir: Path | str | None = None) -> Path:
22
- return Path(profiles_dir).resolve() if profiles_dir else profile_dir(workspace)
23
-
24
- def _agent_profile_dir(agent: dict[str, Any]) -> Path | None:
25
- value = agent.get("_profile_dir")
26
- return Path(str(value)).resolve() if value else None
27
-
28
- def _safe_inspection_command(name: str, profiles_dir: Path | str | None = None) -> str:
29
- if profiles_dir:
30
- return f"team-agent profile show {name} --team {Path(profiles_dir).resolve().parent} --json"
31
- return f"team-agent profile show {name} --workspace . --json"
32
-
33
- def _safe_init_command(name: str, auth_mode: str, profiles_dir: Path | str | None = None) -> str:
34
- if profiles_dir:
35
- return f"team-agent profile init {name} --auth-mode {auth_mode} --team {Path(profiles_dir).resolve().parent}"
36
- return f"team-agent profile init {name} --auth-mode {auth_mode}"
37
-
38
- def ensure_profile_secret_boundary_dir(directory: Path) -> Path:
39
- directory.mkdir(parents=True, exist_ok=True)
40
- first_path = directory / "AGENTS.md"
41
- for filename in ("AGENTS.md", "CLAUDE.md"):
42
- path = directory / filename
43
- if not path.exists():
44
- path.write_text(PROFILE_SECRET_BOUNDARY_TEXT, encoding="utf-8")
45
- return first_path
46
-
47
- def ensure_profile_secret_boundary(workspace: Path) -> Path:
48
- directory = profile_dir(workspace)
49
- return ensure_profile_secret_boundary_dir(directory)
50
-
51
- def init_profile(workspace: Path, name: str, auth_mode: str, profiles_dir: Path | str | None = None) -> dict[str, Any]:
52
- if auth_mode not in AUTH_MODES:
53
- raise ValueError(f"unknown auth_mode: {auth_mode}")
54
- directory = _profile_lookup_dir(workspace, profiles_dir)
55
- directory.mkdir(parents=True, exist_ok=True)
56
- ensure_profile_secret_boundary_dir(directory)
57
- path = directory / f"{name}.env"
58
- template_path = directory / f"{name}.example.env"
59
- if auth_mode == "subscription":
60
- body = f"AUTH_MODE=subscription\nPROFILE_NAME={name}\n"
61
- elif auth_mode == "official_api":
62
- body = f"AUTH_MODE=official_api\nPROFILE_NAME={name}\nAPI_KEY=\nMODEL=\n"
63
- else:
64
- body = f"AUTH_MODE=compatible_api\nPROFILE_NAME={name}\nBASE_URL=\nAPI_KEY=\nMODEL=\n"
65
- created_profile = False
66
- created_template = False
67
- if not path.exists():
68
- path.write_text(body, encoding="utf-8")
69
- try:
70
- path.chmod(0o600)
71
- except OSError:
72
- pass
73
- created_profile = True
74
- if not template_path.exists():
75
- template_path.write_text(body, encoding="utf-8")
76
- created_template = True
77
- safe_inspection = _safe_inspection_command(name, directory if profiles_dir else None)
78
- return {
79
- "ok": True,
80
- "profile": name,
81
- "auth_mode": auth_mode,
82
- "path": str(path),
83
- "template_path": str(template_path),
84
- "created_profile": created_profile,
85
- "created_template": created_template,
86
- "secret_written": False,
87
- "safe_inspection_command": safe_inspection,
88
- "raw_file_read_allowed_for_agents": False,
89
- "instruction": (
90
- f"Fill {path} locally; do not paste API keys into chat or role docs. "
91
- f"Agents must inspect this profile only with `{safe_inspection}`."
92
- ),
93
- }
94
-
95
- def doctor_profile(workspace: Path, name: str, profiles_dir: Path | str | None = None) -> dict[str, Any]:
96
- directory = _profile_lookup_dir(workspace, profiles_dir)
97
- loaded = load_profile(workspace, name, directory)
98
- real_path = directory / f"{name}.env"
99
- example_path = directory / f"{name}.example.env"
100
- exists = bool(loaded.get("exists"))
101
- values = loaded.get("values", {})
102
- keys_present = [key for key, value in values.items() if value]
103
- auth_mode = values.get("AUTH_MODE")
104
- return {
105
- "ok": exists,
106
- "profile": name,
107
- "path": str(loaded.get("path") or real_path),
108
- "template_path": str(example_path),
109
- "credential_present": real_path.exists(),
110
- "auth_mode": auth_mode,
111
- "keys_present": sorted(keys_present),
112
- "secret_keys_present": sorted(key for key in keys_present if _is_secret_key(key)),
113
- "redaction_engine": redact_text(" ".join(f"{key}=present" for key in keys_present)).get("engine"),
114
- "secret_values_printed": False,
115
- "safe_for_agent_context": True,
116
- "raw_file_read_allowed_for_agents": False,
117
- "safe_inspection_command": _safe_inspection_command(name, directory if profiles_dir else None),
118
- "suggestion": None if exists else f"Run {_safe_init_command(name, 'subscription', directory if profiles_dir else None)}.",
119
- }
120
-
121
- def show_profile(workspace: Path, name: str, profiles_dir: Path | str | None = None) -> dict[str, Any]:
122
- loaded = load_profile(workspace, name, profiles_dir)
123
- values: dict[str, str] = loaded.get("values", {})
124
- auth_mode = values.get("AUTH_MODE")
125
- safe_values = {
126
- key: _safe_profile_value(key, value)
127
- for key, value in sorted(values.items())
128
- }
129
- missing = _common_missing_values(auth_mode, values)
130
- return {
131
- "ok": bool(loaded.get("exists")),
132
- "profile": name,
133
- "credential_present": bool(loaded.get("credential_present")),
134
- "auth_mode": auth_mode,
135
- "values": safe_values,
136
- "keys_present": sorted(key for key, value in values.items() if value),
137
- "secret_keys_present": sorted(key for key, value in values.items() if value and _is_secret_key(key)),
138
- "missing_common": missing,
139
- "safe_for_agent_context": True,
140
- "secret_values_printed": False,
141
- "raw_file_read_allowed_for_agents": False,
142
- "instruction": (
143
- "Use this redacted output for diagnostics. Do not read .team/*/profiles/*.env "
144
- "or .team/runtime/provider-env/*.env into agent context."
145
- ),
146
- }
147
-
148
- def known_profiles(team_dir: Path) -> set[str]:
149
- directory = team_dir / "profiles"
150
- if not directory.exists():
151
- return set()
152
- names = set()
153
- for path in directory.glob("*.env"):
154
- names.add(path.name.removesuffix(".env").removesuffix(".example"))
155
- for path in directory.glob("*.example.env"):
156
- names.add(path.name.removesuffix(".example.env"))
157
- return names
158
-
159
- def gitignore_patterns() -> list[str]:
160
- return [".team/*/profiles/*.env", "!.team/*/profiles/*.example.env", ".team/runtime/provider-env/*.env"]
161
-
162
- def load_profile(workspace: Path, name: str, profiles_dir: Path | str | None = None) -> dict[str, Any]:
163
- directory = _profile_lookup_dir(workspace, profiles_dir)
164
- real_path = directory / f"{name}.env"
165
- example_path = directory / f"{name}.example.env"
166
- path = real_path if real_path.exists() else example_path
167
- if not path.exists():
168
- return {
169
- "exists": False,
170
- "profile": name,
171
- "path": str(real_path),
172
- "template_path": str(example_path),
173
- "credential_present": False,
174
- "values": {},
175
- }
176
- values = parse_env_file(path)
177
- return {
178
- "exists": True,
179
- "profile": name,
180
- "path": str(path),
181
- "template_path": str(example_path),
182
- "credential_present": real_path.exists(),
183
- "values": values,
184
- }
185
-
186
- def parse_env_file(path: Path) -> dict[str, str]:
187
- values: dict[str, str] = {}
188
- for raw in path.read_text(encoding="utf-8").splitlines():
189
- line = raw.strip()
190
- if not line or line.startswith("#"):
191
- continue
192
- if line.startswith("export "):
193
- line = line[len("export ") :].strip()
194
- if "=" not in line:
195
- continue
196
- key, value = line.split("=", 1)
197
- key = key.strip()
198
- if not PROFILE_KEY_RE.fullmatch(key):
199
- continue
200
- values[key] = _strip_env_value(value.strip())
201
- return values
202
-
203
- def validate_agent_profile(workspace: Path, agent: dict[str, Any]) -> dict[str, Any]:
204
- profile = agent.get("profile")
205
- auth_mode = str(agent.get("auth_mode") or "subscription")
206
- profiles_dir = _agent_profile_dir(agent)
207
- result = {
208
- "ok": True,
209
- "agent_id": agent.get("id"),
210
- "provider": agent.get("provider"),
211
- "profile": profile,
212
- "auth_mode": auth_mode,
213
- "path": None,
214
- "credential_present": False,
215
- "keys_present": [],
216
- "missing_required": [],
217
- "secret_values_printed": False,
218
- }
219
- if not profile:
220
- return result
221
- loaded = load_profile(workspace, str(profile), profiles_dir)
222
- result["path"] = loaded.get("path")
223
- result["credential_present"] = bool(loaded.get("credential_present"))
224
- if not loaded.get("exists"):
225
- result["ok"] = False
226
- result["reason"] = "profile_missing"
227
- result["suggestion"] = (
228
- f"Run {_safe_init_command(str(profile), auth_mode, profiles_dir)}, then ask the human user "
229
- "to fill the generated local profile file."
230
- )
231
- return result
232
- values: dict[str, str] = loaded.get("values", {})
233
- file_auth_mode = values.get("AUTH_MODE")
234
- if file_auth_mode and file_auth_mode != auth_mode:
235
- result["ok"] = False
236
- result["reason"] = "auth_mode_mismatch"
237
- result["file_auth_mode"] = file_auth_mode
238
- result["suggestion"] = (
239
- f"Set AUTH_MODE={auth_mode} in the local profile or update the role doc. "
240
- f"Inspect safely with `{_safe_inspection_command(str(profile), profiles_dir)}`."
241
- )
242
- return result
243
- keys_present = sorted(key for key, value in values.items() if value)
244
- result["keys_present"] = keys_present
245
- role_model = str(agent.get("model") or "") or None
246
- profile_model = values.get("MODEL") or values.get("ANTHROPIC_MODEL")
247
- effective = role_model or profile_model
248
- result["effective_model"] = effective
249
- result["model_source"] = "role" if role_model else ("profile" if profile_model else None)
250
- if auth_mode == "compatible_api" and role_model and profile_model and role_model != profile_model:
251
- result["ok"] = False
252
- result["reason"] = "model_mismatch"
253
- result["suggestion"] = (
254
- f"Role model {role_model!r} does not match the profile MODEL value; "
255
- f"inspect safely with `{_safe_inspection_command(str(profile), profiles_dir)}`, "
256
- "then remove model from the role doc or make both values identical."
257
- )
258
- return result
259
- required = required_profile_keys(str(agent.get("provider") or ""), auth_mode)
260
- missing = [key for key in required if not values.get(key) and not _alternate_value(values, key)]
261
- if auth_mode == "compatible_api" and not effective:
262
- missing.append("MODEL")
263
- result["missing_required"] = missing
264
- if missing:
265
- result["ok"] = False
266
- result["reason"] = "profile_required_values_missing"
267
- result["suggestion"] = (
268
- f"Ask the human user to fill {', '.join(missing)} in local profile {profile}; "
269
- f"inspect safely with `{_safe_inspection_command(str(profile), profiles_dir)}`. "
270
- "Do not paste API keys into chat."
271
- )
272
- return result
273
-
274
- def smoke_check_agent_profile(workspace: Path, agent: dict[str, Any], timeout: float = 8.0) -> dict[str, Any]:
275
- auth_mode = str(agent.get("auth_mode") or "subscription")
276
- profile = agent.get("profile")
277
- result = {
278
- "ok": True,
279
- "agent_id": agent.get("id"),
280
- "provider": agent.get("provider"),
281
- "profile": profile,
282
- "auth_mode": auth_mode,
283
- "status": "not_required",
284
- "secret_values_printed": False,
285
- }
286
- if auth_mode != "compatible_api" or not profile:
287
- return result
288
- loaded = load_profile(workspace, str(profile), _agent_profile_dir(agent))
289
- values: dict[str, str] = loaded.get("values", {})
290
- if str(values.get("PROFILE_SMOKE", "true")).lower() in {"0", "false", "no", "off"}:
291
- result["status"] = "skipped_by_profile"
292
- return result
293
- validation = validate_agent_profile(workspace, agent)
294
- if not validation.get("ok"):
295
- return {**result, **validation, "ok": False, "status": "profile_invalid"}
296
- provider = str(agent.get("provider") or "")
297
- model = effective_model(agent, workspace)
298
- from team_agent.profiles.smoke import _anthropic_compatible_smoke, _openai_compatible_smoke
299
-
300
- if provider in {"claude", "claude_code"}:
301
- return _anthropic_compatible_smoke(values, model, result, timeout)
302
- if provider == "codex":
303
- return _openai_compatible_smoke(values, model, result, timeout)
304
- result["status"] = "unsupported_provider_smoke_skipped"
305
- return result
306
-
307
- def required_profile_keys(provider: str, auth_mode: str) -> list[str]:
308
- if auth_mode == "subscription":
309
- return []
310
- if provider in {"claude", "claude_code"}:
311
- if auth_mode == "official_api":
312
- return ["API_KEY"]
313
- if auth_mode == "compatible_api":
314
- return ["BASE_URL", "API_KEY"]
315
- if provider == "codex":
316
- if auth_mode == "official_api":
317
- return ["API_KEY"]
318
- if auth_mode == "compatible_api":
319
- return ["BASE_URL", "API_KEY"]
320
- if provider == "gemini_cli" and auth_mode in {"official_api", "compatible_api"}:
321
- return ["API_KEY"]
322
- return []
323
-
324
- def compact_profile_check(check: dict[str, Any]) -> dict[str, Any]:
325
- keys = [
326
- "agent_id",
327
- "provider",
328
- "profile",
329
- "auth_mode",
330
- "ok",
331
- "status",
332
- "reason",
333
- "credential_present",
334
- "keys_present",
335
- "missing_required",
336
- "effective_model",
337
- "model_source",
338
- "suggestion",
339
- "http_status",
340
- "endpoint",
341
- "error",
342
- "proxy_configured",
343
- "proxy_scheme",
344
- "proxy_url",
345
- "proxy_source",
346
- "proxy_mode",
347
- ]
348
- return {key: check.get(key) for key in keys if key in check}
349
-
350
- def prepare_agent_profile_launch(workspace: Path, agent: dict[str, Any]) -> dict[str, Any] | None:
351
- profile = agent.get("profile")
352
- if not profile:
353
- return None
354
- check = validate_agent_profile(workspace, agent)
355
- if not check.get("ok"):
356
- raise ValueError(_format_profile_check_failure(check))
357
- loaded = load_profile(workspace, str(profile), _agent_profile_dir(agent))
358
- values: dict[str, str] = loaded.get("values", {})
359
- auth_mode = str(agent.get("auth_mode") or values.get("AUTH_MODE") or "subscription")
360
- provider = str(agent.get("provider") or "")
361
- from team_agent.profiles.provider_env import (
362
- _compatible_api_network_exports,
363
- _compatible_claude_config_dir,
364
- _profile_proxy_mode,
365
- _provider_command_overrides,
366
- _provider_env_exports,
367
- _provider_env_unsets,
368
- _write_runtime_env_file,
369
- )
370
- from team_agent.profiles.constants import COMPATIBLE_API_NETWORK_ENV_KEYS
371
-
372
- exports = _provider_env_exports(provider, auth_mode, values)
373
- claude_projects_root = None
374
- if provider in {"claude", "claude_code"} and auth_mode == "compatible_api":
375
- claude_config_dir = _compatible_claude_config_dir(workspace, str(agent["id"]))
376
- exports["CLAUDE_CONFIG_DIR"] = str(claude_config_dir)
377
- claude_projects_root = str(claude_config_dir / "projects")
378
- exports.update(_compatible_api_network_exports(auth_mode, values))
379
- unsets = _provider_env_unsets(provider, auth_mode)
380
- if auth_mode == "compatible_api" and _profile_proxy_mode(values) == "direct":
381
- unsets.extend(COMPATIBLE_API_NETWORK_ENV_KEYS)
382
- exports = {key: value for key, value in exports.items() if key not in COMPATIBLE_API_NETWORK_ENV_KEYS}
383
- overrides = _provider_command_overrides(str(agent.get("provider") or ""), auth_mode, values, agent)
384
- env_file = None
385
- if exports or unsets:
386
- env_file = _write_runtime_env_file(workspace, str(agent["id"]), exports, unsets)
387
- return {
388
- "profile": str(profile),
389
- "auth_mode": auth_mode,
390
- "path": loaded.get("path"),
391
- "credential_present": loaded.get("credential_present"),
392
- "env_file": str(env_file) if env_file else None,
393
- "env_keys": sorted(exports),
394
- "env_unset": sorted(unsets),
395
- "claude_projects_root": claude_projects_root,
396
- "command_overrides": overrides,
397
- "secret_values_printed": False,
398
- }
399
-
400
- def effective_model(agent: dict[str, Any], workspace: Path | None = None) -> str | None:
401
- if agent.get("model"):
402
- return str(agent["model"])
403
- if workspace is None or not agent.get("profile"):
404
- return None
405
- loaded = load_profile(workspace, str(agent["profile"]), _agent_profile_dir(agent))
406
- values: dict[str, str] = loaded.get("values", {})
407
- return values.get("MODEL") or values.get("ANTHROPIC_MODEL")