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,244 @@
1
+ """Slash command wrapper for Agent that injects command events into streams."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import re
7
+ from typing import TYPE_CHECKING, Any, cast
8
+
9
+ import anyio
10
+ from slashed.events import (
11
+ CommandExecutedEvent,
12
+ CommandOutputEvent as SlashedCommandOutputEvent,
13
+ )
14
+
15
+ from agentpool.agents.events import CommandCompleteEvent, CommandOutputEvent
16
+ from agentpool.log import get_logger
17
+
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import AsyncGenerator, Callable
21
+
22
+ from slashed import CommandContext, CommandStore
23
+ from slashed.events import CommandStoreEvent
24
+
25
+ from agentpool.agents.base_agent import BaseAgent
26
+ from agentpool.agents.events import SlashedAgentStreamEvent
27
+ from agentpool.common_types import PromptCompatible
28
+
29
+
30
+ logger = get_logger(__name__)
31
+ SLASH_PATTERN = re.compile(r"^/([\w-]+)(?:\s+(.*))?$")
32
+
33
+
34
+ def _parse_slash_command(command_text: str) -> tuple[str, str] | None:
35
+ """Parse slash command into name and args.
36
+
37
+ Args:
38
+ command_text: Full command text
39
+
40
+ Returns:
41
+ Tuple of (cmd_name, args) or None if invalid
42
+ """
43
+ if match := SLASH_PATTERN.match(command_text.strip()):
44
+ cmd_name = match.group(1)
45
+ args = match.group(2) or ""
46
+ return cmd_name, args.strip()
47
+ return None
48
+
49
+
50
+ class SlashedAgent[TDeps, OutputDataT]:
51
+ """Wrapper around Agent that handles slash commands in streams.
52
+
53
+ Uses the "commands first" strategy from the ACP adapter:
54
+ 1. Execute all slash commands first
55
+ 2. Then process remaining content through wrapped agent
56
+ 3. If only commands, end without LLM processing
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ agent: BaseAgent[TDeps, OutputDataT],
62
+ command_store: CommandStore | None = None,
63
+ *,
64
+ context_data_factory: Callable[[], Any] | None = None,
65
+ ) -> None:
66
+ """Initialize with wrapped agent and command store.
67
+
68
+ Args:
69
+ agent: The agent to wrap
70
+ command_store: Command store for slash commands (creates default if None)
71
+ context_data_factory: Optional factory for creating command context data
72
+ """
73
+ self.agent = agent
74
+ self._context_data_factory = context_data_factory
75
+ self._event_queue: asyncio.Queue[CommandStoreEvent] | None = None
76
+
77
+ # Create store with our streaming event handler
78
+ if command_store is None:
79
+ from slashed import CommandStore
80
+
81
+ from agentpool_commands import get_commands
82
+
83
+ cmds = get_commands()
84
+ self.command_store = CommandStore(event_handler=self._emit_event, commands=cmds)
85
+ else:
86
+ self.command_store = command_store
87
+
88
+ async def _emit_event(self, event: CommandStoreEvent) -> None:
89
+ """Bridge store events to async queue during command execution."""
90
+ if self._event_queue:
91
+ await self._event_queue.put(event)
92
+
93
+ def _is_slash_command(self, text: str) -> bool:
94
+ """Check if text starts with a slash command.
95
+
96
+ Args:
97
+ text: Text to check
98
+
99
+ Returns:
100
+ True if text is a slash command
101
+ """
102
+ return bool(SLASH_PATTERN.match(text.strip()))
103
+
104
+ async def _execute_slash_command_streaming(
105
+ self, command_text: str
106
+ ) -> AsyncGenerator[CommandOutputEvent | CommandCompleteEvent]:
107
+ """Execute a single slash command and yield events as they happen.
108
+
109
+ Args:
110
+ command_text: Full command text including slash
111
+
112
+ Yields:
113
+ Command output and completion events
114
+ """
115
+ parsed = _parse_slash_command(command_text)
116
+ if not parsed:
117
+ logger.warning("Invalid slash command", command=command_text)
118
+ yield CommandCompleteEvent(command="unknown", success=False)
119
+ return
120
+
121
+ cmd_name, args = parsed
122
+
123
+ # Set up event queue for this command execution
124
+ self._event_queue = asyncio.Queue()
125
+ context_data = ( # Create command context
126
+ self._context_data_factory() if self._context_data_factory else self.agent.get_context()
127
+ )
128
+
129
+ cmd_ctx = self.command_store.create_context(data=context_data)
130
+ command_str = f"{cmd_name} {args}".strip()
131
+ execute_task = asyncio.create_task(self.command_store.execute_command(command_str, cmd_ctx))
132
+
133
+ success = True
134
+ try:
135
+ # Yield events from queue as command runs
136
+ while not execute_task.done():
137
+ try:
138
+ # Wait for events with short timeout to check task completion
139
+ event = await asyncio.wait_for(self._event_queue.get(), timeout=0.1)
140
+ # Convert store events to our stream events
141
+ match event:
142
+ case SlashedCommandOutputEvent(output=output):
143
+ yield CommandOutputEvent(command=cmd_name, output=output)
144
+ case CommandExecutedEvent(success=False, error=error) if error:
145
+ output = f"Command error: {error}"
146
+ yield CommandOutputEvent(command=cmd_name, output=output)
147
+ success = False
148
+ except TimeoutError:
149
+ continue
150
+
151
+ # Ensure command task completes and handle any remaining events
152
+ try:
153
+ await execute_task
154
+ except Exception as e:
155
+ logger.exception("Command execution failed", command=cmd_name)
156
+ success = False
157
+ yield CommandOutputEvent(command=cmd_name, output=f"Command error: {e}")
158
+
159
+ # Drain any remaining events from queue
160
+ while not self._event_queue.empty():
161
+ try:
162
+ match self._event_queue.get_nowait():
163
+ case SlashedCommandOutputEvent(output=output):
164
+ yield CommandOutputEvent(command=cmd_name, output=output)
165
+ except asyncio.QueueEmpty:
166
+ break
167
+
168
+ # Always yield completion event
169
+ yield CommandCompleteEvent(command=cmd_name, success=success)
170
+
171
+ finally:
172
+ # Clean up event queue
173
+ self._event_queue = None
174
+
175
+ async def run_stream(
176
+ self,
177
+ *prompts: PromptCompatible,
178
+ **kwargs: Any,
179
+ ) -> AsyncGenerator[SlashedAgentStreamEvent[OutputDataT]]:
180
+ """Run agent with slash command support.
181
+
182
+ Separates slash commands from regular prompts, executes commands first,
183
+ then processes remaining content through the wrapped agent.
184
+
185
+ Args:
186
+ *prompts: Input prompts (may include slash commands)
187
+ **kwargs: Additional arguments passed to agent.run_stream
188
+
189
+ Yields:
190
+ Stream events from command execution and agent processing
191
+ """
192
+ # Separate slash commands from regular content
193
+ commands: list[str] = []
194
+ regular_prompts: list[Any] = []
195
+
196
+ for prompt in prompts:
197
+ if isinstance(prompt, str) and self._is_slash_command(prompt):
198
+ logger.debug("Found slash command", command=prompt)
199
+ commands.append(prompt.strip())
200
+ else:
201
+ regular_prompts.append(prompt)
202
+
203
+ # Execute all commands first with streaming
204
+ if commands:
205
+ for command in commands:
206
+ logger.info("Processing slash command", command=command)
207
+ async for cmd_event in self._execute_slash_command_streaming(command):
208
+ yield cmd_event
209
+
210
+ # If we have regular content, process it through the agent
211
+ if regular_prompts:
212
+ logger.debug("Processing prompts through agent", num_prompts=len(regular_prompts))
213
+ async for event in self.agent.run_stream(*regular_prompts, **kwargs):
214
+ # ACPAgent always returns str, cast to match OutputDataT
215
+ yield cast("SlashedAgentStreamEvent[OutputDataT]", event)
216
+
217
+ # If we only had commands and no regular content, we're done
218
+ # (no additional events needed)
219
+
220
+
221
+ if __name__ == "__main__":
222
+ import asyncio
223
+
224
+ from agentpool import Agent
225
+
226
+ async def main() -> None:
227
+ agent = Agent("test-agent", model="test", session=False)
228
+ slashed = SlashedAgent(agent) # Uses built-in commands by default
229
+
230
+ # Add a simple test command that outputs multiple lines
231
+ @slashed.command_store.command(name="test-streaming", category="test")
232
+ async def test_streaming(ctx: CommandContext[Any], *args: Any, **kwargs: Any) -> None:
233
+ """Test command that outputs multiple lines."""
234
+ await ctx.print("Starting streaming test...")
235
+ for i in range(3):
236
+ await ctx.print(f"Output line {i + 1}")
237
+ await anyio.sleep(0.1) # Small delay to simulate work
238
+ await ctx.print("Streaming test complete!")
239
+
240
+ print("Testing SlashedAgent streaming:")
241
+ async for event in slashed.run_stream("/test-streaming"):
242
+ print(f"Event: {event}")
243
+
244
+ anyio.run(main)
@@ -0,0 +1,178 @@
1
+ """System prompt management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from contextlib import asynccontextmanager
6
+ from typing import TYPE_CHECKING, Any, Literal
7
+
8
+ from agentpool import text_templates
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import AsyncIterator
13
+
14
+ from toprompt import AnyPromptType
15
+
16
+ from agentpool.agents.base_agent import BaseAgent
17
+ from agentpool.prompts.manager import PromptManager
18
+
19
+
20
+ ToolInjectionMode = Literal["off", "all"]
21
+ ToolUsageStyle = Literal["suggestive", "strict"]
22
+
23
+
24
+ class SystemPrompts:
25
+ """Manages system prompts for an agent."""
26
+
27
+ def __init__(
28
+ self,
29
+ prompts: AnyPromptType | list[AnyPromptType] | None = None,
30
+ template: str | None = None,
31
+ dynamic: bool = True,
32
+ prompt_manager: PromptManager | None = None,
33
+ inject_agent_info: bool = True,
34
+ inject_tools: ToolInjectionMode = "off",
35
+ tool_usage_style: ToolUsageStyle = "suggestive",
36
+ ) -> None:
37
+ """Initialize prompt manager."""
38
+ from jinjarope import Environment
39
+ from toprompt import to_prompt
40
+
41
+ match prompts:
42
+ case list():
43
+ self.prompts = prompts
44
+ case None:
45
+ self.prompts = []
46
+ case _:
47
+ self.prompts = [prompts]
48
+ self.prompt_manager = prompt_manager
49
+ self.template = template
50
+ self.dynamic = dynamic
51
+ self.inject_agent_info = inject_agent_info
52
+ self.inject_tools = inject_tools
53
+ self.tool_usage_style = tool_usage_style
54
+ self._cached = False
55
+ self._env = Environment(enable_async=True)
56
+ self._env.filters["to_prompt"] = to_prompt
57
+
58
+ def __repr__(self) -> str:
59
+ return (
60
+ f"SystemPrompts(prompts={len(self.prompts)}, "
61
+ f"dynamic={self.dynamic}, inject_agent_info={self.inject_agent_info}, "
62
+ f"inject_tools={self.inject_tools!r})"
63
+ )
64
+
65
+ def __len__(self) -> int:
66
+ return len(self.prompts)
67
+
68
+ def __getitem__(self, idx: int | slice) -> AnyPromptType | list[AnyPromptType]:
69
+ return self.prompts[idx]
70
+
71
+ async def add_by_reference(self, reference: str) -> None:
72
+ """Add a system prompt using reference syntax.
73
+
74
+ Args:
75
+ reference: [provider:]identifier[@version][?var1=val1,...]
76
+
77
+ Examples:
78
+ await sys_prompts.add_by_reference("code_review?language=python")
79
+ await sys_prompts.add_by_reference("langfuse:expert@v2")
80
+ """
81
+ if not self.prompt_manager:
82
+ msg = "No prompt_manager available to resolve prompts"
83
+ raise RuntimeError(msg)
84
+
85
+ try:
86
+ content = await self.prompt_manager.get(reference)
87
+ self.prompts.append(content)
88
+ except Exception as e:
89
+ msg = f"Failed to add prompt {reference!r}"
90
+ raise RuntimeError(msg) from e
91
+
92
+ async def add(
93
+ self,
94
+ identifier: str,
95
+ *,
96
+ provider: str | None = None,
97
+ version: str | None = None,
98
+ variables: dict[str, Any] | None = None,
99
+ ) -> None:
100
+ """Add a system prompt.
101
+
102
+ Args:
103
+ identifier: Prompt identifier/name
104
+ provider: Provider name (None = builtin)
105
+ version: Optional version string
106
+ variables: Optional template variables
107
+
108
+ Examples:
109
+ await sys_prompts.add("code_review", variables={"language": "python"})
110
+ await sys_prompts.add("expert", provider="langfuse", version="v2")
111
+ """
112
+ if not self.prompt_manager:
113
+ msg = "No prompt_manager available to resolve prompts"
114
+ raise RuntimeError(msg)
115
+
116
+ try:
117
+ content = await self.prompt_manager.get_from(
118
+ identifier,
119
+ provider=provider,
120
+ version=version,
121
+ variables=variables,
122
+ )
123
+ self.prompts.append(content)
124
+ except Exception as e:
125
+ ref = f"{provider + ':' if provider else ''}{identifier}"
126
+ msg = f"Failed to add prompt {ref!r}"
127
+ raise RuntimeError(msg) from e
128
+
129
+ def clear(self) -> None:
130
+ """Clear all system prompts."""
131
+ self.prompts = []
132
+
133
+ async def refresh_cache(self) -> None:
134
+ """Force re-evaluation of prompts."""
135
+ from toprompt import to_prompt
136
+
137
+ evaluated = []
138
+ for prompt in self.prompts:
139
+ result = await to_prompt(prompt)
140
+ evaluated.append(result)
141
+ self.prompts = evaluated
142
+ self._cached = True
143
+
144
+ @asynccontextmanager
145
+ async def temporary_prompt(
146
+ self, prompt: AnyPromptType, exclusive: bool = False
147
+ ) -> AsyncIterator[None]:
148
+ """Temporarily override system prompts.
149
+
150
+ Args:
151
+ prompt: Single prompt or sequence of prompts to use temporarily
152
+ exclusive: Whether to only use given prompt. If False, prompt will be
153
+ appended to the agents prompts temporarily.
154
+ """
155
+ from toprompt import to_prompt
156
+
157
+ original_prompts = self.prompts.copy()
158
+ new_prompt = await to_prompt(prompt)
159
+ self.prompts = [new_prompt] if not exclusive else [*self.prompts, new_prompt]
160
+ try:
161
+ yield
162
+ finally:
163
+ self.prompts = original_prompts
164
+
165
+ async def format_system_prompt(self, agent: BaseAgent[Any, Any]) -> str:
166
+ """Format complete system prompt."""
167
+ if not self.dynamic and not self._cached:
168
+ await self.refresh_cache()
169
+ template = self._env.from_string(self.template or text_templates.get_system_prompt())
170
+ result = await template.render_async(
171
+ agent=agent,
172
+ prompts=self.prompts,
173
+ dynamic=self.dynamic,
174
+ inject_agent_info=self.inject_agent_info,
175
+ inject_tools=self.inject_tools,
176
+ tool_usage_style=self.tool_usage_style,
177
+ )
178
+ return result.strip()
@@ -0,0 +1,184 @@
1
+ """Tool wrapping utilities for pydantic-ai integration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import replace
6
+ from functools import wraps
7
+ import inspect
8
+ import time
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from pydantic_ai import RunContext
12
+
13
+ from agentpool.agents.context import AgentContext
14
+ from agentpool.tasks import ChainAbortedError, RunAbortedError, ToolSkippedError
15
+ from agentpool.utils.inspection import execute, get_argument_key
16
+ from agentpool.utils.signatures import create_modified_signature, update_signature
17
+
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Awaitable, Callable
21
+
22
+ from agentpool.hooks import AgentHooks
23
+ from agentpool.tools.base import Tool
24
+
25
+
26
+ def wrap_tool[TReturn]( # noqa: PLR0915
27
+ tool: Tool[TReturn],
28
+ agent_ctx: AgentContext,
29
+ hooks: AgentHooks | None = None,
30
+ ) -> Callable[..., Awaitable[TReturn | None]]:
31
+ """Wrap tool with confirmation handling and hooks.
32
+
33
+ Strategy:
34
+ - Tools with RunContext only: Normal pydantic-ai handling
35
+ - Tools with AgentContext only: Treat as regular tools, inject AgentContext
36
+ - Tools with both contexts: Present as RunContext-only to pydantic-ai, inject AgentContext
37
+ - Tools with no context: Normal pydantic-ai handling
38
+
39
+ Args:
40
+ tool: The tool to wrap.
41
+ agent_ctx: Agent context for confirmation handling and dependency injection.
42
+ hooks: Optional AgentHooks for pre/post tool execution hooks.
43
+ """
44
+ fn = tool.callable
45
+ run_ctx_key = get_argument_key(fn, RunContext)
46
+ agent_ctx_key = get_argument_key(fn, AgentContext)
47
+
48
+ # Validate parameter order if RunContext is present
49
+ if run_ctx_key:
50
+ param_names = list(inspect.signature(fn).parameters.keys())
51
+ run_ctx_index = param_names.index(run_ctx_key)
52
+ if run_ctx_index != 0:
53
+ msg = f"Tool {tool.name!r}: RunContext param {run_ctx_key!r} must come first."
54
+ raise ValueError(msg)
55
+
56
+ async def _execute_with_hooks(
57
+ execute_fn: Callable[..., Awaitable[TReturn]],
58
+ tool_input: dict[str, Any],
59
+ *args: Any,
60
+ **kwargs: Any,
61
+ ) -> TReturn | None:
62
+ """Execute tool with pre/post hooks."""
63
+ # Pre-tool hooks
64
+ if hooks:
65
+ pre_result = await hooks.run_pre_tool_hooks(
66
+ agent_name=agent_ctx.node_name,
67
+ tool_name=tool.name,
68
+ tool_input=tool_input,
69
+ conversation_id=None, # Could be passed through if needed
70
+ )
71
+ if pre_result.get("decision") == "deny":
72
+ reason = pre_result.get("reason", "Blocked by pre-tool hook")
73
+ msg = f"Tool {tool.name} blocked: {reason}"
74
+ raise ToolSkippedError(msg)
75
+
76
+ # Apply modified input if provided
77
+ if modified := pre_result.get("modified_input"):
78
+ kwargs.update(modified)
79
+
80
+ # Execute the tool
81
+ start_time = time.perf_counter()
82
+ result = await execute_fn(*args, **kwargs)
83
+ duration_ms = (time.perf_counter() - start_time) * 1000
84
+
85
+ # Post-tool hooks
86
+ if hooks:
87
+ await hooks.run_post_tool_hooks(
88
+ agent_name=agent_ctx.node_name,
89
+ tool_name=tool.name,
90
+ tool_input=tool_input,
91
+ tool_output=result,
92
+ duration_ms=duration_ms,
93
+ conversation_id=None,
94
+ )
95
+
96
+ return result
97
+
98
+ if run_ctx_key or agent_ctx_key:
99
+ # Tool has RunContext and/or AgentContext
100
+ async def wrapped(ctx: RunContext, *args: Any, **kwargs: Any) -> TReturn | None: # pyright: ignore
101
+ result = await agent_ctx.handle_confirmation(tool, kwargs)
102
+ if result == "allow":
103
+ # Populate AgentContext with RunContext data if needed
104
+ if agent_ctx.data is None:
105
+ agent_ctx.data = ctx.deps
106
+
107
+ if agent_ctx_key: # inject AgentContext
108
+ # Create per-call copy with tool execution fields (avoids race condition)
109
+ call_ctx = replace(
110
+ agent_ctx,
111
+ tool_name=ctx.tool_name,
112
+ tool_call_id=ctx.tool_call_id,
113
+ tool_input=kwargs.copy(),
114
+ )
115
+ kwargs[agent_ctx_key] = call_ctx
116
+
117
+ tool_input = kwargs.copy()
118
+ if run_ctx_key:
119
+ # Pass RunContext to original function
120
+ return await _execute_with_hooks(
121
+ lambda *a, **kw: execute(fn, ctx, *a, **kw),
122
+ tool_input,
123
+ *args,
124
+ **kwargs,
125
+ )
126
+ # Don't pass RunContext to original function since it didn't expect it
127
+ return await _execute_with_hooks(
128
+ lambda *a, **kw: execute(fn, *a, **kw),
129
+ tool_input,
130
+ *args,
131
+ **kwargs,
132
+ )
133
+ await _handle_confirmation_result(result, tool.name)
134
+ return None
135
+
136
+ else:
137
+ # Tool has no context - normal function call
138
+ async def wrapped(*args: Any, **kwargs: Any) -> TReturn | None: # type: ignore[misc]
139
+ result = await agent_ctx.handle_confirmation(tool, kwargs)
140
+ if result == "allow":
141
+ tool_input = kwargs.copy()
142
+ return await _execute_with_hooks(
143
+ lambda *a, **kw: execute(fn, *a, **kw),
144
+ tool_input,
145
+ *args,
146
+ **kwargs,
147
+ )
148
+ await _handle_confirmation_result(result, tool.name)
149
+ return None
150
+
151
+ # Apply wraps first
152
+ wraps(fn)(wrapped) # pyright: ignore
153
+ # Python 3.14: functools.wraps copies __annotate__ but not __annotations__.
154
+ # Any subsequent assignment to __annotations__ destroys __annotate__ (PEP 649).
155
+ # Restore from original to preserve deferred annotation evaluation.
156
+ # TODO: probably review all wraps() calls in the codebase.
157
+ wrapped.__annotations__ = fn.__annotations__
158
+ wrapped.__doc__ = tool.description
159
+ wrapped.__name__ = tool.name
160
+ # Modify signature for pydantic-ai: hide AgentContext, add RunContext if needed
161
+ # Must be done AFTER wraps to prevent overwriting
162
+ if agent_ctx_key and not run_ctx_key:
163
+ # Tool has AgentContext only - make it appear to have RunContext to pydantic-ai
164
+ new_sig = create_modified_signature(fn, remove=agent_ctx_key, inject={"ctx": RunContext})
165
+ update_signature(wrapped, new_sig)
166
+ elif agent_ctx_key and run_ctx_key:
167
+ # Tool has both contexts - hide AgentContext from pydantic-ai
168
+ new_sig = create_modified_signature(fn, remove=agent_ctx_key)
169
+ update_signature(wrapped, new_sig)
170
+ return wrapped
171
+
172
+
173
+ async def _handle_confirmation_result(result: str, name: str) -> None:
174
+ """Handle non-allow confirmation results."""
175
+ match result:
176
+ case "skip":
177
+ msg = f"Tool {name} execution skipped"
178
+ raise ToolSkippedError(msg)
179
+ case "abort_run":
180
+ msg = "Run aborted by user"
181
+ raise RunAbortedError(msg)
182
+ case "abort_chain":
183
+ msg = "Agent chain aborted by user"
184
+ raise ChainAbortedError(msg)
@@ -0,0 +1,28 @@
1
+ """Base classes for providers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import abstractmethod
6
+ from typing import Any, Self
7
+
8
+ from pydantic import BaseModel
9
+
10
+
11
+ class BaseProvider[ConfigT: BaseModel]:
12
+ """Base class for all providers."""
13
+
14
+ def __init__(self, config: ConfigT) -> None:
15
+ """Initialize provider with configuration."""
16
+ self.config = config
17
+
18
+ @classmethod
19
+ @abstractmethod
20
+ def from_kwargs(cls, **kwargs: Any) -> Self:
21
+ """Alternative constructor with explicit parameters."""
22
+ raise NotImplementedError
23
+
24
+ def __repr__(self) -> str:
25
+ """Return string representation of provider with non-default config values."""
26
+ non_defaults = self.config.model_dump(exclude_defaults=True)
27
+ fields_str = ", ".join(f"{k}={v!r}" for k, v in non_defaults.items())
28
+ return f"{self.__class__.__name__}({fields_str})"