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,294 @@
1
+ """ACP to native event converters.
2
+
3
+ This module provides conversion from ACP session updates to native agentpool
4
+ streaming events, enabling ACPAgent to yield the same event types as native agents.
5
+
6
+ This is the reverse of the conversion done in acp_server/session.py handle_event().
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import base64
12
+ from typing import TYPE_CHECKING, Any, overload
13
+
14
+ from pydantic_ai import (
15
+ AudioUrl,
16
+ BinaryContent,
17
+ BinaryImage,
18
+ DocumentUrl,
19
+ ImageUrl,
20
+ PartDeltaEvent,
21
+ TextPartDelta,
22
+ ThinkingPartDelta,
23
+ VideoUrl,
24
+ )
25
+
26
+ from acp.schema import (
27
+ AgentMessageChunk,
28
+ AgentPlanUpdate,
29
+ AgentThoughtChunk,
30
+ AudioContentBlock,
31
+ BlobResourceContents,
32
+ ContentToolCallContent,
33
+ EmbeddedResourceContentBlock,
34
+ FileEditToolCallContent,
35
+ ImageContentBlock,
36
+ ResourceContentBlock,
37
+ TerminalToolCallContent,
38
+ TextContentBlock,
39
+ ToolCallProgress,
40
+ ToolCallStart,
41
+ UserMessageChunk,
42
+ )
43
+ from agentpool.agents.events import (
44
+ DiffContentItem,
45
+ LocationContentItem,
46
+ PlanUpdateEvent,
47
+ TerminalContentItem,
48
+ ToolCallProgressEvent,
49
+ ToolCallStartEvent,
50
+ )
51
+
52
+
53
+ if TYPE_CHECKING:
54
+ from collections.abc import Sequence
55
+
56
+ from pydantic_ai import UserContent
57
+
58
+ from acp.schema import ContentBlock, SessionUpdate
59
+ from acp.schema.mcp import HttpMcpServer, McpServer, SseMcpServer, StdioMcpServer
60
+ from acp.schema.tool_call import ToolCallContent, ToolCallLocation
61
+ from agentpool.agents.events import RichAgentStreamEvent, ToolCallContentItem
62
+ from agentpool_config.mcp_server import (
63
+ MCPServerConfig,
64
+ SSEMCPServerConfig,
65
+ StdioMCPServerConfig,
66
+ StreamableHTTPMCPServerConfig,
67
+ )
68
+
69
+
70
+ def convert_acp_locations(
71
+ locations: list[ToolCallLocation] | None,
72
+ ) -> list[LocationContentItem]:
73
+ """Convert ACP ToolCallLocation list to native LocationContentItem list."""
74
+ return [LocationContentItem(path=loc.path, line=loc.line) for loc in locations or []]
75
+
76
+
77
+ def convert_acp_content(content: list[ToolCallContent] | None) -> list[ToolCallContentItem]:
78
+ """Convert ACP ToolCallContent list to native ToolCallContentItem list."""
79
+ if not content:
80
+ return []
81
+
82
+ result: list[ToolCallContentItem] = []
83
+ for item in content:
84
+ match item:
85
+ case TerminalToolCallContent(terminal_id=terminal_id):
86
+ result.append(TerminalContentItem(terminal_id=terminal_id))
87
+ case FileEditToolCallContent(path=path, old_text=old_text, new_text=new_text):
88
+ result.append(DiffContentItem(path=path, old_text=old_text, new_text=new_text))
89
+ case ContentToolCallContent(content=TextContentBlock(text=text)):
90
+ from agentpool.agents.events import TextContentItem
91
+
92
+ result.append(TextContentItem(text=text))
93
+ return result
94
+
95
+
96
+ def convert_to_acp_content(prompts: Sequence[UserContent]) -> list[ContentBlock]:
97
+ """Convert pydantic-ai UserContent to ACP ContentBlock format.
98
+
99
+ Handles text, images, audio, video, and document content types.
100
+
101
+ Args:
102
+ prompts: pydantic-ai UserContent items
103
+
104
+ Returns:
105
+ List of ACP ContentBlock items
106
+ """
107
+ content_blocks: list[ContentBlock] = []
108
+
109
+ for item in prompts:
110
+ match item:
111
+ case str(text):
112
+ content_blocks.append(TextContentBlock(text=text))
113
+
114
+ case BinaryImage(data=data, media_type=media_type):
115
+ encoded = base64.b64encode(data).decode("utf-8")
116
+ content_blocks.append(ImageContentBlock(data=encoded, mime_type=media_type))
117
+
118
+ case BinaryContent(data=data, media_type=media_type):
119
+ encoded = base64.b64encode(data).decode("utf-8")
120
+ # Handle different media types
121
+ if media_type and media_type.startswith("image/"):
122
+ content_blocks.append(ImageContentBlock(data=encoded, mime_type=media_type))
123
+ elif media_type and media_type.startswith("audio/"):
124
+ content_blocks.append(AudioContentBlock(data=encoded, mime_type=media_type))
125
+ elif media_type == "application/pdf":
126
+ blob_resource = BlobResourceContents(
127
+ blob=encoded,
128
+ mime_type="application/pdf",
129
+ uri=f"data:application/pdf;base64,{encoded[:50]}...",
130
+ )
131
+ content_blocks.append(EmbeddedResourceContentBlock(resource=blob_resource))
132
+ else:
133
+ # Generic binary as embedded resource
134
+ blob_resource = BlobResourceContents(
135
+ blob=encoded,
136
+ mime_type=media_type or "application/octet-stream",
137
+ uri=f"data:{media_type or 'application/octet-stream'};base64,...",
138
+ )
139
+ content_blocks.append(EmbeddedResourceContentBlock(resource=blob_resource))
140
+
141
+ case ImageUrl(url=url, media_type=typ):
142
+ content_blocks.append(
143
+ ResourceContentBlock(uri=url, name="Image", mime_type=typ or "image/jpeg")
144
+ )
145
+
146
+ case AudioUrl(url=url, media_type=media_type):
147
+ content_blocks.append(
148
+ ResourceContentBlock(
149
+ uri=url,
150
+ name="Audio",
151
+ mime_type=media_type or "audio/wav",
152
+ description="Audio content",
153
+ )
154
+ )
155
+
156
+ case DocumentUrl(url=url, media_type=media_type):
157
+ content_blocks.append(
158
+ ResourceContentBlock(
159
+ uri=url,
160
+ name="Document",
161
+ mime_type=media_type or "application/pdf",
162
+ description="Document",
163
+ )
164
+ )
165
+
166
+ case VideoUrl(url=url, media_type=media_type):
167
+ content_blocks.append(
168
+ ResourceContentBlock(
169
+ uri=url,
170
+ name="Video",
171
+ mime_type=media_type or "video/mp4",
172
+ description="Video content",
173
+ )
174
+ )
175
+
176
+ return content_blocks
177
+
178
+
179
+ def acp_to_native_event(update: SessionUpdate) -> RichAgentStreamEvent[Any] | None: # noqa: PLR0911
180
+ """Convert ACP session update to native streaming event.
181
+
182
+ Args:
183
+ update: ACP SessionUpdate from session/update notification
184
+
185
+ Returns:
186
+ Corresponding native event, or None if no mapping exists
187
+ """
188
+ match update:
189
+ # Text message chunks -> PartDeltaEvent with TextPartDelta
190
+ case AgentMessageChunk(content=TextContentBlock(text=text)):
191
+ return PartDeltaEvent(index=0, delta=TextPartDelta(content_delta=text))
192
+ # Thought chunks -> PartDeltaEvent with ThinkingPartDelta
193
+ case AgentThoughtChunk(content=TextContentBlock(text=text)):
194
+ return PartDeltaEvent(index=0, delta=ThinkingPartDelta(content_delta=text))
195
+ # User message echo - could emit as PartStartEvent if needed
196
+ case UserMessageChunk():
197
+ return None # Usually ignored
198
+ # Tool call start -> ToolCallStartEvent
199
+ case ToolCallStart() as tc:
200
+ return ToolCallStartEvent(
201
+ tool_call_id=tc.tool_call_id,
202
+ tool_name=tc.title, # ACP uses title, not separate tool_name
203
+ title=tc.title,
204
+ kind=tc.kind or "other",
205
+ content=convert_acp_content(list(tc.content) if tc.content else None),
206
+ locations=convert_acp_locations(list(tc.locations) if tc.locations else None),
207
+ raw_input=tc.raw_input or {},
208
+ )
209
+
210
+ # Tool call progress -> ToolCallProgressEvent
211
+ case ToolCallProgress() as tc:
212
+ items = convert_acp_content(list(tc.content) if tc.content else None)
213
+ return ToolCallProgressEvent(
214
+ tool_call_id=tc.tool_call_id,
215
+ status=tc.status or "in_progress",
216
+ title=tc.title,
217
+ items=items,
218
+ message=str(tc.raw_output) if tc.raw_output else None,
219
+ )
220
+
221
+ # Plan update -> PlanUpdateEvent
222
+ case AgentPlanUpdate(entries=acp_entries):
223
+ from agentpool.resource_providers.plan_provider import PlanEntry
224
+
225
+ native_entries = [
226
+ PlanEntry(content=e.content, priority=e.priority, status=e.status)
227
+ for e in acp_entries
228
+ ]
229
+ return PlanUpdateEvent(entries=native_entries)
230
+
231
+ case _:
232
+ return None
233
+
234
+
235
+ @overload
236
+ def mcp_config_to_acp(config: StdioMCPServerConfig) -> StdioMcpServer | None: ...
237
+
238
+
239
+ @overload
240
+ def mcp_config_to_acp(config: SSEMCPServerConfig) -> SseMcpServer | None: ...
241
+
242
+
243
+ @overload
244
+ def mcp_config_to_acp(config: StreamableHTTPMCPServerConfig) -> HttpMcpServer | None: ...
245
+
246
+
247
+ @overload
248
+ def mcp_config_to_acp(config: MCPServerConfig) -> McpServer | None: ...
249
+
250
+
251
+ def mcp_config_to_acp(config: MCPServerConfig) -> McpServer | None:
252
+ """Convert native MCPServerConfig to ACP McpServer format.
253
+
254
+ Args:
255
+ config: agentpool MCP server configuration
256
+
257
+ Returns:
258
+ ACP-compatible McpServer instance, or None if conversion not possible
259
+ """
260
+ from acp.schema.common import EnvVariable
261
+ from acp.schema.mcp import HttpMcpServer, SseMcpServer, StdioMcpServer
262
+ from agentpool_config.mcp_server import (
263
+ SSEMCPServerConfig,
264
+ StdioMCPServerConfig,
265
+ StreamableHTTPMCPServerConfig,
266
+ )
267
+
268
+ match config:
269
+ case StdioMCPServerConfig(command=command, args=args):
270
+ env_vars = config.get_env_vars() if hasattr(config, "get_env_vars") else {}
271
+ return StdioMcpServer(
272
+ name=config.name or command,
273
+ command=command,
274
+ args=list(args) if args else [],
275
+ env=[EnvVariable(name=k, value=v) for k, v in env_vars.items()],
276
+ )
277
+ case SSEMCPServerConfig(url=url):
278
+ return SseMcpServer(name=config.name or str(url), url=url, headers=[])
279
+ case StreamableHTTPMCPServerConfig(url=url):
280
+ return HttpMcpServer(name=config.name or str(url), url=url, headers=[])
281
+ case _:
282
+ return None
283
+
284
+
285
+ def mcp_configs_to_acp(configs: Sequence[MCPServerConfig]) -> list[McpServer]:
286
+ """Convert a sequence of MCPServerConfig to ACP McpServer list.
287
+
288
+ Args:
289
+ configs: Sequence of agentpool MCP server configurations
290
+
291
+ Returns:
292
+ List of ACP-compatible McpServer instances (skips unconvertible configs)
293
+ """
294
+ return [converted for config in configs if (converted := mcp_config_to_acp(config)) is not None]
@@ -0,0 +1,317 @@
1
+ """ACP Agent - MessageNode wrapping an external ACP subprocess."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING, Any
8
+ import uuid
9
+
10
+ import anyio
11
+
12
+ from acp.client.protocol import Client
13
+ from acp.schema import (
14
+ CreateTerminalResponse,
15
+ KillTerminalCommandResponse,
16
+ ReadTextFileResponse,
17
+ ReleaseTerminalResponse,
18
+ RequestPermissionResponse,
19
+ TerminalOutputResponse,
20
+ WaitForTerminalExitResponse,
21
+ WriteTextFileResponse,
22
+ )
23
+ from agentpool.log import get_logger
24
+ from agentpool.tools.base import Tool
25
+
26
+
27
+ if TYPE_CHECKING:
28
+ from exxec import ExecutionEnvironment
29
+
30
+ from acp.schema import (
31
+ CreateTerminalRequest,
32
+ KillTerminalCommandRequest,
33
+ ReadTextFileRequest,
34
+ ReleaseTerminalRequest,
35
+ RequestPermissionRequest,
36
+ SessionNotification,
37
+ TerminalOutputRequest,
38
+ WaitForTerminalExitRequest,
39
+ WriteTextFileRequest,
40
+ )
41
+ from agentpool.agents.acp_agent import ACPAgent
42
+ from agentpool.agents.acp_agent.session_state import ACPSessionState
43
+ from agentpool.ui.base import InputProvider
44
+ from agentpool_config.nodes import ToolConfirmationMode
45
+
46
+ logger = get_logger(__name__)
47
+
48
+
49
+ class ACPClientHandler(Client):
50
+ """Client handler that collects session updates and handles agent requests.
51
+
52
+ This implements the full ACP Client protocol including:
53
+ - Session update collection (text chunks, thoughts, tool calls)
54
+ - Filesystem operations (read/write files) via ExecutionEnvironment
55
+ - Terminal operations (create, output, kill, release) via ProcessManager
56
+ - Permission request handling via InputProvider
57
+
58
+ The handler accumulates session updates in an ACPSessionState instance,
59
+ allowing the ACPAgent to build the final response from streamed chunks.
60
+
61
+ Uses ExecutionEnvironment for all file and process operations, enabling
62
+ swappable backends (local, Docker, E2B, SSH, etc.).
63
+
64
+ The handler holds a reference to the parent ACPAgent, delegating env access
65
+ to ensure the env stays in sync when reassigned externally.
66
+ """
67
+
68
+ def __init__(
69
+ self,
70
+ agent: ACPAgent[Any],
71
+ state: ACPSessionState,
72
+ input_provider: InputProvider | None = None,
73
+ ) -> None:
74
+ self._agent = agent
75
+ self.state = state
76
+ self._input_provider = input_provider
77
+ self._update_event = asyncio.Event()
78
+ # Map ACP terminal IDs to process manager IDs
79
+ self._terminal_to_process: dict[str, str] = {}
80
+ # Copy tool confirmation mode from agent (can be updated via set_tool_confirmation_mode)
81
+ self.tool_confirmation_mode: ToolConfirmationMode = agent.tool_confirmation_mode
82
+
83
+ @property
84
+ def env(self) -> ExecutionEnvironment:
85
+ """Get execution environment for subprocess requests.
86
+
87
+ Uses the agent's client_env which handles subprocess file/terminal
88
+ operations. Falls back to agent's main env if not explicitly configured.
89
+ """
90
+ return self._agent.client_env
91
+
92
+ @property
93
+ def allow_file_operations(self) -> bool:
94
+ return self._agent.config.allow_file_operations
95
+
96
+ @property
97
+ def allow_terminal(self) -> bool:
98
+ return self._agent.config.allow_terminal
99
+
100
+ async def session_update(self, params: SessionNotification[Any]) -> None:
101
+ """Handle session update notifications from the agent."""
102
+ from agentpool.agents.acp_agent.acp_converters import acp_to_native_event
103
+
104
+ update = params.update
105
+ # Convert to native event and queue it
106
+ if native_event := acp_to_native_event(update):
107
+ self.state.events.append(native_event)
108
+ self._update_event.set()
109
+
110
+ async def request_permission( # noqa: PLR0911
111
+ self, params: RequestPermissionRequest
112
+ ) -> RequestPermissionResponse:
113
+ """Handle permission requests via InputProvider."""
114
+ name = params.tool_call.title or "operation"
115
+ logger.info("Permission requested", tool_name=name)
116
+
117
+ # Check tool_confirmation_mode FIRST, before any forwarding
118
+ # This ensures "bypass permissions" mode works even for nested ACP agents
119
+ if self.tool_confirmation_mode == "never" and params.options:
120
+ option_id = params.options[0].option_id
121
+ logger.debug("Auto-granting permission (tool_confirmation_mode=never)", tool_name=name)
122
+ return RequestPermissionResponse.allowed(option_id)
123
+
124
+ # Try callback second (forwards to parent session for nested ACP agents)
125
+ if self._agent.acp_permission_callback:
126
+ # return RequestPermissionResponse.allowed(option_id=params.options[0].option_id) # "acceptEdits" # noqa: E501
127
+ try:
128
+ logger.debug("Forwarding permission via callback", tool_name=name)
129
+ response = await self._agent.acp_permission_callback(params)
130
+ logger.debug(
131
+ "Permission response received", tool_name=name, outcome=response.outcome.outcome
132
+ )
133
+ except Exception:
134
+ logger.exception("Failed to forward permission via callback")
135
+ # Fall through to old logic
136
+ else:
137
+ return response
138
+
139
+ if self._input_provider:
140
+ ctx = self._agent.get_context() # Use the agent's NodeContext
141
+ # Create a dummy tool representation from ACP params
142
+ tool = Tool(callable=lambda: None, name=params.tool_call.tool_call_id, description=name)
143
+ # Extract arguments - ACP doesn't expose them in ToolCall
144
+ try:
145
+ result = await self._input_provider.get_tool_confirmation(ctx, tool=tool, args={})
146
+ # Map confirmation result to ACP response
147
+ if result == "allow":
148
+ option_id = params.options[0].option_id if params.options else "allow"
149
+ return RequestPermissionResponse.allowed(option_id)
150
+ if result == "skip":
151
+ return RequestPermissionResponse.denied()
152
+ return RequestPermissionResponse.denied() # abort_run
153
+
154
+ except Exception:
155
+ logger.exception("Failed to get permission via input provider")
156
+ return RequestPermissionResponse.denied()
157
+
158
+ logger.debug("Denying permission (no input provider)", tool_name=name)
159
+ return RequestPermissionResponse.denied()
160
+
161
+ async def read_text_file(self, params: ReadTextFileRequest) -> ReadTextFileResponse:
162
+ """Read text from file via ExecutionEnvironment filesystem."""
163
+ if not self.allow_file_operations:
164
+ raise RuntimeError("File operations not allowed")
165
+
166
+ fs = self.env.get_fs()
167
+ try:
168
+ content_bytes = await fs._cat_file(params.path)
169
+ content = content_bytes.decode("utf-8")
170
+ # Apply line filtering if requested
171
+ if params.line is not None or params.limit is not None:
172
+ lines = content.splitlines(keepends=True)
173
+ start_line = (params.line - 1) if params.line else 0
174
+ end_line = start_line + params.limit if params.limit else len(lines)
175
+ content = "".join(lines[start_line:end_line])
176
+
177
+ logger.debug("Read file", path=params.path, num_chars=len(content))
178
+ return ReadTextFileResponse(content=content)
179
+
180
+ except FileNotFoundError:
181
+ logger.exception("File not found", path=params.path)
182
+ raise
183
+ except Exception:
184
+ logger.exception("Failed to read file", path=params.path)
185
+ raise
186
+
187
+ async def write_text_file(self, params: WriteTextFileRequest) -> WriteTextFileResponse:
188
+ """Write text to file via ExecutionEnvironment filesystem."""
189
+ if not self.allow_file_operations:
190
+ raise RuntimeError("File operations not allowed")
191
+ fs = self.env.get_fs()
192
+ content_bytes = params.content.encode("utf-8")
193
+ parent = str(Path(params.path).parent)
194
+ try:
195
+ if parent and parent != ".": # Ensure parent directory exists
196
+ await fs._makedirs(parent, exist_ok=True)
197
+ await fs._pipe_file(params.path, content_bytes)
198
+ logger.debug("Wrote file", path=params.path, num_chars=len(params.content))
199
+ return WriteTextFileResponse()
200
+ except Exception:
201
+ logger.exception("Failed to write file", path=params.path)
202
+ raise
203
+
204
+ async def create_terminal(self, params: CreateTerminalRequest) -> CreateTerminalResponse:
205
+ """Create a new terminal session via ProcessManager."""
206
+ if not self.allow_terminal:
207
+ raise RuntimeError("Terminal operations not allowed")
208
+ try:
209
+ process_id = await self.env.process_manager.start_process(
210
+ command=params.command,
211
+ args=list(params.args) if params.args else None,
212
+ cwd=params.cwd,
213
+ env={var.name: var.value for var in params.env or []},
214
+ )
215
+ except Exception:
216
+ logger.exception("Failed to create terminal", command=params.command)
217
+ raise
218
+ else:
219
+ terminal_id = f"term_{uuid.uuid4().hex[:8]}"
220
+ self._terminal_to_process[terminal_id] = process_id
221
+ msg = "Created terminal"
222
+ logger.info(msg, terminal_id=terminal_id, process_id=process_id, cmd=params.command)
223
+ return CreateTerminalResponse(terminal_id=terminal_id)
224
+
225
+ async def terminal_output(self, params: TerminalOutputRequest) -> TerminalOutputResponse:
226
+ """Get output from terminal via ProcessManager."""
227
+ if not self.allow_terminal:
228
+ raise RuntimeError("Terminal operations not allowed")
229
+
230
+ terminal_id = params.terminal_id
231
+ if terminal_id not in self._terminal_to_process:
232
+ raise ValueError(f"Terminal {terminal_id} not found")
233
+
234
+ process_id = self._terminal_to_process[terminal_id]
235
+ proc_output = await self.env.process_manager.get_output(process_id)
236
+ output = proc_output.combined or proc_output.stdout or ""
237
+ return TerminalOutputResponse(output=output, truncated=proc_output.truncated)
238
+
239
+ async def wait_for_terminal_exit(
240
+ self, params: WaitForTerminalExitRequest
241
+ ) -> WaitForTerminalExitResponse:
242
+ """Wait for terminal process to exit via ProcessManager."""
243
+ if not self.allow_terminal:
244
+ raise RuntimeError("Terminal operations not allowed")
245
+
246
+ terminal_id = params.terminal_id
247
+ if terminal_id not in self._terminal_to_process:
248
+ raise ValueError(f"Terminal {terminal_id} not found")
249
+
250
+ process_id = self._terminal_to_process[terminal_id]
251
+ exit_code = await self.env.process_manager.wait_for_exit(process_id)
252
+ logger.debug("Terminal exited", terminal_id=terminal_id, exit_code=exit_code)
253
+ return WaitForTerminalExitResponse(exit_code=exit_code)
254
+
255
+ async def kill_terminal(
256
+ self, params: KillTerminalCommandRequest
257
+ ) -> KillTerminalCommandResponse:
258
+ """Kill terminal process via ProcessManager."""
259
+ if not self.allow_terminal:
260
+ raise RuntimeError("Terminal operations not allowed")
261
+
262
+ terminal_id = params.terminal_id
263
+ if terminal_id not in self._terminal_to_process:
264
+ raise ValueError(f"Terminal {terminal_id} not found")
265
+
266
+ process_id = self._terminal_to_process[terminal_id]
267
+ await self.env.process_manager.kill_process(process_id)
268
+ logger.info("Killed terminal", terminal_id=terminal_id)
269
+ return KillTerminalCommandResponse()
270
+
271
+ async def release_terminal(self, params: ReleaseTerminalRequest) -> ReleaseTerminalResponse:
272
+ """Release terminal resources via ProcessManager."""
273
+ if not self.allow_terminal:
274
+ raise RuntimeError("Terminal operations not allowed")
275
+
276
+ terminal_id = params.terminal_id
277
+ if terminal_id not in self._terminal_to_process:
278
+ raise ValueError(f"Terminal {terminal_id} not found")
279
+ process_id = self._terminal_to_process[terminal_id]
280
+ await self.env.process_manager.release_process(process_id)
281
+ del self._terminal_to_process[terminal_id]
282
+ logger.info("Released terminal", terminal_id=terminal_id)
283
+ return ReleaseTerminalResponse()
284
+
285
+ async def cleanup(self) -> None:
286
+ """Clean up all resources."""
287
+ for terminal_id, process_id in list(self._terminal_to_process.items()):
288
+ try:
289
+ await self.env.process_manager.release_process(process_id)
290
+ except Exception:
291
+ logger.exception("Error cleaning up terminal", terminal_id=terminal_id)
292
+
293
+ self._terminal_to_process.clear()
294
+
295
+ async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
296
+ """Handle extension methods."""
297
+ logger.debug("Extension method called", method=method)
298
+ return {"ok": True, "method": method}
299
+
300
+ async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
301
+ """Handle extension notifications."""
302
+ logger.debug("Extension notification", method=method)
303
+
304
+
305
+ if __name__ == "__main__":
306
+ from agentpool.agents.acp_agent import ACPAgent
307
+
308
+ async def main() -> None:
309
+ """Demo: Basic call to an ACP agent."""
310
+ args = ["run", "agentpool", "serve-acp", "--model-provider", "openai"]
311
+ cwd = str(Path.cwd())
312
+ async with ACPAgent(command="uv", args=args, cwd=cwd, event_handlers=["detailed"]) as agent:
313
+ print("Response (streaming): ", end="", flush=True)
314
+ async for chunk in agent.run_stream("Say hello briefly."):
315
+ print(chunk, end="", flush=True)
316
+
317
+ anyio.run(main)
@@ -0,0 +1,44 @@
1
+ """ACP Agent - MessageNode wrapping an external ACP subprocess."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field as dataclass_field
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from agentpool.log import get_logger
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from acp.schema import SessionModelState, SessionModeState
13
+ from agentpool.agents.events import RichAgentStreamEvent
14
+
15
+ logger = get_logger(__name__)
16
+
17
+ PROTOCOL_VERSION = 1
18
+
19
+
20
+ @dataclass
21
+ class ACPSessionState:
22
+ """Tracks state of an ACP session."""
23
+
24
+ session_id: str
25
+ """The session ID from the ACP server."""
26
+
27
+ events: list[RichAgentStreamEvent[Any]] = dataclass_field(default_factory=list)
28
+ """Queue of native events converted from ACP updates."""
29
+
30
+ current_model_id: str | None = None
31
+ """Current model ID from session state."""
32
+
33
+ models: SessionModelState | None = None
34
+ """Full model state including available models from nested ACP agent."""
35
+
36
+ modes: SessionModeState | None = None
37
+ """Full mode state including available modes from nested ACP agent."""
38
+
39
+ current_mode_id: str | None = None
40
+ """Current mode ID."""
41
+
42
+ def clear(self) -> None:
43
+ self.events.clear()
44
+ # Note: Don't clear current_model_id or models - those persist across prompts