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,1021 @@
1
+ """ClaudeCodeAgent - Native Claude Agent SDK integration.
2
+
3
+ This module provides an agent implementation that wraps the Claude Agent SDK's
4
+ ClaudeSDKClient for native integration with agentpool.
5
+
6
+ The ClaudeCodeAgent acts as a client to the Claude Code CLI, enabling:
7
+ - Bidirectional streaming communication
8
+ - Tool permission handling via callbacks
9
+ - Integration with agentpool's event system
10
+
11
+ Example:
12
+ ```python
13
+ async with ClaudeCodeAgent(
14
+ name="claude_coder",
15
+ cwd="/path/to/project",
16
+ allowed_tools=["Read", "Write", "Bash"],
17
+ ) as agent:
18
+ async for event in agent.run_stream("Write a hello world program"):
19
+ print(event)
20
+ ```
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import asyncio
26
+ from decimal import Decimal
27
+ from typing import TYPE_CHECKING, Any, Self, cast
28
+ import uuid
29
+
30
+ import anyio
31
+ from pydantic import TypeAdapter
32
+ from pydantic_ai import (
33
+ ModelRequest,
34
+ ModelResponse,
35
+ PartDeltaEvent,
36
+ PartEndEvent,
37
+ PartStartEvent,
38
+ RunUsage,
39
+ TextPart,
40
+ TextPartDelta,
41
+ ThinkingPart,
42
+ ThinkingPartDelta,
43
+ ToolCallPart,
44
+ ToolReturnPart,
45
+ UserPromptPart,
46
+ )
47
+
48
+ from agentpool.agents.base_agent import BaseAgent
49
+ from agentpool.agents.claude_code_agent.converters import claude_message_to_events
50
+ from agentpool.agents.events import (
51
+ RunErrorEvent,
52
+ RunStartedEvent,
53
+ StreamCompleteEvent,
54
+ ToolCallCompleteEvent,
55
+ ToolCallStartEvent,
56
+ )
57
+ from agentpool.log import get_logger
58
+ from agentpool.messaging import ChatMessage
59
+ from agentpool.messaging.messages import TokenCost
60
+ from agentpool.messaging.processing import prepare_prompts
61
+ from agentpool.models.claude_code_agents import ClaudeCodeAgentConfig
62
+ from agentpool.utils.streams import merge_queue_into_iterator
63
+
64
+
65
+ if TYPE_CHECKING:
66
+ from collections.abc import AsyncIterator, Sequence
67
+ from types import TracebackType
68
+
69
+ from claude_agent_sdk import (
70
+ ClaudeAgentOptions,
71
+ ClaudeSDKClient,
72
+ McpServerConfig,
73
+ PermissionMode,
74
+ PermissionResult,
75
+ ToolPermissionContext,
76
+ ToolUseBlock,
77
+ )
78
+ from evented.configs import EventConfig
79
+ from exxec import ExecutionEnvironment
80
+ from toprompt import AnyPromptType
81
+
82
+ from agentpool.agents.context import AgentContext
83
+ from agentpool.agents.events import RichAgentStreamEvent
84
+ from agentpool.common_types import (
85
+ BuiltinEventHandlerType,
86
+ IndividualEventHandler,
87
+ PromptCompatible,
88
+ )
89
+ from agentpool.delegation import AgentPool
90
+ from agentpool.mcp_server.tool_bridge import ToolManagerBridge
91
+ from agentpool.messaging import MessageHistory
92
+ from agentpool.talk.stats import MessageStats
93
+ from agentpool.ui.base import InputProvider
94
+ from agentpool_config.mcp_server import MCPServerConfig
95
+ from agentpool_config.nodes import ToolConfirmationMode
96
+
97
+
98
+ logger = get_logger(__name__)
99
+
100
+
101
+ class ClaudeCodeAgent[TDeps = None, TResult = str](BaseAgent[TDeps, TResult]):
102
+ """Agent wrapping Claude Agent SDK's ClaudeSDKClient.
103
+
104
+ This provides native integration with Claude Code, enabling:
105
+ - Bidirectional streaming for interactive conversations
106
+ - Tool permission handling via can_use_tool callback
107
+ - Full access to Claude Code's capabilities (file ops, terminals, etc.)
108
+
109
+ The agent manages:
110
+ - ClaudeSDKClient lifecycle (connect on enter, disconnect on exit)
111
+ - Event conversion from Claude SDK to agentpool events
112
+ - Tool confirmation via input provider
113
+ """
114
+
115
+ def __init__(
116
+ self,
117
+ *,
118
+ config: ClaudeCodeAgentConfig | None = None,
119
+ name: str | None = None,
120
+ description: str | None = None,
121
+ display_name: str | None = None,
122
+ cwd: str | None = None,
123
+ allowed_tools: list[str] | None = None,
124
+ disallowed_tools: list[str] | None = None,
125
+ system_prompt: str | Sequence[str] | None = None,
126
+ include_builtin_system_prompt: bool = True,
127
+ model: str | None = None,
128
+ max_turns: int | None = None,
129
+ max_thinking_tokens: int | None = None,
130
+ permission_mode: PermissionMode | None = None,
131
+ mcp_servers: Sequence[MCPServerConfig] | None = None,
132
+ environment: dict[str, str] | None = None,
133
+ add_dir: list[str] | None = None,
134
+ builtin_tools: list[str] | None = None,
135
+ fallback_model: str | None = None,
136
+ dangerously_skip_permissions: bool = False,
137
+ env: ExecutionEnvironment | None = None,
138
+ input_provider: InputProvider | None = None,
139
+ agent_pool: AgentPool[Any] | None = None,
140
+ enable_logging: bool = True,
141
+ event_configs: Sequence[EventConfig] | None = None,
142
+ event_handlers: Sequence[IndividualEventHandler | BuiltinEventHandlerType] | None = None,
143
+ tool_confirmation_mode: ToolConfirmationMode = "always",
144
+ output_type: type[TResult] | None = None,
145
+ ) -> None:
146
+ """Initialize ClaudeCodeAgent.
147
+
148
+ Args:
149
+ config: Configuration object (alternative to individual kwargs)
150
+ name: Agent name
151
+ description: Agent description
152
+ display_name: Display name for UI
153
+ cwd: Working directory for Claude Code
154
+ allowed_tools: List of allowed tool names
155
+ disallowed_tools: List of disallowed tool names
156
+ system_prompt: System prompt - string or list (appended to builtin by default)
157
+ include_builtin_system_prompt: If True, the builtin system prompt is included.
158
+ model: Model to use (e.g., "claude-sonnet-4-5")
159
+ max_turns: Maximum conversation turns
160
+ max_thinking_tokens: Max tokens for extended thinking
161
+ permission_mode: Permission mode ("default", "acceptEdits", "plan", "bypassPermissions")
162
+ mcp_servers: External MCP servers to connect to (internal format, converted at runtime)
163
+ environment: Environment variables for the agent process
164
+ add_dir: Additional directories to allow tool access to
165
+ builtin_tools: Available tools from Claude Code's built-in set (empty list disables all)
166
+ fallback_model: Fallback model when default is overloaded
167
+ dangerously_skip_permissions: Bypass all permission checks (sandboxed only)
168
+ env: Execution environment
169
+ input_provider: Provider for user input/confirmations
170
+ agent_pool: Agent pool for multi-agent coordination
171
+ enable_logging: Whether to enable logging
172
+ event_configs: Event configuration
173
+ event_handlers: Event handlers for streaming events
174
+ tool_confirmation_mode: Tool confirmation behavior
175
+ output_type: Type for structured output (uses JSON schema)
176
+ """
177
+ from agentpool.agents.sys_prompts import SystemPrompts
178
+
179
+ # Build config from kwargs if not provided
180
+ if config is None:
181
+ config = ClaudeCodeAgentConfig(
182
+ name=name or "claude_code",
183
+ description=description,
184
+ display_name=display_name,
185
+ cwd=cwd,
186
+ model=model,
187
+ allowed_tools=allowed_tools,
188
+ disallowed_tools=disallowed_tools,
189
+ system_prompt=system_prompt,
190
+ include_builtin_system_prompt=include_builtin_system_prompt,
191
+ max_turns=max_turns,
192
+ max_thinking_tokens=max_thinking_tokens,
193
+ permission_mode=permission_mode,
194
+ mcp_servers=list(mcp_servers) if mcp_servers else [],
195
+ env=environment,
196
+ add_dir=add_dir,
197
+ builtin_tools=builtin_tools,
198
+ fallback_model=fallback_model,
199
+ dangerously_skip_permissions=dangerously_skip_permissions,
200
+ )
201
+
202
+ super().__init__(
203
+ name=name or config.name or "claude_code",
204
+ description=description or config.description,
205
+ display_name=display_name or config.display_name,
206
+ agent_pool=agent_pool,
207
+ enable_logging=enable_logging,
208
+ event_configs=event_configs or list(config.triggers),
209
+ env=env,
210
+ input_provider=input_provider,
211
+ output_type=output_type or str, # type: ignore[arg-type]
212
+ tool_confirmation_mode=tool_confirmation_mode,
213
+ event_handlers=event_handlers,
214
+ )
215
+
216
+ self._config = config
217
+ self._cwd = cwd or config.cwd
218
+ self._allowed_tools = allowed_tools or config.allowed_tools
219
+ self._disallowed_tools = disallowed_tools or config.disallowed_tools
220
+ self._include_builtin_system_prompt = (
221
+ include_builtin_system_prompt and config.include_builtin_system_prompt
222
+ )
223
+
224
+ # Initialize SystemPrompts manager
225
+ # Normalize system_prompt to a list
226
+ all_prompts: list[AnyPromptType] = []
227
+ prompt_source = system_prompt if system_prompt is not None else config.system_prompt
228
+ if prompt_source is not None:
229
+ if isinstance(prompt_source, str):
230
+ all_prompts.append(prompt_source)
231
+ else:
232
+ all_prompts.extend(prompt_source)
233
+ prompt_manager = agent_pool.manifest.prompt_manager if agent_pool else None
234
+ self.sys_prompts = SystemPrompts(all_prompts, prompt_manager=prompt_manager)
235
+ self._model = model or config.model
236
+ self._max_turns = max_turns or config.max_turns
237
+ self._max_thinking_tokens = max_thinking_tokens or config.max_thinking_tokens
238
+ self._permission_mode: PermissionMode | None = permission_mode or config.permission_mode
239
+ self._external_mcp_servers = list(mcp_servers) if mcp_servers else config.get_mcp_servers()
240
+ self._environment = environment or config.env
241
+ self._add_dir = add_dir or config.add_dir
242
+ self._builtin_tools = builtin_tools if builtin_tools is not None else config.builtin_tools
243
+ self._fallback_model = fallback_model or config.fallback_model
244
+ self._dangerously_skip_permissions = (
245
+ dangerously_skip_permissions or config.dangerously_skip_permissions
246
+ )
247
+
248
+ # Client state
249
+ self._client: ClaudeSDKClient | None = None
250
+ self._current_model: str | None = self._model
251
+ self.deps_type = type(None)
252
+
253
+ # ToolBridge state for exposing toolsets via MCP
254
+ self._tool_bridge: ToolManagerBridge | None = None
255
+ self._owns_bridge = False # Track if we created the bridge (for cleanup)
256
+ self._mcp_servers: dict[str, McpServerConfig] = {} # Claude SDK MCP server configs
257
+
258
+ def get_context(self, data: Any = None) -> AgentContext:
259
+ """Create a new context for this agent.
260
+
261
+ Args:
262
+ data: Optional custom data to attach to the context
263
+
264
+ Returns:
265
+ A new AgentContext instance
266
+ """
267
+ from agentpool.agents import AgentContext
268
+ from agentpool.models import AgentsManifest
269
+
270
+ defn = self.agent_pool.manifest if self.agent_pool else AgentsManifest()
271
+ return AgentContext(
272
+ node=self, pool=self.agent_pool, config=self._config, definition=defn, data=data
273
+ )
274
+
275
+ def _convert_mcp_servers_to_sdk_format(self) -> dict[str, McpServerConfig]:
276
+ """Convert internal MCPServerConfig to Claude SDK format.
277
+
278
+ Returns:
279
+ Dict mapping server names to SDK-compatible config dicts
280
+ """
281
+ from claude_agent_sdk import McpServerConfig
282
+
283
+ from agentpool_config.mcp_server import (
284
+ SSEMCPServerConfig,
285
+ StdioMCPServerConfig,
286
+ StreamableHTTPMCPServerConfig,
287
+ )
288
+
289
+ result: dict[str, McpServerConfig] = {}
290
+
291
+ for idx, server in enumerate(self._external_mcp_servers):
292
+ # Determine server name
293
+ if server.name:
294
+ name = server.name
295
+ elif isinstance(server, StdioMCPServerConfig) and server.args:
296
+ name = server.args[-1].split("/")[-1].split("@")[0]
297
+ elif isinstance(server, StdioMCPServerConfig):
298
+ name = server.command
299
+ elif isinstance(server, SSEMCPServerConfig | StreamableHTTPMCPServerConfig):
300
+ from urllib.parse import urlparse
301
+
302
+ name = urlparse(str(server.url)).hostname or f"server_{idx}"
303
+ else:
304
+ name = f"server_{idx}"
305
+
306
+ # Build SDK-compatible config
307
+ config: dict[str, Any]
308
+ match server:
309
+ case StdioMCPServerConfig(command=command, args=args):
310
+ config = {"type": "stdio", "command": command, "args": args}
311
+ if server.env:
312
+ config["env"] = server.get_env_vars()
313
+ case SSEMCPServerConfig(url=url):
314
+ config = {"type": "sse", "url": str(url)}
315
+ if server.headers:
316
+ config["headers"] = server.headers
317
+ case StreamableHTTPMCPServerConfig(url=url):
318
+ config = {"type": "http", "url": str(url)}
319
+ if server.headers:
320
+ config["headers"] = server.headers
321
+
322
+ result[name] = cast(McpServerConfig, config)
323
+
324
+ return result
325
+
326
+ async def _setup_toolsets(self) -> None:
327
+ """Initialize toolsets from config and create bridge if needed.
328
+
329
+ Creates providers from toolset configs, adds them to the tool manager,
330
+ and starts an MCP bridge to expose them to Claude Code via the SDK's
331
+ native MCP support. Also converts external MCP servers to SDK format.
332
+ """
333
+ from agentpool.mcp_server.tool_bridge import BridgeConfig, ToolManagerBridge
334
+
335
+ # Convert external MCP servers to SDK format first
336
+ if self._external_mcp_servers:
337
+ external_configs = self._convert_mcp_servers_to_sdk_format()
338
+ self._mcp_servers.update(external_configs)
339
+ self.log.info("External MCP servers configured", server_count=len(external_configs))
340
+
341
+ if not self._config.toolsets:
342
+ return
343
+
344
+ # Create providers from toolset configs and add to tool manager
345
+ for toolset_config in self._config.toolsets:
346
+ provider = toolset_config.get_provider()
347
+ self.tools.add_provider(provider)
348
+
349
+ # Auto-create bridge to expose tools via MCP
350
+ config = BridgeConfig(
351
+ transport="streamable-http", server_name=f"agentpool-{self.name}-tools"
352
+ )
353
+ self._tool_bridge = ToolManagerBridge(node=self, config=config)
354
+ await self._tool_bridge.start()
355
+ self._owns_bridge = True
356
+
357
+ # Get Claude SDK-compatible MCP config and merge into our servers dict
358
+ mcp_config = self._tool_bridge.get_claude_mcp_server_config()
359
+ self._mcp_servers.update(mcp_config)
360
+ self.log.info("Toolsets initialized", toolset_count=len(self._config.toolsets))
361
+
362
+ async def add_tool_bridge(self, bridge: ToolManagerBridge) -> None:
363
+ """Add an external tool bridge to expose its tools via MCP.
364
+
365
+ The bridge must already be started. Its MCP server config will be
366
+ added to the Claude SDK options. Use this for bridges created externally
367
+ (e.g., from AgentPool). For toolsets defined in config, bridges
368
+ are created automatically.
369
+
370
+ Args:
371
+ bridge: Started ToolManagerBridge instance
372
+ """
373
+ if self._tool_bridge is None: # Don't replace our own bridge
374
+ self._tool_bridge = bridge
375
+
376
+ # Get Claude SDK-compatible config and merge
377
+ mcp_config = bridge.get_claude_mcp_server_config()
378
+ self._mcp_servers.update(mcp_config)
379
+ self.log.info("Added external tool bridge", server_name=bridge.config.server_name)
380
+
381
+ async def _cleanup_bridge(self) -> None:
382
+ """Clean up tool bridge resources."""
383
+ if self._tool_bridge and self._owns_bridge:
384
+ await self._tool_bridge.stop()
385
+ self._tool_bridge = None
386
+ self._owns_bridge = False
387
+ self._mcp_servers.clear()
388
+
389
+ @property
390
+ def model_name(self) -> str | None:
391
+ """Get the model name."""
392
+ return self._current_model
393
+
394
+ def _build_options(self, *, formatted_system_prompt: str | None = None) -> ClaudeAgentOptions:
395
+ """Build ClaudeAgentOptions from runtime state.
396
+
397
+ Args:
398
+ formatted_system_prompt: Pre-formatted system prompt from SystemPrompts manager
399
+ """
400
+ from claude_agent_sdk import ClaudeAgentOptions
401
+ from claude_agent_sdk.types import SystemPromptPreset
402
+
403
+ # Build system prompt value
404
+ system_prompt: str | SystemPromptPreset | None = None
405
+ if formatted_system_prompt:
406
+ if self._include_builtin_system_prompt:
407
+ # Use SystemPromptPreset to append to builtin prompt
408
+ system_prompt = SystemPromptPreset(
409
+ type="preset",
410
+ preset="claude_code",
411
+ append=formatted_system_prompt,
412
+ )
413
+ else:
414
+ system_prompt = formatted_system_prompt
415
+
416
+ # Determine effective permission mode
417
+ permission_mode = self._permission_mode
418
+ if self._dangerously_skip_permissions and not permission_mode:
419
+ permission_mode = "bypassPermissions"
420
+
421
+ # Determine can_use_tool callback
422
+ bypass = permission_mode == "bypassPermissions" or self._dangerously_skip_permissions
423
+ can_use_tool = (
424
+ self._can_use_tool if self.tool_confirmation_mode != "never" and not bypass else None
425
+ )
426
+
427
+ # Build structured output format if needed
428
+ output_format: dict[str, Any] | None = None
429
+ if self._output_type is not str:
430
+ adapter = TypeAdapter(self._output_type)
431
+ schema = adapter.json_schema()
432
+ output_format = {"type": "json_schema", "schema": schema}
433
+
434
+ return ClaudeAgentOptions(
435
+ cwd=self._cwd,
436
+ allowed_tools=self._allowed_tools or [],
437
+ disallowed_tools=self._disallowed_tools or [],
438
+ system_prompt=system_prompt,
439
+ model=self._model,
440
+ max_turns=self._max_turns,
441
+ max_thinking_tokens=self._max_thinking_tokens,
442
+ permission_mode=permission_mode,
443
+ env=self._environment or {},
444
+ add_dirs=self._add_dir or [], # type: ignore[arg-type] # SDK uses list not Sequence
445
+ tools=self._builtin_tools,
446
+ fallback_model=self._fallback_model,
447
+ can_use_tool=can_use_tool,
448
+ output_format=output_format,
449
+ mcp_servers=self._mcp_servers or {},
450
+ include_partial_messages=True,
451
+ )
452
+
453
+ async def _can_use_tool( # noqa: PLR0911
454
+ self,
455
+ tool_name: str,
456
+ input_data: dict[str, Any],
457
+ context: ToolPermissionContext,
458
+ ) -> PermissionResult:
459
+ """Handle tool permission requests.
460
+
461
+ Args:
462
+ tool_name: Name of the tool being called
463
+ input_data: Tool input arguments
464
+ context: Permission context with suggestions
465
+
466
+ Returns:
467
+ PermissionResult indicating allow or deny
468
+ """
469
+ from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
470
+
471
+ from agentpool.tools.base import Tool
472
+
473
+ # Auto-grant if confirmation mode is "never"
474
+ if self.tool_confirmation_mode == "never":
475
+ return PermissionResultAllow()
476
+
477
+ # Auto-grant tools from our own bridge - they already show ToolCallStartEvent in UI
478
+ # Bridge tools are named like: mcp__agentpool-{agent_name}-tools__{tool}
479
+ if self._tool_bridge:
480
+ bridge_prefix = f"mcp__{self._tool_bridge.config.server_name}__"
481
+ if tool_name.startswith(bridge_prefix):
482
+ return PermissionResultAllow()
483
+
484
+ # Use input provider if available
485
+ if self._input_provider:
486
+ # Create a dummy Tool for the confirmation dialog
487
+ desc = f"Claude Code tool: {tool_name}"
488
+ tool = Tool(callable=lambda: None, name=tool_name, description=desc)
489
+ result = await self._input_provider.get_tool_confirmation(
490
+ context=self.get_context(),
491
+ tool=tool,
492
+ args=input_data,
493
+ )
494
+
495
+ match result:
496
+ case "allow":
497
+ return PermissionResultAllow()
498
+ case "skip":
499
+ return PermissionResultDeny(message="User skipped tool execution")
500
+ case "abort_run" | "abort_chain":
501
+ return PermissionResultDeny(message="User aborted execution", interrupt=True)
502
+ case _:
503
+ return PermissionResultDeny(message="Unknown confirmation result")
504
+
505
+ # Default: deny if no input provider
506
+ return PermissionResultDeny(message="No input provider configured")
507
+
508
+ async def __aenter__(self) -> Self:
509
+ """Connect to Claude Code."""
510
+ from claude_agent_sdk import ClaudeSDKClient
511
+
512
+ await super().__aenter__()
513
+ await self._setup_toolsets() # Setup toolsets before building opts (they add MCP servers)
514
+ formatted_prompt = await self.sys_prompts.format_system_prompt(self)
515
+ options = self._build_options(formatted_system_prompt=formatted_prompt)
516
+ self._client = ClaudeSDKClient(options=options)
517
+ await self._client.connect()
518
+ self.log.info("Claude Code client connected")
519
+ return self
520
+
521
+ async def __aexit__(
522
+ self,
523
+ exc_type: type[BaseException] | None,
524
+ exc_val: BaseException | None,
525
+ exc_tb: TracebackType | None,
526
+ ) -> None:
527
+ """Disconnect from Claude Code."""
528
+ # Clean up tool bridge first
529
+ await self._cleanup_bridge()
530
+ if self._client:
531
+ try:
532
+ await self._client.disconnect()
533
+ self.log.info("Claude Code client disconnected")
534
+ except Exception:
535
+ self.log.exception("Error disconnecting Claude Code client")
536
+ self._client = None
537
+ await super().__aexit__(exc_type, exc_val, exc_tb)
538
+
539
+ async def run(
540
+ self,
541
+ *prompts: PromptCompatible,
542
+ message_id: str | None = None,
543
+ input_provider: InputProvider | None = None,
544
+ message_history: MessageHistory | None = None,
545
+ ) -> ChatMessage[TResult]:
546
+ """Execute prompt against Claude Code.
547
+
548
+ Args:
549
+ prompts: Prompts to send
550
+ message_id: Optional message ID for the returned message
551
+ input_provider: Optional input provider for permission requests
552
+ message_history: Optional MessageHistory to use instead of agent's own
553
+
554
+ Returns:
555
+ ChatMessage containing the agent's response
556
+ """
557
+ final_message: ChatMessage[TResult] | None = None
558
+ async for event in self.run_stream(
559
+ *prompts,
560
+ message_id=message_id,
561
+ input_provider=input_provider,
562
+ message_history=message_history,
563
+ ):
564
+ if isinstance(event, StreamCompleteEvent):
565
+ final_message = event.message
566
+
567
+ if final_message is None:
568
+ msg = "No final message received from stream"
569
+ raise RuntimeError(msg)
570
+
571
+ return final_message
572
+
573
+ async def run_stream( # noqa: PLR0915
574
+ self,
575
+ *prompts: PromptCompatible,
576
+ message_id: str | None = None,
577
+ input_provider: InputProvider | None = None,
578
+ message_history: MessageHistory | None = None,
579
+ ) -> AsyncIterator[RichAgentStreamEvent[TResult]]:
580
+ """Stream events from Claude Code execution.
581
+
582
+ Args:
583
+ prompts: Prompts to send
584
+ message_id: Optional message ID for the final message
585
+ input_provider: Optional input provider for permission requests
586
+ message_history: Optional MessageHistory to use instead of agent's own
587
+
588
+ Yields:
589
+ RichAgentStreamEvent instances during execution
590
+ """
591
+ from claude_agent_sdk import (
592
+ AssistantMessage,
593
+ Message,
594
+ ResultMessage,
595
+ TextBlock,
596
+ ThinkingBlock,
597
+ ToolResultBlock,
598
+ ToolUseBlock as ToolUseBlockType,
599
+ UserMessage,
600
+ )
601
+ from claude_agent_sdk.types import StreamEvent
602
+
603
+ # Reset cancellation state
604
+ self._cancelled = False
605
+ self._current_stream_task = asyncio.current_task()
606
+
607
+ # Update input provider if provided
608
+ if input_provider is not None:
609
+ self._input_provider = input_provider
610
+
611
+ if not self._client:
612
+ msg = "Agent not initialized - use async context manager"
613
+ raise RuntimeError(msg)
614
+
615
+ conversation = message_history if message_history is not None else self.conversation
616
+ # Prepare prompts
617
+ user_msg, processed_prompts, _original_message = await prepare_prompts(*prompts)
618
+ # Get pending parts from conversation (staged content)
619
+ pending_parts = conversation.get_pending_parts()
620
+ # Combine pending parts with new prompts, then join into single string for Claude SDK
621
+ all_parts = [*pending_parts, *processed_prompts]
622
+ prompt_text = " ".join(str(p) for p in all_parts)
623
+ run_id = str(uuid.uuid4())
624
+ # Emit run started
625
+ run_started = RunStartedEvent(
626
+ thread_id=self.conversation_id,
627
+ run_id=run_id,
628
+ agent_name=self.name,
629
+ )
630
+ for handler in self.event_handler._wrapped_handlers:
631
+ await handler(None, run_started)
632
+ yield run_started
633
+ request = ModelRequest(parts=[UserPromptPart(content=prompt_text)])
634
+ model_messages: list[ModelResponse | ModelRequest] = [request]
635
+ current_response_parts: list[TextPart | ThinkingPart | ToolCallPart] = []
636
+ text_chunks: list[str] = []
637
+ pending_tool_calls: dict[str, ToolUseBlock] = {}
638
+
639
+ try:
640
+ await self._client.query(prompt_text)
641
+ # Merge SDK messages with event queue for real-time tool event streaming
642
+ async with merge_queue_into_iterator(
643
+ self._client.receive_response(), self._event_queue
644
+ ) as merged_events:
645
+ async for event_or_message in merged_events:
646
+ # Check if it's a queued event (from tools via EventEmitter)
647
+ if not isinstance(event_or_message, Message):
648
+ # It's an event from the queue - yield it immediately
649
+ for handler in self.event_handler._wrapped_handlers:
650
+ await handler(None, event_or_message)
651
+ yield event_or_message
652
+ continue
653
+
654
+ message = event_or_message
655
+ # Process assistant messages - extract parts incrementally
656
+ if isinstance(message, AssistantMessage):
657
+ # Update model name from first assistant message
658
+ if message.model:
659
+ self._current_model = message.model
660
+ for block in message.content:
661
+ match block:
662
+ case TextBlock(text=text):
663
+ text_chunks.append(text)
664
+ current_response_parts.append(TextPart(content=text))
665
+ case ThinkingBlock(thinking=thinking):
666
+ current_response_parts.append(ThinkingPart(content=thinking))
667
+ case ToolUseBlockType(id=tc_id, name=name, input=input_data):
668
+ pending_tool_calls[tc_id] = block
669
+ current_response_parts.append(
670
+ ToolCallPart(
671
+ tool_name=name, args=input_data, tool_call_id=tc_id
672
+ )
673
+ )
674
+ # Emit ToolCallStartEvent with rich display info
675
+ from agentpool.agents.claude_code_agent.converters import (
676
+ derive_rich_tool_info,
677
+ )
678
+
679
+ rich_info = derive_rich_tool_info(name, input_data)
680
+ tool_start_event = ToolCallStartEvent(
681
+ tool_call_id=tc_id,
682
+ tool_name=name,
683
+ title=rich_info.title,
684
+ kind=rich_info.kind,
685
+ locations=rich_info.locations,
686
+ content=rich_info.content,
687
+ raw_input=input_data,
688
+ )
689
+ for handler in self.event_handler._wrapped_handlers:
690
+ await handler(None, tool_start_event)
691
+ yield tool_start_event
692
+ case ToolResultBlock(tool_use_id=tc_id, content=content):
693
+ # Tool result received - flush response parts and add request
694
+ if current_response_parts:
695
+ response = ModelResponse(parts=current_response_parts)
696
+ model_messages.append(response)
697
+ current_response_parts = []
698
+
699
+ # Get tool name from pending calls
700
+ tool_use = pending_tool_calls.pop(tc_id, None)
701
+ tool_name = tool_use.name if tool_use else "unknown"
702
+ tool_input = tool_use.input if tool_use else {}
703
+ tool_done_event = ToolCallCompleteEvent(
704
+ tool_name=tool_name,
705
+ tool_call_id=tc_id,
706
+ tool_input=tool_input,
707
+ tool_result=content,
708
+ agent_name=self.name,
709
+ message_id="",
710
+ )
711
+ for handler in self.event_handler._wrapped_handlers:
712
+ await handler(None, tool_done_event)
713
+ yield tool_done_event
714
+
715
+ # Add tool return as ModelRequest
716
+ part = ToolReturnPart(
717
+ tool_name=tool_name, content=content, tool_call_id=tc_id
718
+ )
719
+ model_messages.append(ModelRequest(parts=[part]))
720
+
721
+ # Process user messages - may contain tool results
722
+ elif isinstance(message, UserMessage):
723
+ user_content = message.content
724
+ user_blocks = (
725
+ [user_content] if isinstance(user_content, str) else user_content
726
+ )
727
+ for user_block in user_blocks:
728
+ if isinstance(user_block, ToolResultBlock):
729
+ tc_id = user_block.tool_use_id
730
+ result_content = user_block.content
731
+
732
+ # Flush response parts
733
+ if current_response_parts:
734
+ model_messages.append(
735
+ ModelResponse(parts=current_response_parts)
736
+ )
737
+ current_response_parts = []
738
+
739
+ # Get tool name from pending calls
740
+ tool_use = pending_tool_calls.pop(tc_id, None)
741
+ tool_name = tool_use.name if tool_use else "unknown"
742
+ tool_input = tool_use.input if tool_use else {}
743
+ # Emit ToolCallCompleteEvent
744
+ tool_complete_event = ToolCallCompleteEvent(
745
+ tool_name=tool_name,
746
+ tool_call_id=tc_id,
747
+ tool_input=tool_input,
748
+ tool_result=result_content,
749
+ agent_name=self.name,
750
+ message_id="",
751
+ )
752
+ for handler in self.event_handler._wrapped_handlers:
753
+ await handler(None, tool_complete_event)
754
+ yield tool_complete_event
755
+ # Add tool return as ModelRequest
756
+ part = ToolReturnPart(
757
+ tool_name=tool_name,
758
+ content=result_content,
759
+ tool_call_id=tc_id,
760
+ )
761
+ model_messages.append(ModelRequest(parts=[part]))
762
+
763
+ # Handle StreamEvent for real-time streaming
764
+ elif isinstance(message, StreamEvent):
765
+ event_data = message.event
766
+ event_type = event_data.get("type")
767
+ index = event_data.get("index", 0)
768
+
769
+ # Handle content_block_start events
770
+ if event_type == "content_block_start":
771
+ content_block = event_data.get("content_block", {})
772
+ block_type = content_block.get("type")
773
+
774
+ if block_type == "text":
775
+ start_event = PartStartEvent(index=index, part=TextPart(content=""))
776
+ for handler in self.event_handler._wrapped_handlers:
777
+ await handler(None, start_event)
778
+ yield start_event
779
+
780
+ elif block_type == "thinking":
781
+ thinking_part = ThinkingPart(content="")
782
+ start_event = PartStartEvent(index=index, part=thinking_part)
783
+ for handler in self.event_handler._wrapped_handlers:
784
+ await handler(None, start_event)
785
+ yield start_event
786
+
787
+ elif block_type == "tool_use":
788
+ # Tool use start is handled via AssistantMessage ToolUseBlock
789
+ pass
790
+
791
+ # Handle content_block_delta events (text streaming)
792
+ elif event_type == "content_block_delta":
793
+ delta = event_data.get("delta", {})
794
+ delta_type = delta.get("type")
795
+
796
+ if delta_type == "text_delta":
797
+ text_delta = delta.get("text", "")
798
+ if text_delta:
799
+ text_part = TextPartDelta(content_delta=text_delta)
800
+ delta_event = PartDeltaEvent(index=index, delta=text_part)
801
+ for handler in self.event_handler._wrapped_handlers:
802
+ await handler(None, delta_event)
803
+ yield delta_event
804
+
805
+ elif delta_type == "thinking_delta":
806
+ thinking_delta = delta.get("thinking", "")
807
+ if thinking_delta:
808
+ delta = ThinkingPartDelta(content_delta=thinking_delta)
809
+ delta_event = PartDeltaEvent(index=index, delta=delta)
810
+ for handler in self.event_handler._wrapped_handlers:
811
+ await handler(None, delta_event)
812
+ yield delta_event
813
+
814
+ # Handle content_block_stop events
815
+ elif event_type == "content_block_stop":
816
+ # We don't have the full part content here, emit with empty part
817
+ # The actual content was accumulated via deltas
818
+ end_event = PartEndEvent(index=index, part=TextPart(content=""))
819
+ for handler in self.event_handler._wrapped_handlers:
820
+ await handler(None, end_event)
821
+ yield end_event
822
+
823
+ # Skip further processing for StreamEvent - don't duplicate
824
+ continue
825
+
826
+ # Convert to events and yield
827
+ # (skip AssistantMessage - already streamed via StreamEvent)
828
+ if not isinstance(message, AssistantMessage):
829
+ events = claude_message_to_events(
830
+ message,
831
+ agent_name=self.name,
832
+ pending_tool_calls={}, # Already handled above
833
+ )
834
+ for event in events:
835
+ for handler in self.event_handler._wrapped_handlers:
836
+ await handler(None, event)
837
+ yield event
838
+
839
+ # Check for result (end of response) and capture usage info
840
+ if isinstance(message, ResultMessage):
841
+ result_message = message
842
+ break
843
+
844
+ # Check for cancellation
845
+ if self._cancelled:
846
+ self.log.info("Stream cancelled by user")
847
+ # Emit partial response
848
+ response_msg = ChatMessage[TResult](
849
+ content="".join(text_chunks), # type: ignore[arg-type]
850
+ role="assistant",
851
+ name=self.name,
852
+ message_id=message_id or str(uuid.uuid4()),
853
+ conversation_id=self.conversation_id,
854
+ model_name=self.model_name,
855
+ messages=model_messages,
856
+ finish_reason="stop",
857
+ )
858
+ complete_event = StreamCompleteEvent(message=response_msg)
859
+ for handler in self.event_handler._wrapped_handlers:
860
+ await handler(None, complete_event)
861
+ yield complete_event
862
+ return
863
+ else:
864
+ result_message = None
865
+
866
+ except asyncio.CancelledError:
867
+ self.log.info("Stream cancelled via CancelledError")
868
+ # Emit partial response on cancellation
869
+ response_msg = ChatMessage[TResult](
870
+ content="".join(text_chunks), # type: ignore[arg-type]
871
+ role="assistant",
872
+ name=self.name,
873
+ message_id=message_id or str(uuid.uuid4()),
874
+ conversation_id=self.conversation_id,
875
+ model_name=self.model_name,
876
+ messages=model_messages,
877
+ finish_reason="stop",
878
+ )
879
+ complete_event = StreamCompleteEvent(message=response_msg)
880
+ for handler in self.event_handler._wrapped_handlers:
881
+ await handler(None, complete_event)
882
+ yield complete_event
883
+ return
884
+
885
+ except Exception as e:
886
+ error_event = RunErrorEvent(message=str(e), run_id=run_id, agent_name=self.name)
887
+ for handler in self.event_handler._wrapped_handlers:
888
+ await handler(None, error_event)
889
+ yield error_event
890
+ raise
891
+
892
+ # Flush any remaining response parts
893
+ if current_response_parts:
894
+ model_messages.append(ModelResponse(parts=current_response_parts))
895
+
896
+ # Determine final content - use structured output if available
897
+ final_content: TResult = (
898
+ result_message.structured_output # type: ignore[assignment]
899
+ if self._output_type is not str and result_message and result_message.structured_output
900
+ else "".join(text_chunks)
901
+ )
902
+
903
+ # Build cost_info from ResultMessage if available
904
+ cost_info: TokenCost | None = None
905
+ if result_message and result_message.usage:
906
+ usage = result_message.usage
907
+ run_usage = RunUsage(
908
+ input_tokens=usage.get("input_tokens", 0),
909
+ output_tokens=usage.get("output_tokens", 0),
910
+ cache_read_tokens=usage.get("cache_read_input_tokens", 0),
911
+ cache_write_tokens=usage.get("cache_creation_input_tokens", 0),
912
+ )
913
+ total_cost = Decimal(str(result_message.total_cost_usd or 0))
914
+ cost_info = TokenCost(token_usage=run_usage, total_cost=total_cost)
915
+
916
+ chat_message = ChatMessage[TResult](
917
+ content=final_content,
918
+ role="assistant",
919
+ name=self.name,
920
+ message_id=message_id or str(uuid.uuid4()),
921
+ conversation_id=self.conversation_id,
922
+ model_name=self.model_name,
923
+ messages=model_messages,
924
+ cost_info=cost_info,
925
+ response_time=result_message.duration_ms / 1000 if result_message else None,
926
+ )
927
+
928
+ # Emit stream complete
929
+ complete_event = StreamCompleteEvent[TResult](message=chat_message)
930
+ for handler in self.event_handler._wrapped_handlers:
931
+ await handler(None, complete_event)
932
+ yield complete_event
933
+ # Record to history
934
+ self.message_sent.emit(chat_message)
935
+ conversation.add_chat_messages([user_msg, chat_message])
936
+
937
+ async def run_iter(
938
+ self,
939
+ *prompt_groups: Sequence[PromptCompatible],
940
+ ) -> AsyncIterator[ChatMessage[TResult]]:
941
+ """Run agent sequentially on multiple prompt groups.
942
+
943
+ Args:
944
+ prompt_groups: Groups of prompts to process sequentially
945
+
946
+ Yields:
947
+ Response messages in sequence
948
+ """
949
+ for prompts in prompt_groups:
950
+ response = await self.run(*prompts)
951
+ yield response
952
+
953
+ async def interrupt(self) -> None:
954
+ """Interrupt the currently running stream.
955
+
956
+ Calls the Claude SDK's native interrupt() method to stop the query,
957
+ then cancels the local stream task.
958
+ """
959
+ self._cancelled = True
960
+
961
+ # Use Claude SDK's native interrupt
962
+ if self._client:
963
+ try:
964
+ await self._client.interrupt()
965
+ self.log.info("Claude Code client interrupted")
966
+ except Exception:
967
+ self.log.exception("Failed to interrupt Claude Code client")
968
+
969
+ # Also cancel the current stream task
970
+ if self._current_stream_task and not self._current_stream_task.done():
971
+ self._current_stream_task.cancel()
972
+
973
+ async def set_model(self, model: str) -> None:
974
+ """Set the model for future requests.
975
+
976
+ Note: This updates the model for the next query. The client
977
+ maintains the connection, so this takes effect on the next query().
978
+
979
+ Args:
980
+ model: Model name to use
981
+ """
982
+ self._model = model
983
+ self._current_model = model
984
+
985
+ if self._client:
986
+ await self._client.set_model(model)
987
+ self.log.info("Model changed", model=model)
988
+
989
+ async def set_tool_confirmation_mode(self, mode: ToolConfirmationMode) -> None:
990
+ """Set tool confirmation mode.
991
+
992
+ Args:
993
+ mode: Confirmation mode - "always", "never", or "per_tool"
994
+ """
995
+ self.tool_confirmation_mode = mode
996
+ # Update permission mode on client if connected
997
+ if self._client and mode == "never":
998
+ await self._client.set_permission_mode("bypassPermissions")
999
+ elif self._client and mode == "always":
1000
+ await self._client.set_permission_mode("default")
1001
+
1002
+ async def get_stats(self) -> MessageStats:
1003
+ """Get message statistics."""
1004
+ from agentpool.talk.stats import MessageStats
1005
+
1006
+ return MessageStats(messages=list(self.conversation.chat_messages))
1007
+
1008
+
1009
+ if __name__ == "__main__":
1010
+ import os
1011
+
1012
+ os.environ["ANTHROPIC_API_KEY"] = ""
1013
+
1014
+ async def main() -> None:
1015
+ """Demo: Basic call to Claude Code."""
1016
+ async with ClaudeCodeAgent(name="demo", event_handlers=["detailed"]) as agent:
1017
+ print("Response (streaming): ", end="", flush=True)
1018
+ async for _ in agent.run_stream("What files are in the current directory?"):
1019
+ pass
1020
+
1021
+ anyio.run(main)