agentpool 2.1.9__py3-none-any.whl

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.

Potentially problematic release.


This version of agentpool might be problematic. Click here for more details.

Files changed (474) hide show
  1. acp/README.md +64 -0
  2. acp/__init__.py +172 -0
  3. acp/__main__.py +10 -0
  4. acp/acp_requests.py +285 -0
  5. acp/agent/__init__.py +6 -0
  6. acp/agent/connection.py +256 -0
  7. acp/agent/implementations/__init__.py +6 -0
  8. acp/agent/implementations/debug_server/__init__.py +1 -0
  9. acp/agent/implementations/debug_server/cli.py +79 -0
  10. acp/agent/implementations/debug_server/debug.html +234 -0
  11. acp/agent/implementations/debug_server/debug_server.py +496 -0
  12. acp/agent/implementations/testing.py +91 -0
  13. acp/agent/protocol.py +65 -0
  14. acp/bridge/README.md +162 -0
  15. acp/bridge/__init__.py +6 -0
  16. acp/bridge/__main__.py +91 -0
  17. acp/bridge/bridge.py +246 -0
  18. acp/bridge/py.typed +0 -0
  19. acp/bridge/settings.py +15 -0
  20. acp/client/__init__.py +7 -0
  21. acp/client/connection.py +251 -0
  22. acp/client/implementations/__init__.py +7 -0
  23. acp/client/implementations/default_client.py +185 -0
  24. acp/client/implementations/headless_client.py +266 -0
  25. acp/client/implementations/noop_client.py +110 -0
  26. acp/client/protocol.py +61 -0
  27. acp/connection.py +280 -0
  28. acp/exceptions.py +46 -0
  29. acp/filesystem.py +524 -0
  30. acp/notifications.py +832 -0
  31. acp/py.typed +0 -0
  32. acp/schema/__init__.py +265 -0
  33. acp/schema/agent_plan.py +30 -0
  34. acp/schema/agent_requests.py +126 -0
  35. acp/schema/agent_responses.py +256 -0
  36. acp/schema/base.py +39 -0
  37. acp/schema/capabilities.py +230 -0
  38. acp/schema/client_requests.py +247 -0
  39. acp/schema/client_responses.py +96 -0
  40. acp/schema/common.py +81 -0
  41. acp/schema/content_blocks.py +188 -0
  42. acp/schema/mcp.py +82 -0
  43. acp/schema/messages.py +171 -0
  44. acp/schema/notifications.py +82 -0
  45. acp/schema/protocol_stuff.md +3 -0
  46. acp/schema/session_state.py +160 -0
  47. acp/schema/session_updates.py +419 -0
  48. acp/schema/slash_commands.py +51 -0
  49. acp/schema/terminal.py +15 -0
  50. acp/schema/tool_call.py +347 -0
  51. acp/stdio.py +250 -0
  52. acp/task/__init__.py +53 -0
  53. acp/task/debug.py +197 -0
  54. acp/task/dispatcher.py +93 -0
  55. acp/task/queue.py +69 -0
  56. acp/task/sender.py +82 -0
  57. acp/task/state.py +87 -0
  58. acp/task/supervisor.py +93 -0
  59. acp/terminal_handle.py +30 -0
  60. acp/tool_call_reporter.py +199 -0
  61. acp/tool_call_state.py +178 -0
  62. acp/transports.py +104 -0
  63. acp/utils.py +240 -0
  64. agentpool/__init__.py +63 -0
  65. agentpool/__main__.py +7 -0
  66. agentpool/agents/__init__.py +30 -0
  67. agentpool/agents/acp_agent/__init__.py +5 -0
  68. agentpool/agents/acp_agent/acp_agent.py +837 -0
  69. agentpool/agents/acp_agent/acp_converters.py +294 -0
  70. agentpool/agents/acp_agent/client_handler.py +317 -0
  71. agentpool/agents/acp_agent/session_state.py +44 -0
  72. agentpool/agents/agent.py +1264 -0
  73. agentpool/agents/agui_agent/__init__.py +19 -0
  74. agentpool/agents/agui_agent/agui_agent.py +677 -0
  75. agentpool/agents/agui_agent/agui_converters.py +423 -0
  76. agentpool/agents/agui_agent/chunk_transformer.py +204 -0
  77. agentpool/agents/agui_agent/event_types.py +83 -0
  78. agentpool/agents/agui_agent/helpers.py +192 -0
  79. agentpool/agents/architect.py +71 -0
  80. agentpool/agents/base_agent.py +177 -0
  81. agentpool/agents/claude_code_agent/__init__.py +11 -0
  82. agentpool/agents/claude_code_agent/claude_code_agent.py +1021 -0
  83. agentpool/agents/claude_code_agent/converters.py +243 -0
  84. agentpool/agents/context.py +105 -0
  85. agentpool/agents/events/__init__.py +61 -0
  86. agentpool/agents/events/builtin_handlers.py +129 -0
  87. agentpool/agents/events/event_emitter.py +320 -0
  88. agentpool/agents/events/events.py +561 -0
  89. agentpool/agents/events/tts_handlers.py +186 -0
  90. agentpool/agents/interactions.py +419 -0
  91. agentpool/agents/slashed_agent.py +244 -0
  92. agentpool/agents/sys_prompts.py +178 -0
  93. agentpool/agents/tool_wrapping.py +184 -0
  94. agentpool/base_provider.py +28 -0
  95. agentpool/common_types.py +226 -0
  96. agentpool/config_resources/__init__.py +16 -0
  97. agentpool/config_resources/acp_assistant.yml +24 -0
  98. agentpool/config_resources/agents.yml +109 -0
  99. agentpool/config_resources/agents_template.yml +18 -0
  100. agentpool/config_resources/agui_test.yml +18 -0
  101. agentpool/config_resources/claude_code_agent.yml +16 -0
  102. agentpool/config_resources/claude_style_subagent.md +30 -0
  103. agentpool/config_resources/external_acp_agents.yml +77 -0
  104. agentpool/config_resources/opencode_style_subagent.md +19 -0
  105. agentpool/config_resources/tts_test_agents.yml +78 -0
  106. agentpool/delegation/__init__.py +8 -0
  107. agentpool/delegation/base_team.py +504 -0
  108. agentpool/delegation/message_flow_tracker.py +39 -0
  109. agentpool/delegation/pool.py +1129 -0
  110. agentpool/delegation/team.py +325 -0
  111. agentpool/delegation/teamrun.py +343 -0
  112. agentpool/docs/__init__.py +5 -0
  113. agentpool/docs/gen_examples.py +42 -0
  114. agentpool/docs/utils.py +370 -0
  115. agentpool/functional/__init__.py +20 -0
  116. agentpool/functional/py.typed +0 -0
  117. agentpool/functional/run.py +80 -0
  118. agentpool/functional/structure.py +136 -0
  119. agentpool/hooks/__init__.py +20 -0
  120. agentpool/hooks/agent_hooks.py +247 -0
  121. agentpool/hooks/base.py +119 -0
  122. agentpool/hooks/callable.py +140 -0
  123. agentpool/hooks/command.py +180 -0
  124. agentpool/hooks/prompt.py +122 -0
  125. agentpool/jinja_filters.py +132 -0
  126. agentpool/log.py +224 -0
  127. agentpool/mcp_server/__init__.py +17 -0
  128. agentpool/mcp_server/client.py +429 -0
  129. agentpool/mcp_server/constants.py +32 -0
  130. agentpool/mcp_server/conversions.py +172 -0
  131. agentpool/mcp_server/helpers.py +47 -0
  132. agentpool/mcp_server/manager.py +232 -0
  133. agentpool/mcp_server/message_handler.py +164 -0
  134. agentpool/mcp_server/registries/__init__.py +1 -0
  135. agentpool/mcp_server/registries/official_registry_client.py +345 -0
  136. agentpool/mcp_server/registries/pulsemcp_client.py +88 -0
  137. agentpool/mcp_server/tool_bridge.py +548 -0
  138. agentpool/messaging/__init__.py +58 -0
  139. agentpool/messaging/compaction.py +928 -0
  140. agentpool/messaging/connection_manager.py +319 -0
  141. agentpool/messaging/context.py +66 -0
  142. agentpool/messaging/event_manager.py +426 -0
  143. agentpool/messaging/events.py +39 -0
  144. agentpool/messaging/message_container.py +209 -0
  145. agentpool/messaging/message_history.py +491 -0
  146. agentpool/messaging/messagenode.py +377 -0
  147. agentpool/messaging/messages.py +655 -0
  148. agentpool/messaging/processing.py +76 -0
  149. agentpool/mime_utils.py +95 -0
  150. agentpool/models/__init__.py +21 -0
  151. agentpool/models/acp_agents/__init__.py +22 -0
  152. agentpool/models/acp_agents/base.py +308 -0
  153. agentpool/models/acp_agents/mcp_capable.py +790 -0
  154. agentpool/models/acp_agents/non_mcp.py +842 -0
  155. agentpool/models/agents.py +450 -0
  156. agentpool/models/agui_agents.py +89 -0
  157. agentpool/models/claude_code_agents.py +238 -0
  158. agentpool/models/file_agents.py +116 -0
  159. agentpool/models/file_parsing.py +367 -0
  160. agentpool/models/manifest.py +658 -0
  161. agentpool/observability/__init__.py +9 -0
  162. agentpool/observability/observability_registry.py +97 -0
  163. agentpool/prompts/__init__.py +1 -0
  164. agentpool/prompts/base.py +27 -0
  165. agentpool/prompts/builtin_provider.py +75 -0
  166. agentpool/prompts/conversion_manager.py +95 -0
  167. agentpool/prompts/convert.py +96 -0
  168. agentpool/prompts/manager.py +204 -0
  169. agentpool/prompts/parts/zed.md +33 -0
  170. agentpool/prompts/prompts.py +581 -0
  171. agentpool/py.typed +0 -0
  172. agentpool/queries/tree-sitter-language-pack/README.md +7 -0
  173. agentpool/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  174. agentpool/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  175. agentpool/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  176. agentpool/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
  177. agentpool/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  178. agentpool/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  179. agentpool/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  180. agentpool/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  181. agentpool/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  182. agentpool/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  183. agentpool/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  184. agentpool/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  185. agentpool/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  186. agentpool/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  187. agentpool/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  188. agentpool/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  189. agentpool/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  190. agentpool/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
  191. agentpool/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  192. agentpool/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  193. agentpool/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  194. agentpool/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  195. agentpool/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  196. agentpool/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  197. agentpool/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  198. agentpool/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  199. agentpool/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  200. agentpool/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  201. agentpool/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  202. agentpool/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  203. agentpool/queries/tree-sitter-languages/README.md +24 -0
  204. agentpool/queries/tree-sitter-languages/c-tags.scm +9 -0
  205. agentpool/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  206. agentpool/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  207. agentpool/queries/tree-sitter-languages/dart-tags.scm +91 -0
  208. agentpool/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  209. agentpool/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  210. agentpool/queries/tree-sitter-languages/elm-tags.scm +19 -0
  211. agentpool/queries/tree-sitter-languages/fortran-tags.scm +15 -0
  212. agentpool/queries/tree-sitter-languages/go-tags.scm +30 -0
  213. agentpool/queries/tree-sitter-languages/haskell-tags.scm +3 -0
  214. agentpool/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  215. agentpool/queries/tree-sitter-languages/java-tags.scm +20 -0
  216. agentpool/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  217. agentpool/queries/tree-sitter-languages/julia-tags.scm +60 -0
  218. agentpool/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  219. agentpool/queries/tree-sitter-languages/matlab-tags.scm +10 -0
  220. agentpool/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  221. agentpool/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  222. agentpool/queries/tree-sitter-languages/php-tags.scm +26 -0
  223. agentpool/queries/tree-sitter-languages/python-tags.scm +12 -0
  224. agentpool/queries/tree-sitter-languages/ql-tags.scm +26 -0
  225. agentpool/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  226. agentpool/queries/tree-sitter-languages/rust-tags.scm +60 -0
  227. agentpool/queries/tree-sitter-languages/scala-tags.scm +65 -0
  228. agentpool/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  229. agentpool/queries/tree-sitter-languages/zig-tags.scm +3 -0
  230. agentpool/repomap.py +1231 -0
  231. agentpool/resource_providers/__init__.py +17 -0
  232. agentpool/resource_providers/aggregating.py +54 -0
  233. agentpool/resource_providers/base.py +172 -0
  234. agentpool/resource_providers/codemode/__init__.py +9 -0
  235. agentpool/resource_providers/codemode/code_executor.py +215 -0
  236. agentpool/resource_providers/codemode/default_prompt.py +19 -0
  237. agentpool/resource_providers/codemode/helpers.py +83 -0
  238. agentpool/resource_providers/codemode/progress_executor.py +212 -0
  239. agentpool/resource_providers/codemode/provider.py +150 -0
  240. agentpool/resource_providers/codemode/remote_mcp_execution.py +143 -0
  241. agentpool/resource_providers/codemode/remote_provider.py +171 -0
  242. agentpool/resource_providers/filtering.py +42 -0
  243. agentpool/resource_providers/mcp_provider.py +246 -0
  244. agentpool/resource_providers/plan_provider.py +196 -0
  245. agentpool/resource_providers/pool.py +69 -0
  246. agentpool/resource_providers/static.py +289 -0
  247. agentpool/running/__init__.py +20 -0
  248. agentpool/running/decorators.py +56 -0
  249. agentpool/running/discovery.py +101 -0
  250. agentpool/running/executor.py +284 -0
  251. agentpool/running/injection.py +111 -0
  252. agentpool/running/py.typed +0 -0
  253. agentpool/running/run_nodes.py +87 -0
  254. agentpool/server.py +122 -0
  255. agentpool/sessions/__init__.py +13 -0
  256. agentpool/sessions/manager.py +302 -0
  257. agentpool/sessions/models.py +71 -0
  258. agentpool/sessions/session.py +239 -0
  259. agentpool/sessions/store.py +163 -0
  260. agentpool/skills/__init__.py +5 -0
  261. agentpool/skills/manager.py +120 -0
  262. agentpool/skills/registry.py +210 -0
  263. agentpool/skills/skill.py +36 -0
  264. agentpool/storage/__init__.py +17 -0
  265. agentpool/storage/manager.py +419 -0
  266. agentpool/storage/serialization.py +136 -0
  267. agentpool/talk/__init__.py +13 -0
  268. agentpool/talk/registry.py +128 -0
  269. agentpool/talk/stats.py +159 -0
  270. agentpool/talk/talk.py +604 -0
  271. agentpool/tasks/__init__.py +20 -0
  272. agentpool/tasks/exceptions.py +25 -0
  273. agentpool/tasks/registry.py +33 -0
  274. agentpool/testing.py +129 -0
  275. agentpool/text_templates/__init__.py +39 -0
  276. agentpool/text_templates/system_prompt.jinja +30 -0
  277. agentpool/text_templates/tool_call_default.jinja +13 -0
  278. agentpool/text_templates/tool_call_markdown.jinja +25 -0
  279. agentpool/text_templates/tool_call_simple.jinja +5 -0
  280. agentpool/tools/__init__.py +16 -0
  281. agentpool/tools/base.py +269 -0
  282. agentpool/tools/exceptions.py +9 -0
  283. agentpool/tools/manager.py +255 -0
  284. agentpool/tools/tool_call_info.py +87 -0
  285. agentpool/ui/__init__.py +2 -0
  286. agentpool/ui/base.py +89 -0
  287. agentpool/ui/mock_provider.py +81 -0
  288. agentpool/ui/stdlib_provider.py +150 -0
  289. agentpool/utils/__init__.py +44 -0
  290. agentpool/utils/baseregistry.py +185 -0
  291. agentpool/utils/count_tokens.py +62 -0
  292. agentpool/utils/dag.py +184 -0
  293. agentpool/utils/importing.py +206 -0
  294. agentpool/utils/inspection.py +334 -0
  295. agentpool/utils/model_capabilities.py +25 -0
  296. agentpool/utils/network.py +28 -0
  297. agentpool/utils/now.py +22 -0
  298. agentpool/utils/parse_time.py +87 -0
  299. agentpool/utils/result_utils.py +35 -0
  300. agentpool/utils/signatures.py +305 -0
  301. agentpool/utils/streams.py +112 -0
  302. agentpool/utils/tasks.py +186 -0
  303. agentpool/vfs_registry.py +250 -0
  304. agentpool-2.1.9.dist-info/METADATA +336 -0
  305. agentpool-2.1.9.dist-info/RECORD +474 -0
  306. agentpool-2.1.9.dist-info/WHEEL +4 -0
  307. agentpool-2.1.9.dist-info/entry_points.txt +14 -0
  308. agentpool-2.1.9.dist-info/licenses/LICENSE +22 -0
  309. agentpool_cli/__init__.py +34 -0
  310. agentpool_cli/__main__.py +66 -0
  311. agentpool_cli/agent.py +175 -0
  312. agentpool_cli/cli_types.py +23 -0
  313. agentpool_cli/common.py +163 -0
  314. agentpool_cli/create.py +175 -0
  315. agentpool_cli/history.py +217 -0
  316. agentpool_cli/log.py +78 -0
  317. agentpool_cli/py.typed +0 -0
  318. agentpool_cli/run.py +84 -0
  319. agentpool_cli/serve_acp.py +177 -0
  320. agentpool_cli/serve_api.py +69 -0
  321. agentpool_cli/serve_mcp.py +74 -0
  322. agentpool_cli/serve_vercel.py +233 -0
  323. agentpool_cli/store.py +171 -0
  324. agentpool_cli/task.py +84 -0
  325. agentpool_cli/utils.py +104 -0
  326. agentpool_cli/watch.py +54 -0
  327. agentpool_commands/__init__.py +180 -0
  328. agentpool_commands/agents.py +199 -0
  329. agentpool_commands/base.py +45 -0
  330. agentpool_commands/commands.py +58 -0
  331. agentpool_commands/completers.py +110 -0
  332. agentpool_commands/connections.py +175 -0
  333. agentpool_commands/markdown_utils.py +31 -0
  334. agentpool_commands/models.py +62 -0
  335. agentpool_commands/prompts.py +78 -0
  336. agentpool_commands/py.typed +0 -0
  337. agentpool_commands/read.py +77 -0
  338. agentpool_commands/resources.py +210 -0
  339. agentpool_commands/session.py +48 -0
  340. agentpool_commands/tools.py +269 -0
  341. agentpool_commands/utils.py +189 -0
  342. agentpool_commands/workers.py +163 -0
  343. agentpool_config/__init__.py +53 -0
  344. agentpool_config/builtin_tools.py +265 -0
  345. agentpool_config/commands.py +237 -0
  346. agentpool_config/conditions.py +301 -0
  347. agentpool_config/converters.py +30 -0
  348. agentpool_config/durable.py +331 -0
  349. agentpool_config/event_handlers.py +600 -0
  350. agentpool_config/events.py +153 -0
  351. agentpool_config/forward_targets.py +251 -0
  352. agentpool_config/hook_conditions.py +331 -0
  353. agentpool_config/hooks.py +241 -0
  354. agentpool_config/jinja.py +206 -0
  355. agentpool_config/knowledge.py +41 -0
  356. agentpool_config/loaders.py +350 -0
  357. agentpool_config/mcp_server.py +243 -0
  358. agentpool_config/nodes.py +202 -0
  359. agentpool_config/observability.py +191 -0
  360. agentpool_config/output_types.py +55 -0
  361. agentpool_config/pool_server.py +267 -0
  362. agentpool_config/prompt_hubs.py +105 -0
  363. agentpool_config/prompts.py +185 -0
  364. agentpool_config/py.typed +0 -0
  365. agentpool_config/resources.py +33 -0
  366. agentpool_config/session.py +119 -0
  367. agentpool_config/skills.py +17 -0
  368. agentpool_config/storage.py +288 -0
  369. agentpool_config/system_prompts.py +190 -0
  370. agentpool_config/task.py +162 -0
  371. agentpool_config/teams.py +52 -0
  372. agentpool_config/tools.py +112 -0
  373. agentpool_config/toolsets.py +1033 -0
  374. agentpool_config/workers.py +86 -0
  375. agentpool_prompts/__init__.py +1 -0
  376. agentpool_prompts/braintrust_hub.py +235 -0
  377. agentpool_prompts/fabric.py +75 -0
  378. agentpool_prompts/langfuse_hub.py +79 -0
  379. agentpool_prompts/promptlayer_provider.py +59 -0
  380. agentpool_prompts/py.typed +0 -0
  381. agentpool_server/__init__.py +9 -0
  382. agentpool_server/a2a_server/__init__.py +5 -0
  383. agentpool_server/a2a_server/a2a_types.py +41 -0
  384. agentpool_server/a2a_server/server.py +190 -0
  385. agentpool_server/a2a_server/storage.py +81 -0
  386. agentpool_server/acp_server/__init__.py +22 -0
  387. agentpool_server/acp_server/acp_agent.py +786 -0
  388. agentpool_server/acp_server/acp_tools.py +43 -0
  389. agentpool_server/acp_server/commands/__init__.py +18 -0
  390. agentpool_server/acp_server/commands/acp_commands.py +594 -0
  391. agentpool_server/acp_server/commands/debug_commands.py +376 -0
  392. agentpool_server/acp_server/commands/docs_commands/__init__.py +39 -0
  393. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +169 -0
  394. agentpool_server/acp_server/commands/docs_commands/get_schema.py +176 -0
  395. agentpool_server/acp_server/commands/docs_commands/get_source.py +110 -0
  396. agentpool_server/acp_server/commands/docs_commands/git_diff.py +111 -0
  397. agentpool_server/acp_server/commands/docs_commands/helpers.py +33 -0
  398. agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py +90 -0
  399. agentpool_server/acp_server/commands/spawn.py +210 -0
  400. agentpool_server/acp_server/converters.py +235 -0
  401. agentpool_server/acp_server/input_provider.py +338 -0
  402. agentpool_server/acp_server/server.py +288 -0
  403. agentpool_server/acp_server/session.py +969 -0
  404. agentpool_server/acp_server/session_manager.py +313 -0
  405. agentpool_server/acp_server/syntax_detection.py +250 -0
  406. agentpool_server/acp_server/zed_tools.md +90 -0
  407. agentpool_server/aggregating_server.py +309 -0
  408. agentpool_server/agui_server/__init__.py +11 -0
  409. agentpool_server/agui_server/server.py +128 -0
  410. agentpool_server/base.py +189 -0
  411. agentpool_server/http_server.py +164 -0
  412. agentpool_server/mcp_server/__init__.py +6 -0
  413. agentpool_server/mcp_server/server.py +314 -0
  414. agentpool_server/mcp_server/zed_wrapper.py +110 -0
  415. agentpool_server/openai_api_server/__init__.py +5 -0
  416. agentpool_server/openai_api_server/completions/__init__.py +1 -0
  417. agentpool_server/openai_api_server/completions/helpers.py +81 -0
  418. agentpool_server/openai_api_server/completions/models.py +98 -0
  419. agentpool_server/openai_api_server/responses/__init__.py +1 -0
  420. agentpool_server/openai_api_server/responses/helpers.py +74 -0
  421. agentpool_server/openai_api_server/responses/models.py +96 -0
  422. agentpool_server/openai_api_server/server.py +242 -0
  423. agentpool_server/py.typed +0 -0
  424. agentpool_storage/__init__.py +9 -0
  425. agentpool_storage/base.py +310 -0
  426. agentpool_storage/file_provider.py +378 -0
  427. agentpool_storage/formatters.py +129 -0
  428. agentpool_storage/memory_provider.py +396 -0
  429. agentpool_storage/models.py +108 -0
  430. agentpool_storage/py.typed +0 -0
  431. agentpool_storage/session_store.py +262 -0
  432. agentpool_storage/sql_provider/__init__.py +21 -0
  433. agentpool_storage/sql_provider/cli.py +146 -0
  434. agentpool_storage/sql_provider/models.py +249 -0
  435. agentpool_storage/sql_provider/queries.py +15 -0
  436. agentpool_storage/sql_provider/sql_provider.py +444 -0
  437. agentpool_storage/sql_provider/utils.py +234 -0
  438. agentpool_storage/text_log_provider.py +275 -0
  439. agentpool_toolsets/__init__.py +15 -0
  440. agentpool_toolsets/builtin/__init__.py +33 -0
  441. agentpool_toolsets/builtin/agent_management.py +239 -0
  442. agentpool_toolsets/builtin/chain.py +288 -0
  443. agentpool_toolsets/builtin/code.py +398 -0
  444. agentpool_toolsets/builtin/debug.py +291 -0
  445. agentpool_toolsets/builtin/execution_environment.py +381 -0
  446. agentpool_toolsets/builtin/file_edit/__init__.py +11 -0
  447. agentpool_toolsets/builtin/file_edit/file_edit.py +747 -0
  448. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/__init__.py +5 -0
  449. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/example_usage.py +311 -0
  450. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/streaming_fuzzy_matcher.py +443 -0
  451. agentpool_toolsets/builtin/history.py +36 -0
  452. agentpool_toolsets/builtin/integration.py +85 -0
  453. agentpool_toolsets/builtin/skills.py +77 -0
  454. agentpool_toolsets/builtin/subagent_tools.py +324 -0
  455. agentpool_toolsets/builtin/tool_management.py +90 -0
  456. agentpool_toolsets/builtin/user_interaction.py +52 -0
  457. agentpool_toolsets/builtin/workers.py +128 -0
  458. agentpool_toolsets/composio_toolset.py +96 -0
  459. agentpool_toolsets/config_creation.py +192 -0
  460. agentpool_toolsets/entry_points.py +47 -0
  461. agentpool_toolsets/fsspec_toolset/__init__.py +7 -0
  462. agentpool_toolsets/fsspec_toolset/diagnostics.py +115 -0
  463. agentpool_toolsets/fsspec_toolset/grep.py +450 -0
  464. agentpool_toolsets/fsspec_toolset/helpers.py +631 -0
  465. agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py +249 -0
  466. agentpool_toolsets/fsspec_toolset/toolset.py +1384 -0
  467. agentpool_toolsets/mcp_run_toolset.py +61 -0
  468. agentpool_toolsets/notifications.py +146 -0
  469. agentpool_toolsets/openapi.py +118 -0
  470. agentpool_toolsets/py.typed +0 -0
  471. agentpool_toolsets/search_toolset.py +202 -0
  472. agentpool_toolsets/semantic_memory_toolset.py +536 -0
  473. agentpool_toolsets/streaming_tools.py +265 -0
  474. agentpool_toolsets/vfs_toolset.py +124 -0
