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,786 @@
1
+ """ACP (Agent Client Protocol) Agent implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import KW_ONLY, dataclass, field
6
+ from importlib.metadata import version as _version
7
+ from typing import TYPE_CHECKING, Any, ClassVar
8
+
9
+ from acp import Agent as ACPAgent
10
+ from acp.schema import (
11
+ ForkSessionResponse,
12
+ InitializeResponse,
13
+ ListSessionsResponse,
14
+ LoadSessionResponse,
15
+ ModelInfo as ACPModelInfo,
16
+ NewSessionResponse,
17
+ PromptResponse,
18
+ ResumeSessionResponse,
19
+ SessionInfo,
20
+ SessionModelState,
21
+ SessionModeState,
22
+ SetSessionModelRequest,
23
+ SetSessionModelResponse,
24
+ SetSessionModeRequest,
25
+ SetSessionModeResponse,
26
+ )
27
+ from agentpool import Agent
28
+ from agentpool.log import get_logger
29
+ from agentpool.utils.tasks import TaskManager
30
+ from agentpool_server.acp_server.converters import (
31
+ # agent_to_mode, # TODO: Re-enable when supporting agent switching via modes
32
+ get_confirmation_modes,
33
+ mode_id_to_confirmation_mode,
34
+ )
35
+ from agentpool_server.acp_server.session_manager import ACPSessionManager
36
+
37
+
38
+ if TYPE_CHECKING:
39
+ from collections.abc import Sequence
40
+
41
+ from pydantic_ai import ModelRequest, ModelResponse
42
+
43
+ from acp import AgentSideConnection, Client
44
+ from acp.schema import (
45
+ AuthenticateRequest,
46
+ CancelNotification,
47
+ ClientCapabilities,
48
+ ForkSessionRequest,
49
+ Implementation,
50
+ InitializeRequest,
51
+ ListSessionsRequest,
52
+ LoadSessionRequest,
53
+ NewSessionRequest,
54
+ PromptRequest,
55
+ ResumeSessionRequest,
56
+ SetSessionModelRequest,
57
+ SetSessionModeRequest,
58
+ )
59
+ from agentpool import AgentPool
60
+ from agentpool_server.acp_server.server import ACPServer
61
+ from agentpool_server.acp_server.session import ACPSession
62
+
63
+ logger = get_logger(__name__)
64
+
65
+
66
+ # Claude Code model definitions - simple IDs that the SDK understands
67
+ CLAUDE_CODE_MODELS: list[ACPModelInfo] = [
68
+ ACPModelInfo(
69
+ model_id="opus",
70
+ name="Claude Opus",
71
+ description="Claude Opus - most capable model",
72
+ ),
73
+ ACPModelInfo(
74
+ model_id="sonnet",
75
+ name="Claude Sonnet",
76
+ description="Claude Sonnet - balanced performance and speed",
77
+ ),
78
+ ACPModelInfo(
79
+ model_id="haiku",
80
+ name="Claude Haiku",
81
+ description="Claude Haiku - fast and cost-effective",
82
+ ),
83
+ ]
84
+
85
+
86
+ def create_claude_code_model_state(current_model: str | None = None) -> SessionModelState:
87
+ """Create SessionModelState for Claude Code agents.
88
+
89
+ Args:
90
+ current_model: Currently active model ID (e.g., "sonnet")
91
+
92
+ Returns:
93
+ SessionModelState with Claude Code models
94
+ """
95
+ model_ids = [m.model_id for m in CLAUDE_CODE_MODELS]
96
+ current = current_model if current_model in model_ids else "sonnet"
97
+ return SessionModelState(available_models=CLAUDE_CODE_MODELS, current_model_id=current)
98
+
99
+
100
+ def create_session_model_state(
101
+ available_models: Sequence[ACPModelInfo], current_model: str | None = None
102
+ ) -> SessionModelState | None:
103
+ """Create a SessionModelState from available ACP models.
104
+
105
+ Args:
106
+ available_models: List of ACP ModelInfo objects (already converted from tokonomics)
107
+ current_model: The currently active model (defaults to first available)
108
+
109
+ Returns:
110
+ SessionModelState with all available models, None if no models provided
111
+ """
112
+ if not available_models:
113
+ return None
114
+ # Use first model as current if not specified or not found
115
+ all_ids = [model.model_id for model in available_models]
116
+ current_model_id = current_model if current_model in all_ids else all_ids[0]
117
+ return SessionModelState(
118
+ available_models=list(available_models), current_model_id=current_model_id
119
+ )
120
+
121
+
122
+ @dataclass
123
+ class AgentPoolACPAgent(ACPAgent):
124
+ """Implementation of ACP Agent protocol interface for AgentPool.
125
+
126
+ This class implements the external library's Agent protocol interface,
127
+ bridging AgentPool with the standard ACP JSON-RPC protocol.
128
+ """
129
+
130
+ PROTOCOL_VERSION: ClassVar = 1
131
+
132
+ connection: AgentSideConnection
133
+ """ACP connection for client communication."""
134
+
135
+ agent_pool: AgentPool[Any]
136
+ """AgentPool containing available agents."""
137
+
138
+ _: KW_ONLY
139
+
140
+ available_models: Sequence[ACPModelInfo] = field(default_factory=list)
141
+ """List of available ACP ModelInfo objects (converted from tokonomics at startup)."""
142
+
143
+ file_access: bool = True
144
+ """Whether agent can access filesystem."""
145
+
146
+ terminal_access: bool = True
147
+ """Whether agent can use terminal."""
148
+
149
+ debug_commands: bool = False
150
+ """Whether to enable debug slash commands for testing."""
151
+
152
+ default_agent: str | None = None
153
+ """Optional specific agent name to use as default."""
154
+
155
+ load_skills: bool = True
156
+ """Whether to load client-side skills from .claude/skills directory."""
157
+
158
+ server: ACPServer | None = field(default=None)
159
+ """Reference to the ACPServer for pool hot-switching."""
160
+
161
+ def __post_init__(self) -> None:
162
+ """Initialize derived attributes and setup after field assignment."""
163
+ self.client: Client = self.connection
164
+ self.client_capabilities: ClientCapabilities | None = None
165
+ self.client_info: Implementation | None = None
166
+ self.session_manager = ACPSessionManager(pool=self.agent_pool)
167
+ self.tasks = TaskManager()
168
+ self._initialized = False
169
+
170
+ agent_count = len(self.agent_pool.agents)
171
+ logger.info("Created ACP agent implementation", agent_count=agent_count)
172
+ if self.debug_commands:
173
+ logger.info("Debug slash commands enabled for ACP testing")
174
+
175
+ # Note: Tool registration happens after initialize() when we know client caps
176
+
177
+ async def initialize(self, params: InitializeRequest) -> InitializeResponse:
178
+ """Initialize the agent and negotiate capabilities."""
179
+ logger.info("Initializing ACP agent implementation")
180
+ version = min(params.protocol_version, self.PROTOCOL_VERSION)
181
+ self.client_capabilities = params.client_capabilities
182
+ self.client_info = params.client_info
183
+ logger.info(
184
+ "Client capabilities",
185
+ capabilities=self.client_capabilities,
186
+ client_info=self.client_info,
187
+ )
188
+ self._initialized = True
189
+ response = InitializeResponse.create(
190
+ protocol_version=version,
191
+ name="agentpool",
192
+ title="AgentPool",
193
+ version=_version("agentpool"),
194
+ load_session=True,
195
+ list_sessions=True,
196
+ http_mcp_servers=True,
197
+ sse_mcp_servers=True,
198
+ audio_prompts=True,
199
+ embedded_context_prompts=True,
200
+ image_prompts=True,
201
+ )
202
+ logger.info("ACP agent initialized successfully", response=response)
203
+ return response
204
+
205
+ async def new_session(self, params: NewSessionRequest) -> NewSessionResponse: # noqa: PLR0915
206
+ """Create a new session."""
207
+ if not self._initialized:
208
+ raise RuntimeError("Agent not initialized")
209
+
210
+ try:
211
+ names = list(self.agent_pool.all_agents.keys())
212
+ if not names:
213
+ logger.error("No agents available for session creation")
214
+ raise RuntimeError("No agents available") # noqa: TRY301
215
+
216
+ # Use specified default agent or fall back to first agent
217
+ if self.default_agent and self.default_agent in names:
218
+ default_name = self.default_agent
219
+ else:
220
+ default_name = names[0]
221
+
222
+ logger.info("Creating new session", agents=names, default_agent=default_name)
223
+ session_id = await self.session_manager.create_session(
224
+ default_agent_name=default_name,
225
+ cwd=params.cwd,
226
+ client=self.client,
227
+ acp_agent=self,
228
+ mcp_servers=params.mcp_servers,
229
+ client_capabilities=self.client_capabilities,
230
+ client_info=self.client_info,
231
+ )
232
+
233
+ # Get mode information - pass through from ACPAgent or use our confirmation modes
234
+ from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
235
+
236
+ if session := self.session_manager.get_session(session_id):
237
+ if isinstance(session.agent, ACPAgentClient):
238
+ # Pass through nested agent's modes (e.g., Claude Code's modes)
239
+ if session.agent._state and session.agent._state.modes:
240
+ state = session.agent._state.modes
241
+ modes = state.available_modes
242
+ else:
243
+ # Fallback to our confirmation modes if nested agent has none
244
+ modes = get_confirmation_modes()
245
+ state = SessionModeState(current_mode_id="default", available_modes=modes)
246
+ else:
247
+ # Native Agent - use our tool confirmation modes
248
+ modes = get_confirmation_modes()
249
+ state = SessionModeState(current_mode_id="default", available_modes=modes)
250
+ else:
251
+ modes = get_confirmation_modes()
252
+ state = SessionModeState(current_mode_id="default", available_modes=modes)
253
+ # TODO: Re-enable agent switching via modes when needed:
254
+ # modes = [agent_to_mode(agent) for agent in self.agent_pool.all_agents.values()]
255
+ # state = SessionModeState(current_mode_id=default_name, available_modes=modes)
256
+
257
+ # Get model information from the default agent
258
+ if session := self.session_manager.get_session(session_id):
259
+ from agentpool.agents.claude_code_agent import ClaudeCodeAgent
260
+
261
+ if isinstance(session.agent, ClaudeCodeAgent):
262
+ # Claude Code uses simple model IDs (sonnet, opus, haiku)
263
+ models = create_claude_code_model_state(session.agent.model_name)
264
+ elif isinstance(session.agent, ACPAgentClient):
265
+ # Nested ACP agent - pass through its model state directly
266
+ if session.agent._state and session.agent._state.models:
267
+ models = session.agent._state.models
268
+ else:
269
+ models = None
270
+ else:
271
+ current_model = session.agent.model_name
272
+ models = create_session_model_state(self.available_models, current_model)
273
+ else:
274
+ models = None
275
+ except Exception:
276
+ logger.exception("Failed to create new session")
277
+ raise
278
+ else:
279
+ # Schedule available commands update after session response is returned
280
+ if session := self.session_manager.get_session(session_id):
281
+ # Schedule task to run after response is sent
282
+ self.tasks.create_task(session.send_available_commands_update())
283
+ self.tasks.create_task(session.init_project_context())
284
+ self.tasks.create_task(session._register_prompt_hub_commands())
285
+ if self.load_skills:
286
+ coro_4 = session.init_client_skills()
287
+ self.tasks.create_task(coro_4, name=f"init_client_skills_{session_id}")
288
+ logger.info("Created session", session_id=session_id, agent_count=len(modes))
289
+ return NewSessionResponse(session_id=session_id, modes=state, models=models)
290
+
291
+ async def load_session(self, params: LoadSessionRequest) -> LoadSessionResponse:
292
+ """Load an existing session from storage.
293
+
294
+ This tries to:
295
+ 1. Check if session is already active
296
+ 2. If not, try to resume from persistent storage
297
+ 3. Initialize MCP servers and other session resources
298
+ 4. Replay conversation history via ACP notifications
299
+ 5. Return session state (modes, models)
300
+ """
301
+ if not self._initialized:
302
+ raise RuntimeError("Agent not initialized")
303
+
304
+ try:
305
+ # First check if session is already active
306
+ session = self.session_manager.get_session(params.session_id)
307
+
308
+ if not session:
309
+ # Try to resume from storage
310
+ msg = "Attempting to resume session from storage"
311
+ logger.info(msg, session_id=params.session_id)
312
+ session = await self.session_manager.resume_session(
313
+ session_id=params.session_id,
314
+ client=self.client,
315
+ acp_agent=self,
316
+ client_capabilities=self.client_capabilities,
317
+ client_info=self.client_info,
318
+ )
319
+
320
+ if not session:
321
+ logger.warning("Session not found in storage", session_id=params.session_id)
322
+ return LoadSessionResponse()
323
+
324
+ # Update session with new request parameters if provided
325
+ if params.cwd and params.cwd != session.cwd:
326
+ session.cwd = params.cwd
327
+ logger.info("Updated session cwd", session_id=params.session_id, cwd=params.cwd)
328
+
329
+ # Initialize MCP servers if provided in load request
330
+ if params.mcp_servers:
331
+ session.mcp_servers = params.mcp_servers
332
+ await session.initialize_mcp_servers()
333
+
334
+ # Build response with current session state
335
+ # Get mode information - pass through from ACPAgent or use our confirmation modes
336
+ from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
337
+
338
+ if isinstance(session.agent, ACPAgentClient):
339
+ # Pass through nested agent's modes (e.g., Claude Code's modes)
340
+ if session.agent._state and session.agent._state.modes:
341
+ mode_state = session.agent._state.modes
342
+ else:
343
+ # Fallback to our confirmation modes if nested agent has none
344
+ modes = get_confirmation_modes()
345
+ mode_state = SessionModeState(current_mode_id="default", available_modes=modes)
346
+ else:
347
+ # Native Agent - use our tool confirmation modes
348
+ modes = get_confirmation_modes()
349
+ mode_state = SessionModeState(current_mode_id="default", available_modes=modes)
350
+ # TODO: Re-enable agent switching via modes when needed:
351
+ # modes = [agent_to_mode(agent) for agent in self.agent_pool.all_agents.values()]
352
+ # mode_state = SessionModeState(
353
+ # current_mode_id=session.current_agent_name,
354
+ # available_modes=modes,
355
+ # )
356
+
357
+ # Get model information based on agent type
358
+ from agentpool.agents.claude_code_agent import ClaudeCodeAgent
359
+
360
+ models: SessionModelState | None
361
+ if session.agent and isinstance(session.agent, ClaudeCodeAgent):
362
+ # Claude Code uses simple model IDs (sonnet, opus, haiku)
363
+ models = create_claude_code_model_state(session.agent.model_name)
364
+ else:
365
+ current_model = session.agent.model_name if session.agent else None
366
+ models = create_session_model_state(self.available_models, current_model)
367
+ # Schedule post-load initialization tasks
368
+ self.tasks.create_task(session.send_available_commands_update())
369
+ self.tasks.create_task(session.init_project_context())
370
+ # Replay conversation history via ACP notifications
371
+ self.tasks.create_task(self._replay_conversation_history(session))
372
+ logger.info("Session loaded successfully", agent=session.current_agent_name)
373
+ return LoadSessionResponse(models=models, modes=mode_state)
374
+
375
+ except Exception:
376
+ logger.exception("Failed to load session", session_id=params.session_id)
377
+ return LoadSessionResponse()
378
+
379
+ async def _replay_conversation_history(self, session: ACPSession) -> None:
380
+ """Replay conversation history for a loaded session via ACP notifications.
381
+
382
+ Per ACP spec, when loading a session the agent MUST replay the entire
383
+ conversation to the client via session/update notifications.
384
+
385
+ Args:
386
+ session: The ACP session to replay history for
387
+ """
388
+ from agentpool_config.session import SessionQuery
389
+
390
+ # Get session data to find conversation_id
391
+ session_data = await self.session_manager.session_manager.store.load(session.session_id)
392
+ if not session_data:
393
+ logger.warning("No session data found for replay", session_id=session.session_id)
394
+ return
395
+
396
+ # Get storage provider
397
+ storage = self.agent_pool.storage
398
+ if not storage:
399
+ logger.debug("No storage provider, skipping conversation replay")
400
+ return
401
+
402
+ # Query messages by conversation_id
403
+ query = SessionQuery(name=session_data.conversation_id)
404
+ try:
405
+ chat_messages = await storage.filter_messages(query)
406
+ except NotImplementedError:
407
+ logger.debug("Storage provider doesn't support history loading")
408
+ return
409
+
410
+ if not chat_messages:
411
+ logger.debug("No messages to replay", session_id=session.session_id)
412
+ return
413
+
414
+ logger.info("Replaying conversation history", message_count=len(chat_messages))
415
+ # Extract ModelRequest/ModelResponse from ChatMessage.messages field
416
+ model_messages: list[ModelRequest | ModelResponse] = []
417
+ for chat_msg in chat_messages:
418
+ if chat_msg.messages:
419
+ model_messages.extend(chat_msg.messages)
420
+
421
+ if not model_messages:
422
+ logger.debug("No model messages to replay", session_id=session.session_id)
423
+ return
424
+
425
+ # Use ACPNotifications.replay() which handles all content types properly
426
+ try:
427
+ await session.notifications.replay(model_messages)
428
+ logger.info("Conversation replay complete", replayed_count=len(model_messages))
429
+ except Exception as e: # noqa: BLE001
430
+ logger.warning("Failed to replay conversation history", error=str(e))
431
+
432
+ async def list_sessions(self, params: ListSessionsRequest) -> ListSessionsResponse:
433
+ """List available sessions.
434
+
435
+ Returns sessions from both active memory and persistent storage.
436
+ Supports pagination via cursor and optional cwd filtering.
437
+ """
438
+ if not self._initialized:
439
+ raise RuntimeError("Agent not initialized")
440
+
441
+ try:
442
+ # Get session IDs from storage (includes both active and persisted)
443
+ session_ids = await self.session_manager.list_sessions(active_only=False)
444
+ # Build SessionInfo objects
445
+ sessions: list[SessionInfo] = []
446
+ for session_id in session_ids:
447
+ # Try active session first
448
+ active_session = self.session_manager.get_session(session_id)
449
+ if active_session:
450
+ # Filter by cwd if specified
451
+ if params.cwd and active_session.cwd != params.cwd:
452
+ continue
453
+ title = f"Session with {active_session.current_agent_name}"
454
+ sessions.append(
455
+ SessionInfo(session_id=session_id, cwd=active_session.cwd, title=title)
456
+ )
457
+ else:
458
+ # Load from storage to get details
459
+ data = await self.session_manager.session_manager.store.load(session_id)
460
+ if data:
461
+ # Filter by cwd if specified
462
+ if params.cwd and data.cwd != params.cwd:
463
+ continue
464
+ info = SessionInfo(
465
+ session_id=session_id,
466
+ cwd=data.cwd or "",
467
+ title=f"Session with {data.agent_name}",
468
+ updated_at=data.last_active.isoformat(),
469
+ )
470
+ sessions.append(info)
471
+
472
+ logger.info("Listed sessions", count=len(sessions))
473
+ return ListSessionsResponse(sessions=sessions)
474
+
475
+ except Exception:
476
+ logger.exception("Failed to list sessions")
477
+ return ListSessionsResponse(sessions=[])
478
+
479
+ async def fork_session(self, params: ForkSessionRequest) -> ForkSessionResponse:
480
+ """Fork an existing session.
481
+
482
+ Creates a new session with the same state as the original.
483
+ UNSTABLE: This feature is not part of the spec yet.
484
+ """
485
+ if not self._initialized:
486
+ raise RuntimeError("Agent not initialized")
487
+
488
+ logger.info("Forking session", session_id=params.session_id)
489
+ # For now, just create a new session - full fork implementation would copy state
490
+ default_agent = next(iter(self.agent_pool.manifest.agents.keys()))
491
+ session_id = await self.session_manager.create_session(
492
+ default_agent_name=default_agent,
493
+ cwd=params.cwd,
494
+ client=self.client,
495
+ acp_agent=self,
496
+ mcp_servers=params.mcp_servers,
497
+ session_id=None, # Let it generate a new ID
498
+ client_capabilities=self.client_capabilities,
499
+ client_info=self.client_info,
500
+ )
501
+ return ForkSessionResponse(session_id=session_id)
502
+
503
+ async def resume_session(self, params: ResumeSessionRequest) -> ResumeSessionResponse:
504
+ """Resume an existing session.
505
+
506
+ Like load_session but doesn't return previous messages.
507
+ UNSTABLE: This feature is not part of the spec yet.
508
+ """
509
+ if not self._initialized:
510
+ raise RuntimeError("Agent not initialized")
511
+
512
+ logger.info("Resuming session", session_id=params.session_id)
513
+ # Similar to load_session but without replaying history
514
+ try:
515
+ session = await self.session_manager.resume_session(
516
+ session_id=params.session_id,
517
+ client=self.client,
518
+ acp_agent=self,
519
+ client_capabilities=self.client_capabilities,
520
+ client_info=self.client_info,
521
+ )
522
+ if not session:
523
+ logger.warning("Session not found", session_id=params.session_id)
524
+ return ResumeSessionResponse()
525
+ except Exception:
526
+ logger.exception("Failed to resume session", session_id=params.session_id)
527
+ return ResumeSessionResponse()
528
+
529
+ async def authenticate(self, params: AuthenticateRequest) -> None:
530
+ """Authenticate with the agent."""
531
+ logger.info("Authentication requested", method_id=params.method_id)
532
+
533
+ async def prompt(self, params: PromptRequest) -> PromptResponse:
534
+ """Process a prompt request."""
535
+ if not self._initialized:
536
+ raise RuntimeError("Agent not initialized")
537
+
538
+ logger.info("Processing prompt", session_id=params.session_id)
539
+ session = self.session_manager.get_session(params.session_id)
540
+
541
+ # Auto-recreate session if not found (e.g., after pool swap)
542
+ if not session:
543
+ logger.info("Session not found, recreating", session_id=params.session_id)
544
+ try:
545
+ # Get default agent name
546
+ names = list(self.agent_pool.all_agents.keys())
547
+ if not names:
548
+ logger.error("No agents available for session recreation")
549
+ return PromptResponse(stop_reason="end_turn")
550
+
551
+ default_name = (
552
+ self.default_agent
553
+ if self.default_agent and self.default_agent in names
554
+ else names[0]
555
+ )
556
+
557
+ # Try to get cwd from stored session data
558
+ cwd = "."
559
+ try:
560
+ stored = await self.session_manager.session_manager.store.load(
561
+ params.session_id
562
+ )
563
+ if stored and stored.cwd:
564
+ cwd = stored.cwd
565
+ except Exception: # noqa: BLE001
566
+ pass # Use default cwd
567
+
568
+ # Recreate session with same ID
569
+ await self.session_manager.create_session(
570
+ default_agent_name=default_name,
571
+ cwd=cwd,
572
+ client=self.client,
573
+ acp_agent=self,
574
+ session_id=params.session_id,
575
+ client_capabilities=self.client_capabilities,
576
+ client_info=self.client_info,
577
+ )
578
+ session = self.session_manager.get_session(params.session_id)
579
+ if session:
580
+ # Initialize session extras
581
+ self.tasks.create_task(session.send_available_commands_update())
582
+ self.tasks.create_task(session.init_project_context())
583
+ except Exception:
584
+ logger.exception("Failed to recreate session", session_id=params.session_id)
585
+ return PromptResponse(stop_reason="end_turn")
586
+
587
+ try:
588
+ if not session:
589
+ raise ValueError(f"Session {params.session_id} not found") # noqa: TRY301
590
+ stop_reason = await session.process_prompt(params.prompt)
591
+ # Return the actual stop reason from the session
592
+ except Exception as e:
593
+ logger.exception("Failed to process prompt", session_id=params.session_id)
594
+ msg = f"Error processing prompt: {e}"
595
+ if session:
596
+ # Send error notification asynchronously to avoid blocking response
597
+ name = f"error_notification_{params.session_id}"
598
+ self.tasks.create_task(session._send_error_notification(msg), name=name)
599
+
600
+ return PromptResponse(stop_reason="end_turn")
601
+ else:
602
+ response = PromptResponse(stop_reason=stop_reason)
603
+ logger.info("Returning PromptResponse", stop_reason=stop_reason)
604
+ return response
605
+
606
+ async def cancel(self, params: CancelNotification) -> None:
607
+ """Cancel operations for a session."""
608
+ logger.info("Cancelling session", session_id=params.session_id)
609
+ try:
610
+ # Get session and cancel it
611
+ if session := self.session_manager.get_session(params.session_id):
612
+ await session.cancel()
613
+ logger.info("Cancelled operations", session_id=params.session_id)
614
+ else:
615
+ msg = "Session not found for cancellation"
616
+ logger.warning(msg, session_id=params.session_id)
617
+
618
+ except Exception:
619
+ logger.exception("Failed to cancel session", session_id=params.session_id)
620
+
621
+ async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
622
+ return {"example": "response"}
623
+
624
+ async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
625
+ return None
626
+
627
+ async def set_session_mode(
628
+ self, params: SetSessionModeRequest
629
+ ) -> SetSessionModeResponse | None:
630
+ """Set the session mode (change tool confirmation level).
631
+
632
+ Maps ACP mode IDs to ToolConfirmationMode and calls set_tool_confirmation_mode
633
+ on the session's agent. Each agent type handles the mode change appropriately:
634
+ - Agent/AGUIAgent: Updates local confirmation mode
635
+ - ACPAgent: Updates local mode AND forwards to remote ACP server
636
+
637
+ Mode mappings:
638
+ - "default": per_tool (confirm tools marked as requiring it)
639
+ - "acceptEdits": never (auto-approve all tool calls)
640
+ """
641
+ from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
642
+
643
+ try:
644
+ session = self.session_manager.get_session(params.session_id)
645
+ if not session:
646
+ logger.warning("Session not found for mode switch", session_id=params.session_id)
647
+ return None
648
+
649
+ # Map mode_id to confirmation mode
650
+ confirmation_mode = mode_id_to_confirmation_mode(params.mode_id)
651
+ if not confirmation_mode:
652
+ logger.error("Invalid mode_id", mode_id=params.mode_id)
653
+ return None
654
+
655
+ # All agent types support set_tool_confirmation_mode
656
+ # ACPAgent handles forwarding to remote server internally
657
+ await session.agent.set_tool_confirmation_mode(confirmation_mode)
658
+
659
+ # Update stored mode state for ACPAgent
660
+ if (
661
+ isinstance(session.agent, ACPAgentClient)
662
+ and session.agent._state
663
+ and session.agent._state.modes
664
+ ):
665
+ session.agent._state.modes.current_mode_id = params.mode_id
666
+
667
+ logger.info(
668
+ "Set tool confirmation mode",
669
+ mode_id=params.mode_id,
670
+ confirmation_mode=confirmation_mode,
671
+ session_id=params.session_id,
672
+ agent_type=type(session.agent).__name__,
673
+ )
674
+ return SetSessionModeResponse()
675
+
676
+ except Exception:
677
+ logger.exception("Failed to set session mode", session_id=params.session_id)
678
+ return None
679
+
680
+ async def set_session_model( # noqa: PLR0911
681
+ self, params: SetSessionModelRequest
682
+ ) -> SetSessionModelResponse | None:
683
+ """Set the session model.
684
+
685
+ Changes the model for the active agent in the session.
686
+ Validates that the requested model is in the available models list:
687
+ - For ClaudeCodeAgent: validates against Claude Code models (sonnet, opus, haiku)
688
+ - For native Agent: validates against server's available_models
689
+ - For ACPAgent: validates against the nested agent's model list
690
+ """
691
+ from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
692
+ from agentpool.agents.claude_code_agent import ClaudeCodeAgent
693
+
694
+ try:
695
+ session = self.session_manager.get_session(params.session_id)
696
+ if not session:
697
+ msg = "Session not found for model switch"
698
+ logger.warning(msg, session_id=params.session_id)
699
+ return None
700
+
701
+ # Validate model based on agent type
702
+ if isinstance(session.agent, ClaudeCodeAgent):
703
+ # For ClaudeCodeAgent, validate against our hardcoded models
704
+ available_ids = [m.model_id for m in CLAUDE_CODE_MODELS]
705
+ if params.model_id not in available_ids:
706
+ logger.warning(
707
+ "Model not in Claude Code available models",
708
+ model_id=params.model_id,
709
+ available=available_ids,
710
+ )
711
+ return None
712
+ await session.agent.set_model(params.model_id)
713
+ logger.info("Set model", model_id=params.model_id, session_id=params.session_id)
714
+ return SetSessionModelResponse()
715
+
716
+ if isinstance(session.agent, ACPAgentClient):
717
+ # For ACPAgent, validate against nested agent's model list
718
+ if session.agent._state and session.agent._state.models:
719
+ available_ids = [
720
+ m.model_id for m in session.agent._state.models.available_models
721
+ ]
722
+ if params.model_id not in available_ids:
723
+ logger.warning(
724
+ "Model not in ACPAgent's available models",
725
+ model_id=params.model_id,
726
+ available=available_ids,
727
+ )
728
+ return None
729
+ # TODO: Use ACP protocol once set_session_model stabilizes
730
+ # For now, we can't actually change the model on ACPAgent
731
+ logger.warning(
732
+ "Model change for ACPAgent not yet supported (ACP protocol UNSTABLE)",
733
+ model_id=params.model_id,
734
+ )
735
+ return None
736
+
737
+ if isinstance(session.agent, Agent):
738
+ # For native Agent, validate against server's available models
739
+ available_ids = [m.model_id for m in self.available_models]
740
+ if params.model_id not in available_ids:
741
+ msg = "Model not in available models"
742
+ logger.warning(msg, model_id=params.model_id, available=available_ids)
743
+ return None
744
+ session.agent.set_model(params.model_id)
745
+
746
+ logger.info("Set model", model_id=params.model_id, session_id=params.session_id)
747
+ return SetSessionModelResponse()
748
+ except Exception:
749
+ logger.exception("Failed to set session model", session_id=params.session_id)
750
+ return None
751
+
752
+ async def swap_pool(self, config_path: str, agent: str | None = None) -> list[str]:
753
+ """Swap the agent pool with a new one from configuration.
754
+
755
+ This coordinates the full pool swap:
756
+ 1. Closes all active sessions
757
+ 2. Delegates to server.swap_pool() for pool lifecycle
758
+ 3. Updates internal references
759
+
760
+ Args:
761
+ config_path: Path to the new agent configuration file
762
+ agent: Optional specific agent to use as default
763
+
764
+ Returns:
765
+ List of agent names in the new pool
766
+
767
+ Raises:
768
+ RuntimeError: If server reference is not set
769
+ ValueError: If config is invalid or agent not found
770
+ """
771
+ if not self.server:
772
+ msg = "Server reference not set - cannot swap pool"
773
+ raise RuntimeError(msg)
774
+
775
+ logger.info("Swapping pool", config_path=config_path, agent=agent)
776
+ # 1. Close all active sessions
777
+ closed_count = await self.session_manager.close_all_sessions()
778
+ logger.info("Closed sessions before pool swap", count=closed_count)
779
+ # 2. Delegate to server for pool lifecycle management
780
+ agent_names = await self.server.swap_pool(config_path, agent)
781
+ # 3. Update internal references
782
+ self.agent_pool = self.server.pool
783
+ self.session_manager._pool = self.server.pool
784
+ self.default_agent = agent
785
+ logger.info("Pool swap complete", agent_names=agent_names)
786
+ return agent_names