@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,336 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import re
5
- import subprocess
6
- import time
7
- from datetime import datetime, timedelta, timezone
8
- from pathlib import Path
9
- from typing import Any
10
-
11
- from team_agent.permissions import resolve_permissions
12
- from team_agent.provider_cli.adapter import (
13
- ProviderAdapter,
14
- ResumeUnavailable,
15
- agent_model,
16
- parse_time,
17
- )
18
- from team_agent.provider_cli.prompt import compile_system_prompt
19
-
20
-
21
- class CodexAdapter(ProviderAdapter):
22
- provider = "codex"
23
- command_name = "codex"
24
- _model_catalog_cache: dict[str, Any] | None = None
25
-
26
- def build_command(self, agent: dict[str, Any], workspace: Path, mcp_config: dict[str, Any]) -> list[str]:
27
- cmd = self._base_command(agent, mcp_config, resume=False)
28
- return cmd
29
-
30
- def build_resume_command(
31
- self,
32
- agent_state: dict[str, Any],
33
- workspace: Path,
34
- mcp_config: dict[str, Any] | None = None,
35
- ) -> list[str]:
36
- _ = workspace
37
- session_id = agent_state.get("session_id")
38
- if not session_id:
39
- raise ResumeUnavailable("codex resume requires session_id")
40
- agent = dict(agent_state.get("_agent_spec") or agent_state)
41
- cmd = self._base_command(agent, mcp_config or {}, resume=True)
42
- cmd.append(str(session_id))
43
- return cmd
44
-
45
- def supports_session_fork(self, agent: dict[str, Any] | None = None) -> bool:
46
- return not agent or agent.get("auth_mode") != "compatible_api"
47
-
48
- def build_fork_command(
49
- self,
50
- agent: dict[str, Any],
51
- source_session_id: str,
52
- workspace: Path,
53
- mcp_config: dict[str, Any],
54
- ) -> list[str]:
55
- _ = workspace
56
- if not source_session_id:
57
- raise ResumeUnavailable("codex fork requires source session_id")
58
- cmd = self._base_command(agent, mcp_config, resume=False, fork=True)
59
- cmd.append(str(source_session_id))
60
- return cmd
61
-
62
- def capture_session_id(
63
- self,
64
- agent_id: str,
65
- spawn_context: dict[str, Any],
66
- timeout_s: float = 3.0,
67
- ) -> dict[str, Any] | None:
68
- _ = agent_id
69
- cwd = spawn_context.get("cwd")
70
- if not cwd:
71
- return None
72
- start = parse_time(spawn_context.get("spawn_time")) or datetime.now(timezone.utc)
73
- root = Path(spawn_context.get("sessions_root") or Path.home() / ".codex" / "sessions")
74
- deadline = time.monotonic() + max(timeout_s, 0.0)
75
- exclude = {str(item) for item in spawn_context.get("exclude_session_ids", []) if item}
76
- while True:
77
- match = find_codex_rollout(root, Path(str(cwd)), start, exclude_session_ids=exclude)
78
- if match:
79
- return {
80
- "session_id": match["session_id"],
81
- "rollout_path": match["rollout_path"],
82
- "captured_at": datetime.now(timezone.utc).isoformat(),
83
- "captured_via": "fs_watch",
84
- "attribution_confidence": match["confidence"],
85
- "spawn_cwd": str(cwd),
86
- }
87
- if time.monotonic() >= deadline:
88
- return None
89
- time.sleep(0.2)
90
-
91
- def _base_command(
92
- self,
93
- agent: dict[str, Any],
94
- mcp_config: dict[str, Any],
95
- resume: bool,
96
- fork: bool = False,
97
- ) -> list[str]:
98
- prompt = compile_system_prompt(agent)
99
- cmd = ["codex"]
100
- if resume:
101
- cmd.append("resume")
102
- elif fork:
103
- cmd.append("fork")
104
- cmd.extend(["--no-alt-screen", "--disable", "shell_snapshot", "--disable", "apps"])
105
- profile_overrides = agent.get("_provider_profile", {}).get("command_overrides", {})
106
- if profile_overrides.get("codex_profile"):
107
- cmd.extend(["--profile", str(profile_overrides["codex_profile"])])
108
- if agent.get("_runtime", {}).get("dangerous_auto_approve"):
109
- cmd.append("--dangerously-bypass-approvals-and-sandbox")
110
- else:
111
- tools = set(resolve_permissions(agent)["tools"])
112
- sandbox = "workspace-write" if {"fs_write", "execute_bash"} & tools else "read-only"
113
- cmd.extend(["--sandbox", sandbox, "--ask-for-approval", "on-request"])
114
- model = agent_model(agent)
115
- if model:
116
- cmd.extend(["--model", model])
117
- for config in profile_overrides.get("codex_config", []):
118
- cmd.extend(["-c", str(config)])
119
- if prompt:
120
- escaped = prompt.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")
121
- cmd.extend(["-c", f'developer_instructions="{escaped}"'])
122
- for server_name, cfg in mcp_config.items():
123
- prefix = f"mcp_servers.{server_name}"
124
- cmd.extend(["-c", f'{prefix}.command="{cfg["command"]}"'])
125
- args = "[" + ", ".join(json.dumps(str(arg)) for arg in cfg.get("args", [])) + "]"
126
- cmd.extend(["-c", f"{prefix}.args={args}"])
127
- for env_key, env_val in cfg.get("env", {}).items():
128
- cmd.extend(["-c", f'{prefix}.env.{env_key}="{env_val}"'])
129
- cmd.extend(["-c", f"{prefix}.tool_timeout_sec=600.0"])
130
- return cmd
131
-
132
- def auth_hint(self) -> dict[str, Any]:
133
- if "OPENAI_API_KEY" in __import__("os").environ:
134
- return {"status": "present", "detail": "OPENAI_API_KEY is set"}
135
- if Path.home().joinpath(".codex").exists():
136
- return {"status": "present", "detail": "~/.codex exists; run codex login if startup fails"}
137
- return {"status": "missing_or_unknown", "detail": "run codex login or set OPENAI_API_KEY"}
138
-
139
- def status_patterns(self) -> dict[str, str]:
140
- return {"idle": r"(›|❯|codex>)", "processing": r"•.*esc to interrupt", "error": "Error|Traceback|panic"}
141
-
142
- def handle_startup_prompts(
143
- self,
144
- session_name: str,
145
- window_name: str,
146
- checks: int = 30,
147
- sleep_s: float = 0.5,
148
- ) -> list[dict[str, Any]]:
149
- handled: list[dict[str, Any]] = []
150
- target = f"{session_name}:{window_name}"
151
- for _ in range(max(checks, 0)):
152
- proc = subprocess.run(
153
- ["tmux", "capture-pane", "-p", "-S", "-", "-t", target],
154
- text=True,
155
- capture_output=True,
156
- timeout=5,
157
- check=False,
158
- )
159
- output = proc.stdout if proc.returncode == 0 else ""
160
- update = maybe_skip_update_prompt(target, output)
161
- if update:
162
- handled.append(update)
163
- if sleep_s > 0:
164
- time.sleep(sleep_s)
165
- continue
166
- trust_pos = max(
167
- output.rfind("Do you trust the contents of this directory?"),
168
- output.rfind("Do you trust the files in this folder?"),
169
- output.rfind("Do you trust this folder?"),
170
- )
171
- ready_pos = max(output.rfind("OpenAI Codex"), output.rfind("›"), output.rfind("codex>"))
172
- if trust_pos >= 0 and trust_pos > ready_pos:
173
- subprocess.run(["tmux", "send-keys", "-t", target, "Enter"], check=False)
174
- handled.append({"prompt": "codex_workspace_trust", "action": "sent_enter"})
175
- if sleep_s > 0:
176
- time.sleep(sleep_s)
177
- continue
178
- if ready_pos >= 0:
179
- break
180
- if sleep_s > 0:
181
- time.sleep(sleep_s)
182
- return handled
183
-
184
- def handle_runtime_prompts(self, session_name: str, window_name: str) -> list[dict[str, Any]]:
185
- target = f"{session_name}:{window_name}"
186
- proc = subprocess.run(
187
- ["tmux", "capture-pane", "-p", "-S", "-", "-t", target],
188
- text=True,
189
- capture_output=True,
190
- timeout=5,
191
- check=False,
192
- )
193
- output = proc.stdout if proc.returncode == 0 else ""
194
- handled = maybe_skip_update_prompt(target, output)
195
- return [handled] if handled else []
196
-
197
- def validate_model(self, model: str | None) -> dict[str, Any]:
198
- if not model:
199
- return {"ok": True, "status": "model_not_set", "provider": self.provider, "model": model}
200
- catalog = self._model_catalog()
201
- if not catalog.get("ok"):
202
- details = {key: value for key, value in catalog.items() if key != "ok"}
203
- return {"ok": False, "status": "model_catalog_unavailable", "provider": self.provider, "model": model, **details}
204
- models = catalog.get("models", [])
205
- slugs = {str(item.get("slug") or "") for item in models if item.get("slug")}
206
- if model in slugs:
207
- return {"ok": True, "status": "model_supported", "provider": self.provider, "model": model}
208
- slug_by_lower = {slug.lower(): slug for slug in slugs}
209
- display_to_slug = {
210
- str(item.get("display_name") or "").lower(): str(item.get("slug"))
211
- for item in models
212
- if item.get("display_name") and item.get("slug")
213
- }
214
- normalized = model.lower()
215
- suggested = slug_by_lower.get(normalized) or display_to_slug.get(normalized)
216
- result = {
217
- "ok": False,
218
- "status": "unsupported_model",
219
- "reason": "model_id_not_found",
220
- "provider": self.provider,
221
- "model": model,
222
- "available_models": sorted(slugs),
223
- }
224
- if suggested:
225
- result["reason"] = "model_id_not_exact"
226
- result["suggested_model"] = suggested
227
- return result
228
-
229
- def _model_catalog(self) -> dict[str, Any]:
230
- if self._model_catalog_cache is not None:
231
- return self._model_catalog_cache
232
- if not self.is_installed():
233
- return {"ok": False, "reason": "codex_command_missing", "command": self.command_name}
234
- try:
235
- proc = subprocess.run(
236
- [self.command_name, "debug", "models"],
237
- text=True,
238
- capture_output=True,
239
- timeout=12,
240
- check=False,
241
- )
242
- except (OSError, subprocess.TimeoutExpired) as exc:
243
- return {"ok": False, "reason": "model_catalog_command_failed", "command": "codex debug models", "error": str(exc)}
244
- if proc.returncode != 0:
245
- return {
246
- "ok": False,
247
- "reason": "model_catalog_command_failed",
248
- "command": "codex debug models",
249
- "stderr": proc.stderr.strip(),
250
- }
251
- try:
252
- data = json.loads(proc.stdout or "{}")
253
- except json.JSONDecodeError as exc:
254
- return {"ok": False, "reason": "model_catalog_parse_failed", "command": "codex debug models", "error": str(exc)}
255
- models = data.get("models")
256
- if not isinstance(models, list):
257
- return {"ok": False, "reason": "model_catalog_shape_invalid", "command": "codex debug models"}
258
- self._model_catalog_cache = {"ok": True, "command": "codex debug models", "models": models}
259
- return self._model_catalog_cache
260
-
261
-
262
- def maybe_skip_update_prompt(target: str, output: str) -> dict[str, Any] | None:
263
- update_pos = max(output.rfind("Update available!"), output.rfind("Update now"))
264
- ready_pos = max(output.rfind("OpenAI Codex"), output.rfind("›"), output.rfind("codex>"))
265
- if update_pos >= 0 and update_pos > ready_pos:
266
- subprocess.run(["tmux", "send-keys", "-t", target, "Down", "Enter"], check=False)
267
- return {"prompt": "codex_update_available", "action": "sent_skip"}
268
- return None
269
-
270
-
271
- def find_codex_rollout(
272
- root: Path,
273
- cwd: Path,
274
- spawn_time: datetime,
275
- exclude_session_ids: set[str] | None = None,
276
- ) -> dict[str, Any] | None:
277
- if not root.exists():
278
- return None
279
- exclude_session_ids = exclude_session_ids or set()
280
- lower_bound = spawn_time - timedelta(seconds=2)
281
- upper_bound = datetime.now(timezone.utc) + timedelta(seconds=5)
282
- candidates: list[dict[str, Any]] = []
283
- for path in sorted(root.glob("**/rollout-*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True)[:1500]:
284
- meta = read_codex_session_meta(path)
285
- if not meta:
286
- continue
287
- meta_cwd = meta.get("cwd")
288
- if not meta_cwd:
289
- continue
290
- try:
291
- same_cwd = Path(str(meta_cwd)).resolve() == cwd.resolve()
292
- except OSError:
293
- same_cwd = str(meta_cwd) == str(cwd)
294
- if not same_cwd:
295
- continue
296
- ts = parse_time(meta.get("timestamp"))
297
- if ts and (ts < lower_bound or ts > upper_bound):
298
- continue
299
- originator = meta.get("originator")
300
- origin_ok = originator in {"codex-tui", "codex_exec"}
301
- session_id = meta.get("id") or rollout_id_from_name(path)
302
- if not session_id:
303
- continue
304
- if str(session_id) in exclude_session_ids:
305
- continue
306
- candidates.append(
307
- {
308
- "session_id": str(session_id),
309
- "rollout_path": str(path),
310
- "timestamp": ts or datetime.fromtimestamp(path.stat().st_mtime, timezone.utc),
311
- "confidence": "high" if origin_ok and ts else "medium",
312
- }
313
- )
314
- if not candidates:
315
- return None
316
- candidates.sort(key=lambda item: item["timestamp"])
317
- return candidates[0]
318
-
319
-
320
- def read_codex_session_meta(path: Path) -> dict[str, Any] | None:
321
- try:
322
- with path.open(encoding="utf-8") as handle:
323
- first = handle.readline()
324
- data = json.loads(first)
325
- except (OSError, json.JSONDecodeError):
326
- return None
327
- if "session_meta" in data:
328
- payload = data.get("session_meta", {}).get("payload")
329
- else:
330
- payload = data.get("payload")
331
- return payload if isinstance(payload, dict) else None
332
-
333
-
334
- def rollout_id_from_name(path: Path) -> str | None:
335
- match = re.search(r"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\.jsonl$", path.name)
336
- return match.group(1) if match else None
@@ -1,8 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from team_agent.provider_cli.unsupported import UnsupportedCliPlug
4
-
5
-
6
- class CopilotCliPlug(UnsupportedCliPlug):
7
- provider = "copilot"
8
- command_name = "copilot"
@@ -1,39 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import sys
4
- from pathlib import Path
5
- from typing import Any
6
-
7
- from team_agent.provider_cli.adapter import ProviderAdapter
8
-
9
-
10
- class FakeAdapter(ProviderAdapter):
11
- provider = "fake"
12
- command_name = sys.executable
13
-
14
- def build_command(self, agent: dict[str, Any], workspace: Path, mcp_config: dict[str, Any]) -> list[str]:
15
- return [
16
- sys.executable,
17
- "-m",
18
- "team_agent.fake_worker",
19
- "--workspace",
20
- str(workspace),
21
- "--agent-id",
22
- agent["id"],
23
- ]
24
-
25
- def build_resume_command(
26
- self,
27
- agent_state: dict[str, Any],
28
- workspace: Path,
29
- mcp_config: dict[str, Any] | None = None,
30
- ) -> list[str]:
31
- agent = dict(agent_state.get("_agent_spec") or agent_state)
32
- agent.setdefault("id", agent_state.get("agent_id") or agent_state.get("id"))
33
- return self.build_command(agent, workspace, mcp_config or {})
34
-
35
- def auth_hint(self) -> dict[str, Any]:
36
- return {"status": "present", "detail": "fake provider is local test worker"}
37
-
38
- def status_patterns(self) -> dict[str, str]:
39
- return {"idle": "TEAM_AGENT_FAKE_READY", "processing": "TEAM_AGENT_FAKE_WORKING", "error": "Traceback"}
@@ -1,95 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- from pathlib import Path
5
- from typing import Any
6
-
7
- from team_agent.provider_cli.adapter import (
8
- ProviderAdapter,
9
- agent_model,
10
- read_json_object,
11
- )
12
- from team_agent.provider_cli.prompt import compile_system_prompt
13
-
14
-
15
- class GeminiCliAdapter(ProviderAdapter):
16
- provider = "gemini_cli"
17
- command_name = "gemini"
18
-
19
- def build_command(self, agent: dict[str, Any], workspace: Path, mcp_config: dict[str, Any]) -> list[str]:
20
- prompt = compile_system_prompt(agent)
21
- cmd = ["gemini"]
22
- if agent.get("_runtime", {}).get("dangerous_auto_approve"):
23
- cmd.extend(["--yolo", "--sandbox", "false"])
24
- model = agent_model(agent)
25
- if model:
26
- cmd.extend(["--model", model])
27
- if prompt:
28
- cmd.extend(["-i", prompt])
29
- return cmd
30
-
31
- def install_mcp(self, workspace: Path, agent_id: str, config: dict[str, Any]) -> Path:
32
- path = super().install_mcp(workspace, agent_id, config)
33
- self._register_mcp_servers(path, config)
34
- return path
35
-
36
- def cleanup_mcp(self, workspace: Path, agent_id: str, mcp_path: Path | None = None) -> None:
37
- path = mcp_path or workspace / ".team" / "runtime" / "mcp" / f"{agent_id}.json"
38
- self._restore_mcp_servers(path)
39
-
40
- def _register_mcp_servers(self, mcp_path: Path, config: dict[str, Any]) -> None:
41
- settings_path = Path.home() / ".gemini" / "settings.json"
42
- settings_path.parent.mkdir(parents=True, exist_ok=True)
43
- settings = read_json_object(settings_path)
44
- mcp_servers = settings.setdefault("mcpServers", {})
45
- if not isinstance(mcp_servers, dict):
46
- raise ValueError(f"{settings_path}: mcpServers must be an object")
47
-
48
- backup = {
49
- "settings_path": str(settings_path),
50
- "servers": {name: mcp_servers.get(name) for name in config},
51
- }
52
- gemini_backup_path(mcp_path).write_text(json.dumps(backup, indent=2), encoding="utf-8")
53
-
54
- for name, server in config.items():
55
- mcp_servers[name] = {
56
- "command": server["command"],
57
- "args": server.get("args", []),
58
- "env": server.get("env", {}),
59
- }
60
- settings_path.write_text(json.dumps(settings, indent=2), encoding="utf-8")
61
-
62
- def _restore_mcp_servers(self, mcp_path: Path) -> None:
63
- backup_path = gemini_backup_path(mcp_path)
64
- if not backup_path.exists():
65
- return
66
- backup = json.loads(backup_path.read_text(encoding="utf-8"))
67
- settings_path = Path(backup["settings_path"])
68
- settings = read_json_object(settings_path)
69
- mcp_servers = settings.setdefault("mcpServers", {})
70
- if not isinstance(mcp_servers, dict):
71
- raise ValueError(f"{settings_path}: mcpServers must be an object")
72
- for name, previous in backup.get("servers", {}).items():
73
- if previous is None:
74
- mcp_servers.pop(name, None)
75
- else:
76
- mcp_servers[name] = previous
77
- settings_path.write_text(json.dumps(settings, indent=2), encoding="utf-8")
78
- backup_path.unlink(missing_ok=True)
79
-
80
- def auth_hint(self) -> dict[str, Any]:
81
- if "GEMINI_API_KEY" in __import__("os").environ:
82
- return {"status": "present", "detail": "GEMINI_API_KEY is set"}
83
- if Path.home().joinpath(".gemini").exists():
84
- return {"status": "present", "detail": "~/.gemini exists; run gemini to verify OAuth"}
85
- return {"status": "missing_or_unknown", "detail": "run gemini OAuth setup or set GEMINI_API_KEY"}
86
-
87
- def status_patterns(self) -> dict[str, str]:
88
- return {"idle": r"\*\s+Type your message", "processing": r"\(esc to cancel", "error": "Error|APIError|Traceback"}
89
-
90
- def exit_text(self) -> str:
91
- return "\x04"
92
-
93
-
94
- def gemini_backup_path(mcp_path: Path) -> Path:
95
- return mcp_path.with_suffix(".gemini-backup.json")
@@ -1,8 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from team_agent.provider_cli.unsupported import UnsupportedCliPlug
4
-
5
-
6
- class OpenCodeCliPlug(UnsupportedCliPlug):
7
- provider = "opencode"
8
- command_name = "opencode"
@@ -1,62 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- from typing import Any
5
-
6
- from team_agent.permissions import resolve_permissions
7
-
8
-
9
- TEAMMATE_SYSTEM_PROMPT = """# Team Agent Teammate Runtime Contract
10
-
11
- You are a teammate in a Team Agent runtime, not the user's primary assistant.
12
- The user normally talks to the team lead. Plain text you write in this worker
13
- session is local to this session and is not a team message.
14
-
15
- Use Team Agent MCP tools for team-visible coordination:
16
- - Send progress, blockers, permission needs, tool failures, scope changes, and
17
- long-running status updates with team_orchestrator.send_message(to='leader',
18
- content='<short message>').
19
- - Send to another teammate by agent id when coordination is useful, or use
20
- to='*' to notify every other team member. The runtime resolves only this team
21
- and excludes your own worker.
22
- - When the task is complete, call team_orchestrator.report_result exactly once.
23
- - Do not pass sender, task_id, agent_id, schema_version, or ack fields unless
24
- doing a low-level compatibility diagnostic. The MCP runtime fills protocol
25
- fields from the current worker and task state.
26
-
27
- If you are blocked or cannot continue, message the leader promptly instead of
28
- waiting silently. If work takes several minutes, send a short progress update.
29
- """
30
-
31
-
32
- def compile_system_prompt(agent: dict[str, Any]) -> str:
33
- prompt_cfg = agent.get("system_prompt", {})
34
- identity = (
35
- f"You are Team Agent worker `{agent.get('id')}` with role `{agent.get('role')}`. "
36
- "When asked about your role or identity, answer with this Team Agent worker identity first, "
37
- "not only the generic provider product identity."
38
- )
39
- chunks: list[str] = [identity, TEAMMATE_SYSTEM_PROMPT]
40
- if prompt_cfg.get("inline"):
41
- chunks.append(str(prompt_cfg["inline"]))
42
- if prompt_cfg.get("file"):
43
- chunks.append(Path(prompt_cfg["file"]).read_text(encoding="utf-8"))
44
- contract = agent.get("output_contract", {})
45
- if contract.get("format") == "result_envelope_v1":
46
- chunks.append(
47
- "For progress or blockers, call team_orchestrator.send_message(to='leader', content='<short message>'); "
48
- "for teammate coordination, send to another agent id or to='*' for every other team member. "
49
- "do not pass sender, task_id, or requires_ack because the MCP runtime fills protocol fields. "
50
- "the runtime injects it into the attached Codex leader pane when the leader has run attach-leader. "
51
- "If no leader is attached, the tool returns a fallback/failed result instead of completion. "
52
- "Final completion must call team_orchestrator.report_result exactly once with a short summary "
53
- "and optional status/changes/tests; MCP fills schema_version, task_id, and agent_id."
54
- )
55
- perms = resolve_permissions(agent)
56
- if perms["has_prompt_only"]:
57
- prompt_only = [e["tool"] for e in perms["resolved_tools"] if e["enforcement"] == "prompt_only"]
58
- chunks.append(
59
- "Permission note: these tools are prompt-only for this provider and not hard-enforced: "
60
- + ", ".join(prompt_only)
61
- )
62
- return "\n\n".join(chunk for chunk in chunks if chunk)
@@ -1,18 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from team_agent.provider_cli.base import ProviderCliSocket
4
- from team_agent.provider_cli.copilot import CopilotCliPlug
5
- from team_agent.provider_cli.opencode import OpenCodeCliPlug
6
-
7
-
8
- PLUG_TYPES = {
9
- "opencode": OpenCodeCliPlug,
10
- "copilot": CopilotCliPlug,
11
- }
12
-
13
-
14
- def build_plug(provider: str) -> ProviderCliSocket:
15
- try:
16
- return PLUG_TYPES[provider]()
17
- except KeyError as exc:
18
- raise KeyError(f"Unsupported provider plug: {provider}") from exc
@@ -1,32 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- from team_agent.provider_cli.base import ProviderCapabilityError, ProviderStartupInput
6
-
7
-
8
- class UnsupportedCliPlug:
9
- provider = ""
10
- command_name = ""
11
-
12
- def build_command(self, startup: ProviderStartupInput) -> list[str]:
13
- _ = startup
14
- self._unsupported("start")
15
-
16
- def build_resume_command(self, startup: ProviderStartupInput) -> list[str]:
17
- _ = startup
18
- self._unsupported("resume")
19
-
20
- def build_fork_command(self, startup: ProviderStartupInput, source_session_id: str) -> list[str]:
21
- _ = startup, source_session_id
22
- self._unsupported("fork_or_branch")
23
-
24
- def capture_session_id(self, startup: ProviderStartupInput, timeout_s: float = 3.0) -> dict[str, Any] | None:
25
- _ = startup, timeout_s
26
- return None
27
-
28
- def cleanup(self, startup: ProviderStartupInput) -> None:
29
- _ = startup
30
-
31
- def _unsupported(self, capability: str) -> Any:
32
- raise ProviderCapabilityError(self.provider, capability, "provider plug exists but is not implemented yet")