@@ -0,0 +1,338 @@
1
+ """ACP-based input provider for agent interactions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+ import webbrowser
7
+
8
+ from mcp import types
9
+
10
+ from acp import AllowedOutcome, PermissionOption
11
+ from acp.utils import DEFAULT_PERMISSION_OPTIONS
12
+ from agentpool.log import get_logger
13
+ from agentpool.ui.base import InputProvider
14
+
15
+
16
+ if TYPE_CHECKING:
17
+ from acp import RequestPermissionResponse
18
+ from agentpool.agents.context import ConfirmationResult
19
+ from agentpool.messaging import ChatMessage
20
+ from agentpool.messaging.context import NodeContext
21
+ from agentpool.tools.base import Tool
22
+ from agentpool_server.acp_server.session import ACPSession
23
+
24
+ logger = get_logger(__name__)
25
+
26
+
27
+ def _create_enum_elicitation_options(schema: dict[str, Any]) -> list[PermissionOption] | None:
28
+ """Create permission options for enum elicitation.
29
+
30
+ max 4 options (not more ids available)
31
+ """
32
+ enum_values = schema.get("enum", [])
33
+ enum_names = schema.get("enumNames", enum_values) # Use enumNames if available
34
+ # Limit to 3 choices + cancel to keep ACP UI reasonable
35
+ if len(enum_values) > 3: # noqa: PLR2004
36
+ logger.warning("Enum truncated for UI", enum_count=len(enum_values), showing_count=3)
37
+ enum_values = enum_values[:3]
38
+ enum_names = enum_names[:3]
39
+
40
+ if not enum_values:
41
+ return None
42
+
43
+ options = []
44
+ for i, (value, name) in enumerate(zip(enum_values, enum_names, strict=False)):
45
+ opt_id = f"enum_{i}_{value}"
46
+ option = PermissionOption(option_id=opt_id, name=str(name), kind="allow_once")
47
+ options.append(option)
48
+ # Add cancel option
49
+ options.append(PermissionOption(option_id="cancel", name="Cancel", kind="reject_always"))
50
+
51
+ return options
52
+
53
+
54
+ def _is_boolean_schema(schema: dict[str, Any]) -> bool:
55
+ """Check if the elicitation schema is requesting a boolean value."""
56
+ # Direct boolean type
57
+ if schema.get("type") == "boolean":
58
+ return True
59
+
60
+ # Check if it's an object with a single boolean property (common pattern)
61
+ if schema.get("type") == "object":
62
+ properties = schema.get("properties", {})
63
+ if len(properties) == 1:
64
+ prop_schema = next(iter(properties.values()))
65
+ return bool(prop_schema.get("type") == "boolean")
66
+
67
+ return False
68
+
69
+
70
+ def _is_enum_schema(schema: dict[str, Any]) -> bool:
71
+ """Check if the elicitation schema is requesting an enum value."""
72
+ return schema.get("type") == "string" and "enum" in schema
73
+
74
+
75
+ def _create_boolean_elicitation_options() -> list[PermissionOption]:
76
+ """Create permission options for boolean elicitation (Yes/No)."""
77
+ return [
78
+ PermissionOption(option_id="true", name="Yes", kind="allow_once"),
79
+ PermissionOption(option_id="false", name="No", kind="reject_once"),
80
+ PermissionOption(option_id="cancel", name="Cancel", kind="reject_always"),
81
+ ]
82
+
83
+
84
+ class ACPInputProvider(InputProvider):
85
+ """Input provider that uses ACP session for user interactions.
86
+
87
+ This provider enables tool confirmation and elicitation requests
88
+ through the ACP protocol, allowing clients to interact with agents
89
+ for permission requests and additional input.
90
+ """
91
+
92
+ def __init__(self, session: ACPSession) -> None:
93
+ """Initialize ACP input provider.
94
+
95
+ Args:
96
+ session: Active ACP session for handling requests
97
+ """
98
+ self.session = session
99
+ self._tool_approvals: dict[str, str] = {} # tool_name -> "allow_always" | "reject_always"
100
+
101
+ async def get_tool_confirmation(
102
+ self,
103
+ context: NodeContext[Any],
104
+ tool: Tool,
105
+ args: dict[str, Any],
106
+ message_history: list[ChatMessage[Any]] | None = None,
107
+ ) -> ConfirmationResult:
108
+ """Get tool execution confirmation via ACP request permission.
109
+
110
+ Uses the ACP session's request_permission mechanism to ask
111
+ the client for confirmation before executing the tool.
112
+
113
+ Args:
114
+ context: Current node context
115
+ tool: Information about the tool to be executed
116
+ args: Tool arguments that will be passed to the tool
117
+ message_history: Optional conversation history
118
+
119
+ Returns:
120
+ Confirmation result indicating whether to allow, skip, or abort
121
+ """
122
+ try:
123
+ # Check if we have a standing approval/rejection for this tool
124
+ if tool.name in self._tool_approvals:
125
+ standing_decision = self._tool_approvals[tool.name]
126
+ if standing_decision == "allow_always":
127
+ logger.debug("Auto-allowing tool", tool_name=tool.name, reason="allow_always")
128
+ return "allow"
129
+ if standing_decision == "reject_always":
130
+ logger.debug("Auto-rejecting tool", tool_name=tool.name, reason="reject_always")
131
+ return "skip"
132
+
133
+ # Create a descriptive title for the permission request
134
+ args_str = ", ".join(f"{k}={v}" for k, v in args.items())
135
+ response = await self.session.requests.request_permission(
136
+ tool_call_id=f"{tool.name}_{hash(frozenset(args.items()))}",
137
+ title=f"Execute tool {tool.name!r} with args: {args_str}",
138
+ options=DEFAULT_PERMISSION_OPTIONS,
139
+ )
140
+ # Map ACP permission response to our confirmation result
141
+ if isinstance(response.outcome, AllowedOutcome):
142
+ return self._handle_permission_response(response.outcome.option_id, tool.name)
143
+ if response.outcome.outcome == "cancelled":
144
+ return "skip"
145
+ # Handle other outcomes
146
+ logger.warning("Unexpected permission outcome", outcome=response.outcome.outcome)
147
+
148
+ except Exception:
149
+ logger.exception("Failed to get tool confirmation")
150
+ # Default to abort on error to be safe
151
+ return "abort_run"
152
+ else:
153
+ return "abort_run"
154
+
155
+ def _handle_permission_response(self, option_id: str, tool_name: str) -> ConfirmationResult:
156
+ """Handle permission response and update tool approval state."""
157
+ match option_id:
158
+ case "allow-once":
159
+ return "allow"
160
+ case "allow-always":
161
+ self._tool_approvals[tool_name] = "allow_always"
162
+ logger.info("Tool approval set", tool_name=tool_name, approval="allow_always")
163
+ return "allow"
164
+ case "reject-once":
165
+ return "skip"
166
+ case "reject-always":
167
+ self._tool_approvals[tool_name] = "reject_always"
168
+ logger.info("Tool approval set", tool_name=tool_name, approval="reject_always")
169
+ return "skip"
170
+ case _:
171
+ logger.warning("Unknown permission option", option_id=option_id)
172
+ return "abort_run"
173
+
174
+ async def get_elicitation( # noqa: PLR0911
175
+ self,
176
+ params: types.ElicitRequestParams,
177
+ ) -> types.ElicitResult | types.ErrorData:
178
+ """Get user response to elicitation request with basic schema support.
179
+
180
+ Currently supports boolean schemas via ACP permission options.
181
+ Other schemas fall back to accept/decline options.
182
+
183
+ Args:
184
+ params: MCP elicit request parameters
185
+
186
+ Returns:
187
+ Elicit result with user's response or error data
188
+ """
189
+ try:
190
+ # Handle URL mode elicitation (OAuth, credentials, payments)
191
+ if isinstance(params, types.ElicitRequestURLParams):
192
+ msg = "URL elicitation request"
193
+ elicit_id = params.elicitationId
194
+ logger.info(msg, message=params.message, url=params.url, elicitation_id=elicit_id)
195
+ tool_call_id = f"elicit_url_{elicit_id}"
196
+ title = f"URL Authorization: {params.message}"
197
+ url_options = [
198
+ PermissionOption(option_id="accept", name="Open URL", kind="allow_once"),
199
+ PermissionOption(option_id="decline", name="Decline", kind="reject_once"),
200
+ ]
201
+ response = await self.session.requests.request_permission(
202
+ tool_call_id=tool_call_id,
203
+ title=title,
204
+ options=url_options,
205
+ )
206
+ if isinstance(response.outcome, AllowedOutcome):
207
+ if response.outcome.option_id == "accept":
208
+ webbrowser.open(params.url)
209
+ return types.ElicitResult(action="accept")
210
+ return types.ElicitResult(action="decline")
211
+ return types.ElicitResult(action="cancel")
212
+
213
+ # Form mode elicitation
214
+ schema = params.requestedSchema
215
+ logger.info("Elicitation request", message=params.message, schema=schema)
216
+ tool_call_id = f"elicit_{hash(params.message)}"
217
+ title = f"Elicitation: {params.message}"
218
+
219
+ if _is_boolean_schema(schema):
220
+ options: list[PermissionOption] | None = _create_boolean_elicitation_options()
221
+ response = await self.session.requests.request_permission(
222
+ tool_call_id=tool_call_id,
223
+ title=title,
224
+ options=options,
225
+ )
226
+ return self._handle_boolean_elicitation_response(response, schema)
227
+ if _is_enum_schema(schema) and (options := _create_enum_elicitation_options(schema)):
228
+ response = await self.session.requests.request_permission(
229
+ tool_call_id=tool_call_id,
230
+ title=title,
231
+ options=options,
232
+ )
233
+ return _handle_enum_elicitation_response(response, schema)
234
+
235
+ options = [
236
+ PermissionOption(option_id="accept", name="Accept", kind="allow_once"),
237
+ PermissionOption(option_id="decline", name="Decline", kind="reject_once"),
238
+ ]
239
+ response = await self.session.requests.request_permission(
240
+ tool_call_id=tool_call_id,
241
+ title=title,
242
+ options=options,
243
+ )
244
+
245
+ # Convert permission response to elicitation result
246
+ if isinstance(response.outcome, AllowedOutcome):
247
+ if response.outcome.option_id == "accept":
248
+ # For non-boolean schemas, return empty content
249
+ return types.ElicitResult(action="accept", content={})
250
+ return types.ElicitResult(action="decline")
251
+ if response.outcome.outcome == "cancelled":
252
+ return types.ElicitResult(action="cancel")
253
+ return types.ElicitResult(action="cancel")
254
+
255
+ except Exception as e:
256
+ logger.exception("Failed to handle elicitation")
257
+ return types.ErrorData(code=types.INTERNAL_ERROR, message=f"Elicitation failed: {e}")
258
+
259
+ def _handle_boolean_elicitation_response( # noqa: PLR0911
260
+ self, response: RequestPermissionResponse, schema: dict[str, Any]
261
+ ) -> types.ElicitResult | types.ErrorData:
262
+ """Handle ACP response for boolean elicitation."""
263
+ if isinstance(response.outcome, AllowedOutcome):
264
+ if response.outcome.option_id == "true":
265
+ # Check if we need to wrap in object structure
266
+ if schema.get("type") == "object":
267
+ properties = schema.get("properties", {})
268
+ if len(properties) == 1:
269
+ prop_name = next(iter(properties.keys()))
270
+ return types.ElicitResult(action="accept", content={prop_name: True})
271
+ return types.ElicitResult(action="accept", content={"value": True})
272
+ if response.outcome.option_id == "false":
273
+ # Check if we need to wrap in object structure
274
+ if schema.get("type") == "object":
275
+ properties = schema.get("properties", {})
276
+ if len(properties) == 1:
277
+ prop_name = next(iter(properties.keys()))
278
+ return types.ElicitResult(action="accept", content={prop_name: False})
279
+ return types.ElicitResult(action="accept", content={"value": False})
280
+ if response.outcome.option_id == "cancel":
281
+ return types.ElicitResult(action="cancel")
282
+
283
+ # Handle cancelled outcome
284
+ if response.outcome.outcome == "cancelled":
285
+ return types.ElicitResult(action="cancel")
286
+
287
+ return types.ElicitResult(action="cancel")
288
+
289
+ def clear_tool_approvals(self) -> None:
290
+ """Clear all stored tool approval decisions.
291
+
292
+ This resets the "allow_always" and "reject_always" states,
293
+ so tools will ask for permission again.
294
+ """
295
+ approval_count = len(self._tool_approvals)
296
+ self._tool_approvals.clear()
297
+ logger.info("Cleared tool approval decisions", count=approval_count)
298
+
299
+ def get_tool_approval_state(self) -> dict[str, str]:
300
+ """Get current tool approval state for debugging/inspection.
301
+
302
+ Returns:
303
+ Dictionary mapping tool names to their approval state
304
+ ("allow_always" or "reject_always")
305
+ """
306
+ return self._tool_approvals.copy()
307
+
308
+
309
+ def _handle_enum_elicitation_response(
310
+ response: RequestPermissionResponse, schema: dict[str, Any]
311
+ ) -> types.ElicitResult | types.ErrorData:
312
+ """Handle ACP response for enum elicitation."""
313
+ from mcp import types
314
+
315
+ if isinstance(response.outcome, AllowedOutcome):
316
+ option_id = response.outcome.option_id
317
+
318
+ if option_id == "cancel":
319
+ return types.ElicitResult(action="cancel")
320
+
321
+ # Extract enum value from option_id format: "enum_{index}_{value}"
322
+ if option_id.startswith("enum_"):
323
+ try:
324
+ parts = option_id.split("_", 2) # Split into ["enum", index, value]
325
+ if len(parts) >= 3: # noqa: PLR2004
326
+ enum_index = int(parts[1])
327
+ enum_values = schema.get("enum", [])
328
+ if 0 <= enum_index < len(enum_values):
329
+ selected_value = enum_values[enum_index]
330
+ return types.ElicitResult(action="accept", content=selected_value)
331
+ except (ValueError, IndexError):
332
+ pass
333
+
334
+ # Fallback if parsing fails
335
+ logger.warning("Failed to parse enum option_id", option_id=option_id)
336
+ return types.ElicitResult(action="cancel")
337
+
338
+ return types.ElicitResult(action="cancel")
@@ -0,0 +1,288 @@
1
+ """ACP (Agent Client Protocol) server implementation for agentpool.
2
+
3
+ This module provides the main server class for exposing AgentPool via
4
+ the Agent Client Protocol.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ from datetime import timedelta
11
+ import functools
12
+ from typing import TYPE_CHECKING, Any, Self
13
+
14
+ import logfire
15
+
16
+ from acp import AgentSideConnection
17
+ from acp.stdio import stdio_streams
18
+ from agentpool import AgentPool
19
+ from agentpool.log import get_logger
20
+ from agentpool.models.manifest import AgentsManifest
21
+ from agentpool_server import BaseServer
22
+ from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent
23
+
24
+
25
+ if TYPE_CHECKING:
26
+ from collections.abc import Sequence
27
+
28
+ from tokonomics.model_discovery import ProviderType
29
+ from tokonomics.model_discovery.model_info import ModelInfo
30
+ from upathtools import JoinablePathLike
31
+
32
+ from acp.schema import ModelInfo as ACPModelInfo
33
+
34
+
35
+ logger = get_logger(__name__)
36
+
37
+
38
+ def _convert_to_acp_model_info(
39
+ toko_models: Sequence[ModelInfo],
40
+ ) -> list[ACPModelInfo]:
41
+ """Convert tokonomics ModelInfo list to ACP ModelInfo list.
42
+
43
+ Args:
44
+ toko_models: List of tokonomics ModelInfo objects
45
+
46
+ Returns:
47
+ List of ACP ModelInfo objects with pydantic_ai_id as model_id
48
+ """
49
+ from acp.schema import ModelInfo as ACPModelInfo
50
+
51
+ return [
52
+ ACPModelInfo(
53
+ model_id=model.pydantic_ai_id,
54
+ name=f"{model.provider}: {model.name}" if model.provider else model.name,
55
+ description=model.format(),
56
+ )
57
+ for model in toko_models
58
+ ]
59
+
60
+
61
+ class ACPServer(BaseServer):
62
+ """ACP (Agent Client Protocol) server for agentpool using external library.
63
+
64
+ Provides a bridge between agentpool's Agent system and the standard ACP
65
+ JSON-RPC protocol using the external acp library for robust communication.
66
+
67
+ The actual client communication happens via the AgentSideConnection created
68
+ when start() is called, which communicates with the external process over stdio.
69
+ """
70
+
71
+ def __init__(
72
+ self,
73
+ pool: AgentPool[Any],
74
+ *,
75
+ name: str | None = None,
76
+ file_access: bool = True,
77
+ terminal_access: bool = True,
78
+ providers: list[ProviderType] | None = None,
79
+ debug_messages: bool = False,
80
+ debug_file: str | None = None,
81
+ debug_commands: bool = False,
82
+ agent: str | None = None,
83
+ load_skills: bool = True,
84
+ config_path: str | None = None,
85
+ ) -> None:
86
+ """Initialize ACP server with configuration.
87
+
88
+ Args:
89
+ pool: AgentPool containing available agents
90
+ name: Optional Server name (auto-generated if None)
91
+ file_access: Whether to support file access operations
92
+ terminal_access: Whether to support terminal access operations
93
+ providers: List of providers to use for model discovery (None = openrouter)
94
+ debug_messages: Whether to enable debug message logging
95
+ debug_file: File path for debug message logging
96
+ debug_commands: Whether to enable debug slash commands for testing
97
+ agent: Optional specific agent name to use (defaults to first agent)
98
+ load_skills: Whether to load client-side skills from .claude/skills
99
+ config_path: Path to the configuration file (for tracking/hot-switching)
100
+ """
101
+ super().__init__(pool, name=name, raise_exceptions=True)
102
+ self.file_access = file_access
103
+ self.terminal_access = terminal_access
104
+ self.providers = providers or ["openai", "anthropic", "gemini"]
105
+ self.debug_messages = debug_messages
106
+ self.debug_file = debug_file
107
+ self.debug_commands = debug_commands
108
+ self.agent = agent
109
+ self.load_skills = load_skills
110
+ self.config_path = config_path
111
+
112
+ self._available_models: list[ACPModelInfo] = []
113
+ self._models_initialized = False
114
+
115
+ @classmethod
116
+ def from_config(
117
+ cls,
118
+ config_path: JoinablePathLike,
119
+ *,
120
+ file_access: bool = True,
121
+ terminal_access: bool = True,
122
+ providers: list[ProviderType] | None = None,
123
+ debug_messages: bool = False,
124
+ debug_file: str | None = None,
125
+ debug_commands: bool = False,
126
+ agent: str | None = None,
127
+ load_skills: bool = True,
128
+ ) -> Self:
129
+ """Create ACP server from existing agentpool configuration.
130
+
131
+ Args:
132
+ config_path: Path to agentpool YAML config file
133
+ file_access: Enable file system access
134
+ terminal_access: Enable terminal access
135
+ providers: List of provider types to use for model discovery
136
+ debug_messages: Enable saving JSON messages to file
137
+ debug_file: Path to debug file
138
+ debug_commands: Enable debug slash commands for testing
139
+ agent: Optional specific agent name to use (defaults to first agent)
140
+ load_skills: Whether to load client-side skills from .claude/skills
141
+
142
+ Returns:
143
+ Configured ACP server instance with agent pool from config
144
+ """
145
+ manifest = AgentsManifest.from_file(config_path)
146
+ pool = AgentPool(manifest=manifest)
147
+ server = cls(
148
+ pool,
149
+ file_access=file_access,
150
+ terminal_access=terminal_access,
151
+ providers=providers,
152
+ debug_messages=debug_messages,
153
+ debug_file=debug_file or "acp-debug.jsonl" if debug_messages else None,
154
+ debug_commands=debug_commands,
155
+ agent=agent,
156
+ load_skills=load_skills,
157
+ config_path=str(config_path),
158
+ )
159
+ agent_names = list(server.pool.agents.keys())
160
+
161
+ # Validate specified agent exists if provided
162
+ if agent and agent not in agent_names:
163
+ msg = f"Specified agent {agent!r} not found in config. Available agents: {agent_names}"
164
+ raise ValueError(msg)
165
+
166
+ server.log.info("Created ACP server with agent pool", agent_names=agent_names)
167
+ if agent:
168
+ server.log.info("ACP session agent", agent=agent)
169
+ return server
170
+
171
+ async def _start_async(self) -> None:
172
+ """Start the ACP server (blocking async - runs until stopped)."""
173
+ agent_names = list(self.pool.agents.keys())
174
+ self.log.info("Starting ACP server on stdio", agent_names=agent_names)
175
+ await self._initialize_models() # Initialize models on first run
176
+ create_acp_agent = functools.partial(
177
+ AgentPoolACPAgent,
178
+ agent_pool=self.pool,
179
+ available_models=self._available_models,
180
+ file_access=self.file_access,
181
+ terminal_access=self.terminal_access,
182
+ debug_commands=self.debug_commands,
183
+ default_agent=self.agent,
184
+ load_skills=self.load_skills,
185
+ server=self,
186
+ )
187
+ reader, writer = await stdio_streams()
188
+ file = self.debug_file if self.debug_messages else None
189
+ conn = AgentSideConnection(create_acp_agent, writer, reader, debug_file=file)
190
+ self.log.info("ACP server started", file=self.file_access, terminal=self.terminal_access)
191
+ try: # Keep the connection alive until shutdown
192
+ await self._shutdown_event.wait()
193
+ except asyncio.CancelledError:
194
+ self.log.info("ACP server shutdown requested")
195
+ raise
196
+ except KeyboardInterrupt:
197
+ self.log.info("ACP server shutdown requested")
198
+ except Exception:
199
+ self.log.exception("Connection receive task failed")
200
+ finally:
201
+ await conn.close()
202
+
203
+ async def swap_pool(
204
+ self,
205
+ config_path: str,
206
+ agent: str | None = None,
207
+ ) -> list[str]:
208
+ """Swap the current pool with a new one from config.
209
+
210
+ This method handles the full lifecycle of swapping pools:
211
+ 1. Validates the new configuration
212
+ 2. Creates and initializes the new pool
213
+ 3. Cleans up the old pool
214
+ 4. Updates internal references
215
+
216
+ Args:
217
+ config_path: Path to the new agent configuration file
218
+ agent: Optional specific agent to use as default
219
+
220
+ Returns:
221
+ List of agent names in the new pool
222
+
223
+ Raises:
224
+ ValueError: If config is invalid or specified agent not found
225
+ FileNotFoundError: If config file doesn't exist
226
+ """
227
+ # 1. Parse and validate new config before touching current pool
228
+ self.log.info("Loading new pool configuration", config_path=config_path)
229
+ new_manifest = AgentsManifest.from_file(config_path)
230
+ new_pool = AgentPool(manifest=new_manifest)
231
+
232
+ # 2. Validate agent exists in new pool if specified
233
+ agent_names = list(new_pool.all_agents.keys())
234
+ if not agent_names:
235
+ msg = "New configuration contains no agents"
236
+ raise ValueError(msg)
237
+
238
+ if agent and agent not in agent_names:
239
+ msg = f"Agent {agent!r} not found in new config. Available: {agent_names}"
240
+ raise ValueError(msg)
241
+
242
+ # 3. Enter new pool context first (so we can roll back if it fails)
243
+ try:
244
+ await new_pool.__aenter__()
245
+ except Exception as e:
246
+ self.log.exception("Failed to initialize new pool")
247
+ msg = f"Failed to initialize new pool: {e}"
248
+ raise ValueError(msg) from e
249
+
250
+ # 4. Exit old pool context
251
+ old_pool = self.pool
252
+ try:
253
+ await old_pool.__aexit__(None, None, None)
254
+ except Exception:
255
+ self.log.exception("Error closing old pool (continuing with swap)")
256
+
257
+ # 5. Update references
258
+ self.pool = new_pool
259
+ self.agent = agent
260
+ self.config_path = config_path
261
+
262
+ self.log.info("Pool swapped successfully", agent_names=agent_names, default_agent=agent)
263
+ return agent_names
264
+
265
+ @logfire.instrument("ACP: Initializing models.")
266
+ async def _initialize_models(self) -> None:
267
+ """Initialize available models using tokonomics model discovery.
268
+
269
+ Converts tokonomics ModelInfo to ACP ModelInfo format at startup
270
+ so all downstream code works with ACP types consistently.
271
+ """
272
+ from tokonomics.model_discovery import get_all_models
273
+
274
+ if self._models_initialized:
275
+ return
276
+ try:
277
+ self.log.info("Discovering available models...")
278
+ delta = timedelta(days=200)
279
+ toko_models = await get_all_models(providers=self.providers, max_age=delta)
280
+ # Convert to ACP format once at startup
281
+ self._available_models = _convert_to_acp_model_info(toko_models)
282
+ self._models_initialized = True
283
+ self.log.info("Discovered models", count=len(self._available_models))
284
+ except Exception:
285
+ self.log.exception("Failed to discover models")
286
+ self._available_models = []
287
+ finally:
288
+ self._models_initialized = True