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
acp/notifications.py ADDED
@@ -0,0 +1,832 @@
1
+ """ACP notification helper for clean session update API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from typing import TYPE_CHECKING, Any, assert_never
7
+
8
+ from pydantic_ai import ModelRequest, ModelResponse, ToolReturnPart, UserPromptPart
9
+
10
+ from acp.schema import (
11
+ AgentMessageChunk,
12
+ AgentPlanUpdate,
13
+ AgentThoughtChunk,
14
+ AudioContentBlock,
15
+ AvailableCommand,
16
+ AvailableCommandsUpdate,
17
+ BlobResourceContents,
18
+ ContentToolCallContent,
19
+ # CurrentModelUpdate,
20
+ CurrentModeUpdate,
21
+ EmbeddedResourceContentBlock,
22
+ FileEditToolCallContent,
23
+ ImageContentBlock,
24
+ ResourceContentBlock,
25
+ SessionNotification,
26
+ TerminalToolCallContent,
27
+ TextContentBlock,
28
+ TextResourceContents,
29
+ ToolCallProgress,
30
+ ToolCallStart,
31
+ UserMessageChunk,
32
+ )
33
+ from acp.schema.tool_call import ToolCallLocation
34
+ from acp.tool_call_reporter import ToolCallReporter
35
+ from acp.utils import generate_tool_title, infer_tool_kind, to_acp_content_blocks
36
+ from agentpool.log import get_logger
37
+
38
+
39
+ if TYPE_CHECKING:
40
+ from collections.abc import Sequence
41
+ from datetime import datetime
42
+
43
+ from acp import (
44
+ AvailableCommand,
45
+ Client,
46
+ PlanEntry,
47
+ ToolCallContent,
48
+ ToolCallKind,
49
+ ToolCallStatus,
50
+ )
51
+ from acp.schema import Audience
52
+
53
+ ContentType = Sequence[ToolCallContent | str]
54
+
55
+ logger = get_logger(__name__)
56
+
57
+
58
+ class ACPNotifications:
59
+ """Clean API for creating and sending ACP session notifications.
60
+
61
+ Provides convenient methods for common notification patterns,
62
+ handling both creation and sending in a single call.
63
+ """
64
+
65
+ def __init__(self, client: Client, session_id: str) -> None:
66
+ """Initialize notifications helper.
67
+
68
+ Args:
69
+ client: ACP client and session_id
70
+ session_id: Session identifier
71
+ """
72
+ self.client = client
73
+ self.id = session_id
74
+ self.log = logger.bind(session_id=session_id)
75
+ self._tool_call_inputs: dict[str, dict[str, Any]] = {}
76
+
77
+ async def create_tool_reporter(
78
+ self,
79
+ tool_call_id: str,
80
+ title: str,
81
+ *,
82
+ kind: ToolCallKind | None = None,
83
+ status: ToolCallStatus = "pending",
84
+ locations: Sequence[ToolCallLocation] | None = None,
85
+ content: Sequence[ToolCallContent] | None = None,
86
+ raw_input: Any | None = None,
87
+ auto_start: bool = True,
88
+ ) -> ToolCallReporter:
89
+ """Create a stateful tool call reporter.
90
+
91
+ The reporter maintains the current state and sends updates when fields change,
92
+ avoiding the need to repeat unchanged fields on every update.
93
+
94
+ Args:
95
+ tool_call_id: Unique identifier for this tool call
96
+ title: Human-readable title describing the tool action
97
+ kind: Category of tool being invoked
98
+ status: Initial execution status
99
+ locations: File locations affected by this tool call
100
+ content: Initial content produced by the tool call
101
+ raw_input: Raw input parameters sent to the tool
102
+ auto_start: Whether to send the initial notification immediately
103
+
104
+ Returns:
105
+ A ToolCallReporter instance for sending updates
106
+
107
+ Example:
108
+ ```python
109
+ reporter = await notifications.create_tool_reporter(
110
+ tool_call_id="abc123",
111
+ title="Reading file",
112
+ kind="read",
113
+ )
114
+ await reporter.update(status="in_progress", message="Opening...")
115
+ await reporter.update(message="Processing...")
116
+ await reporter.complete(message="Done!")
117
+ ```
118
+ """
119
+ reporter = ToolCallReporter(
120
+ notifications=self,
121
+ tool_call_id=tool_call_id,
122
+ title=title,
123
+ kind=kind,
124
+ status=status,
125
+ locations=locations,
126
+ content=content,
127
+ raw_input=raw_input,
128
+ )
129
+ if auto_start:
130
+ await reporter.start()
131
+ return reporter
132
+
133
+ async def tool_call(
134
+ self,
135
+ tool_name: str,
136
+ *,
137
+ tool_input: dict[str, Any],
138
+ tool_output: Any,
139
+ status: ToolCallStatus = "completed",
140
+ tool_call_id: str | None = None,
141
+ ) -> None:
142
+ """Send tool execution as ACP tool call update.
143
+
144
+ Args:
145
+ tool_name: Name of the tool that was executed
146
+ tool_input: Input parameters passed to the tool
147
+ tool_output: Output returned by the tool
148
+ status: Execution status
149
+ tool_call_id: Tool call identifier
150
+
151
+ Returns:
152
+ SessionNotification with tool call update
153
+ """
154
+ # Create tool call content from output
155
+ content: list[ContentToolCallContent] = []
156
+ if tool_output is not None:
157
+ # Handle pre-converted raw content blocks
158
+ if isinstance(tool_output, list) and all(
159
+ isinstance(item, (TextContentBlock, ImageContentBlock, AudioContentBlock))
160
+ for item in tool_output
161
+ ):
162
+ # Wrap raw blocks in ContentToolCallContent
163
+ content = [ContentToolCallContent(content=block) for block in tool_output]
164
+ else:
165
+ # Fallback to string conversion
166
+ output_text = str(tool_output)
167
+ content.append(ContentToolCallContent.text(text=output_text))
168
+
169
+ # Extract file locations if present
170
+ locations = [
171
+ ToolCallLocation(path=value)
172
+ for key, value in tool_input.items()
173
+ if key in {"path", "file_path", "filepath"} and isinstance(value, str)
174
+ ]
175
+
176
+ # Generate a descriptive title from tool name and inputs
177
+ title = generate_tool_title(tool_name, tool_input)
178
+
179
+ # Use appropriate notification type based on status
180
+ if status == "pending":
181
+ await self.tool_call_start(
182
+ tool_call_id=tool_call_id or f"{tool_name}_{hash(str(tool_input))}",
183
+ title=title,
184
+ kind=infer_tool_kind(tool_name),
185
+ locations=locations or None,
186
+ content=content or None,
187
+ raw_input=tool_input,
188
+ )
189
+ else:
190
+ # For in_progress, completed, and failed statuses
191
+ await self.tool_call_progress(
192
+ tool_call_id=tool_call_id or f"{tool_name}_{hash(str(tool_input))}",
193
+ title=title,
194
+ status=status,
195
+ locations=locations or None,
196
+ content=content or None,
197
+ raw_output=tool_output,
198
+ )
199
+
200
+ async def tool_call_start(
201
+ self,
202
+ tool_call_id: str,
203
+ title: str,
204
+ *,
205
+ kind: ToolCallKind | None = None,
206
+ locations: Sequence[ToolCallLocation] | None = None,
207
+ content: ContentType | None = None,
208
+ raw_input: dict[str, Any] | None = None,
209
+ ) -> None:
210
+ """Send a tool call start notification.
211
+
212
+ Args:
213
+ tool_call_id: Tool call identifier
214
+ title: Optional title for the start notification
215
+ kind: Optional tool call kind
216
+ locations: Optional sequence of file/path locations
217
+ content: Optional sequence of content blocks
218
+ raw_input: Optional raw input data
219
+ """
220
+ start = ToolCallStart(
221
+ tool_call_id=tool_call_id,
222
+ status="pending",
223
+ title=title,
224
+ kind=kind,
225
+ locations=locations,
226
+ content=[
227
+ ContentToolCallContent.text(text=i) if isinstance(i, str) else i
228
+ for i in content or []
229
+ ],
230
+ raw_input=raw_input,
231
+ )
232
+ notification = SessionNotification(session_id=self.id, update=start)
233
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
234
+
235
+ async def tool_call_progress(
236
+ self,
237
+ tool_call_id: str,
238
+ status: ToolCallStatus,
239
+ *,
240
+ title: str | None = None,
241
+ raw_output: Any | None = None,
242
+ kind: ToolCallKind | None = None,
243
+ locations: Sequence[ToolCallLocation] | None = None,
244
+ content: ContentType | None = None,
245
+ ) -> None:
246
+ """Send a generic progress notification.
247
+
248
+ Args:
249
+ tool_call_id: Tool call identifier
250
+ status: Progress status
251
+ title: Optional title for the progress update
252
+ raw_output: Optional raw output text
253
+ kind: Optional kind of tool call
254
+ locations: Optional sequence of file/path locations
255
+ content: Optional sequence of content blocks or strings to display
256
+ """
257
+ progress = ToolCallProgress(
258
+ tool_call_id=tool_call_id,
259
+ status=status,
260
+ title=title,
261
+ raw_output=raw_output,
262
+ kind=kind,
263
+ locations=locations,
264
+ content=[
265
+ ContentToolCallContent.text(i) if isinstance(i, str) else i for i in content or []
266
+ ],
267
+ )
268
+ notification = SessionNotification(session_id=self.id, update=progress)
269
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
270
+
271
+ async def tool_call_update(
272
+ self,
273
+ tool_call_id: str,
274
+ *,
275
+ title: str | None = None,
276
+ status: ToolCallStatus | None = None,
277
+ kind: ToolCallKind | None = None,
278
+ locations: Sequence[ToolCallLocation] | None = None,
279
+ content: ContentType | None = None,
280
+ raw_output: Any | None = None,
281
+ ) -> None:
282
+ """Send a tool call update with only the provided fields.
283
+
284
+ Unlike tool_call_progress, all fields are optional. Only fields
285
+ that are explicitly provided (not None) will be included in the
286
+ notification, following the ACP spec which states that only
287
+ changed fields need to be sent.
288
+
289
+ Args:
290
+ tool_call_id: Tool call identifier (required)
291
+ title: Update the human-readable title
292
+ status: Update execution status
293
+ kind: Update tool kind
294
+ locations: Update file locations
295
+ content: Update content blocks
296
+ raw_output: Update raw output
297
+ """
298
+ update = ToolCallProgress(
299
+ tool_call_id=tool_call_id,
300
+ status=status,
301
+ title=title,
302
+ raw_output=raw_output,
303
+ kind=kind,
304
+ locations=locations,
305
+ content=[
306
+ ContentToolCallContent.text(i) if isinstance(i, str) else i for i in content or []
307
+ ]
308
+ if content
309
+ else None,
310
+ )
311
+ notification = SessionNotification(session_id=self.id, update=update)
312
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
313
+
314
+ async def file_edit_progress(
315
+ self,
316
+ tool_call_id: str,
317
+ path: str,
318
+ old_text: str,
319
+ new_text: str,
320
+ *,
321
+ status: ToolCallStatus = "completed",
322
+ title: str | None = None,
323
+ changed_lines: Sequence[int] | None = None,
324
+ ) -> None:
325
+ """Send a notification with file edit content.
326
+
327
+ Args:
328
+ tool_call_id: Tool call identifier
329
+ path: File path being edited
330
+ old_text: Original file content
331
+ new_text: New file content
332
+ status: Progress status (default: 'completed')
333
+ title: Optional title
334
+ changed_lines: List of line numbers where changes occurred (1-based)
335
+ """
336
+ content = FileEditToolCallContent(path=path, old_text=old_text, new_text=new_text)
337
+
338
+ # Create locations for changed lines or fallback to file location
339
+ if changed_lines:
340
+ locations = [ToolCallLocation(path=path, line=i) for i in changed_lines]
341
+ else:
342
+ locations = [ToolCallLocation(path=path)]
343
+
344
+ await self.tool_call_progress(
345
+ tool_call_id=tool_call_id,
346
+ status=status,
347
+ title=title,
348
+ locations=locations,
349
+ content=[content],
350
+ )
351
+
352
+ async def terminal_progress(
353
+ self,
354
+ tool_call_id: str,
355
+ terminal_id: str,
356
+ *,
357
+ status: ToolCallStatus = "completed",
358
+ title: str | None = None,
359
+ raw_output: str | None = None,
360
+ ) -> None:
361
+ """Send a notification with terminal content.
362
+
363
+ Args:
364
+ tool_call_id: Tool call identifier
365
+ terminal_id: Terminal identifier
366
+ status: Progress status (default: 'completed')
367
+ title: Optional title
368
+ raw_output: Optional raw output text
369
+ """
370
+ terminal_content = TerminalToolCallContent(terminal_id=terminal_id)
371
+ await self.tool_call_progress(
372
+ tool_call_id=tool_call_id,
373
+ status=status,
374
+ title=title,
375
+ raw_output=raw_output,
376
+ content=[terminal_content],
377
+ )
378
+
379
+ async def update_plan(self, entries: Sequence[PlanEntry]) -> None:
380
+ """Send a plan notification.
381
+
382
+ Args:
383
+ entries: List of plan entries to send
384
+ """
385
+ plan = AgentPlanUpdate(entries=entries)
386
+ notification = SessionNotification(session_id=self.id, update=plan)
387
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
388
+
389
+ async def update_commands(self, commands: list[AvailableCommand]) -> None:
390
+ """Send a command update notification.
391
+
392
+ Args:
393
+ commands: List of available commands to send
394
+ """
395
+ update = AvailableCommandsUpdate(available_commands=commands)
396
+ notification = SessionNotification(session_id=self.id, update=update)
397
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
398
+
399
+ async def send_agent_text(
400
+ self,
401
+ message: str,
402
+ *,
403
+ audience: Audience | None = None,
404
+ last_modified: datetime | str | None = None,
405
+ priority: float | None = None,
406
+ ) -> None:
407
+ """Send a text message notification.
408
+
409
+ Args:
410
+ message: Text message to send
411
+ audience: Audience to send the message to
412
+ last_modified: Last modified timestamp
413
+ priority: Priority of the message
414
+ """
415
+ update = AgentMessageChunk.text(
416
+ text=message,
417
+ audience=audience,
418
+ last_modified=last_modified,
419
+ priority=priority,
420
+ )
421
+ notification = SessionNotification(session_id=self.id, update=update)
422
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
423
+
424
+ async def send_agent_thought(
425
+ self,
426
+ message: str,
427
+ *,
428
+ audience: Audience | None = None,
429
+ last_modified: datetime | str | None = None,
430
+ priority: float | None = None,
431
+ ) -> None:
432
+ """Send a text message notification.
433
+
434
+ Args:
435
+ message: Text message to send
436
+ audience: Audience to send the message to
437
+ last_modified: Last modified date of the message
438
+ priority: Priority of the message
439
+ """
440
+ update = AgentThoughtChunk.text(
441
+ text=message,
442
+ audience=audience,
443
+ last_modified=last_modified,
444
+ priority=priority,
445
+ )
446
+ notification = SessionNotification(session_id=self.id, update=update)
447
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
448
+
449
+ async def send_user_message(
450
+ self,
451
+ message: str,
452
+ *,
453
+ audience: Audience | None = None,
454
+ last_modified: datetime | str | None = None,
455
+ priority: float | None = None,
456
+ ) -> None:
457
+ """Send a user message notification.
458
+
459
+ Args:
460
+ message: Text message to send
461
+ audience: Audience to send the message to
462
+ last_modified: Last modified date of the message
463
+ priority: Priority of the message
464
+ """
465
+ update = UserMessageChunk.text(
466
+ text=message,
467
+ audience=audience,
468
+ last_modified=last_modified,
469
+ priority=priority,
470
+ )
471
+ notification = SessionNotification(session_id=self.id, update=update)
472
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
473
+
474
+ async def send_user_image(
475
+ self,
476
+ data: str | bytes,
477
+ mime_type: str,
478
+ *,
479
+ uri: str | None = None,
480
+ audience: Audience | None = None,
481
+ last_modified: datetime | str | None = None,
482
+ priority: float | None = None,
483
+ ) -> None:
484
+ """Send a user image notification.
485
+
486
+ Args:
487
+ data: Base64-encoded image data
488
+ mime_type: MIME type of the image
489
+ uri: Optional URI of the image
490
+ audience: Optional audience for the content block
491
+ last_modified: Optional last modified timestamp for the content block
492
+ priority: Optional priority for the content block
493
+ """
494
+ update = UserMessageChunk.image(
495
+ data=data,
496
+ mime_type=mime_type,
497
+ uri=uri,
498
+ audience=audience,
499
+ last_modified=last_modified,
500
+ priority=priority,
501
+ )
502
+ notification = SessionNotification(session_id=self.id, update=update)
503
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
504
+
505
+ async def send_user_audio(
506
+ self,
507
+ data: str | bytes,
508
+ mime_type: str,
509
+ *,
510
+ audience: Audience | None = None,
511
+ last_modified: datetime | str | None = None,
512
+ priority: float | None = None,
513
+ ) -> None:
514
+ """Send a user audio notification.
515
+
516
+ Args:
517
+ data: Base64-encoded audio data
518
+ mime_type: MIME type of the audio
519
+ uri: Optional URI for the audio
520
+ audience: Optional audience for the content block
521
+ last_modified: Optional last modified timestamp for the content block
522
+ priority: Optional priority for the content block
523
+ """
524
+ update = UserMessageChunk.audio(
525
+ data=data,
526
+ mime_type=mime_type,
527
+ audience=audience,
528
+ last_modified=last_modified,
529
+ priority=priority,
530
+ )
531
+ notification = SessionNotification(session_id=self.id, update=update)
532
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
533
+
534
+ async def send_user_resource(
535
+ self,
536
+ uri: str,
537
+ name: str,
538
+ *,
539
+ description: str | None = None,
540
+ mime_type: str | None = None,
541
+ size: int | None = None,
542
+ title: str | None = None,
543
+ audience: Audience | None = None,
544
+ last_modified: datetime | str | None = None,
545
+ priority: float | None = None,
546
+ ) -> None:
547
+ """Send a user resource link notification.
548
+
549
+ Args:
550
+ uri: URI of the resource
551
+ name: Name of the resource
552
+ description: Optional description of the resource
553
+ mime_type: Optional MIME type of the resource
554
+ size: Optional size of the resource in bytes
555
+ title: Optional title of the resource
556
+ audience: Optional audience for the content block
557
+ last_modified: Optional last modified timestamp for the content block
558
+ priority: Optional priority for the content block
559
+ """
560
+ update = UserMessageChunk.resource(
561
+ uri=uri,
562
+ name=name,
563
+ description=description,
564
+ mime_type=mime_type,
565
+ size=size,
566
+ title=title,
567
+ audience=audience,
568
+ last_modified=last_modified,
569
+ priority=priority,
570
+ )
571
+ notification = SessionNotification(session_id=self.id, update=update)
572
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
573
+
574
+ async def replay(self, messages: Sequence[ModelRequest | ModelResponse]) -> None:
575
+ """Replay a sequence of model messages as notifications.
576
+
577
+ Args:
578
+ messages: Sequence of ModelRequest and ModelResponse objects to replay
579
+ """
580
+ for message in messages:
581
+ try:
582
+ match message:
583
+ case ModelRequest():
584
+ await self._replay_request(message)
585
+ case ModelResponse():
586
+ await self._replay_response(message)
587
+ case _ as unreachable:
588
+ assert_never(unreachable)
589
+ except Exception as e:
590
+ self.log.exception("Failed to replay message", error=str(e))
591
+
592
+ async def _replay_request(self, request: ModelRequest) -> None:
593
+ """Replay a ModelRequest by converting it to appropriate ACP notifications."""
594
+ for part in request.parts:
595
+ match part:
596
+ case UserPromptPart(content=content):
597
+ # Handle both str and Sequence[UserContent] types
598
+ if isinstance(content, str):
599
+ await self.send_user_message(content)
600
+ else:
601
+ # Convert multi-modal content to appropriate ACP content blocks
602
+
603
+ converted_content = to_acp_content_blocks(content)
604
+ # Send each content block as separate notifications
605
+ for block in converted_content:
606
+ match block:
607
+ case TextContentBlock(text=text):
608
+ await self.send_user_message(text)
609
+ case ImageContentBlock() as img_block:
610
+ await self.send_user_image(
611
+ data=img_block.data,
612
+ mime_type=img_block.mime_type,
613
+ uri=img_block.uri,
614
+ audience=img_block.annotations.audience
615
+ if img_block.annotations
616
+ else None,
617
+ last_modified=img_block.annotations.last_modified
618
+ if img_block.annotations
619
+ else None,
620
+ priority=img_block.annotations.priority
621
+ if img_block.annotations
622
+ else None,
623
+ )
624
+ case AudioContentBlock() as audio_block:
625
+ await self.send_user_audio(
626
+ data=audio_block.data,
627
+ mime_type=audio_block.mime_type,
628
+ audience=audio_block.annotations.audience
629
+ if audio_block.annotations
630
+ else None,
631
+ last_modified=audio_block.annotations.last_modified
632
+ if audio_block.annotations
633
+ else None,
634
+ priority=audio_block.annotations.priority
635
+ if audio_block.annotations
636
+ else None,
637
+ )
638
+ case ResourceContentBlock() as resource_block:
639
+ await self.send_user_resource(
640
+ uri=resource_block.uri,
641
+ name=resource_block.name,
642
+ description=resource_block.description,
643
+ mime_type=resource_block.mime_type,
644
+ size=resource_block.size,
645
+ title=resource_block.title,
646
+ audience=resource_block.annotations.audience
647
+ if resource_block.annotations
648
+ else None,
649
+ last_modified=resource_block.annotations.last_modified
650
+ if resource_block.annotations
651
+ else None,
652
+ priority=resource_block.annotations.priority
653
+ if resource_block.annotations
654
+ else None,
655
+ )
656
+ case EmbeddedResourceContentBlock() as embedded_block:
657
+ # Handle embedded resources with proper
658
+ # pattern matching
659
+ match embedded_block.resource:
660
+ case TextResourceContents(text=text):
661
+ await self.send_user_message(text)
662
+ case BlobResourceContents() as blob_resource:
663
+ blob_size = len(blob_resource.blob) * 3 // 4
664
+ size_mb = blob_size / (1024 * 1024)
665
+ mime = blob_resource.mime_type or "unknown"
666
+ msg = f"Embedded resource: {mime} ({size_mb:.2f} MB)"
667
+ await self.send_user_message(msg)
668
+ case _ as unreachable:
669
+ assert_never(unreachable) # ty: ignore[type-assertion-failure]
670
+ case _ as unreachable:
671
+ assert_never(unreachable) # ty: ignore[type-assertion-failure]
672
+
673
+ case ToolReturnPart(
674
+ content=content, tool_name=tool_name, tool_call_id=tool_call_id
675
+ ):
676
+ converted_content = to_acp_content_blocks(content)
677
+ tool_input = self._tool_call_inputs.get(tool_call_id, {})
678
+ await self.tool_call(
679
+ tool_name=tool_name,
680
+ tool_input=tool_input,
681
+ tool_output=converted_content,
682
+ status="completed",
683
+ tool_call_id=tool_call_id,
684
+ )
685
+ # Clean up stored input
686
+ self._tool_call_inputs.pop(tool_call_id, None)
687
+
688
+ case _:
689
+ typ = type(part).__name__
690
+ self.log.debug("Unhandled request part type", part_type=typ)
691
+
692
+ async def _replay_response(self, response: ModelResponse) -> None:
693
+ """Replay a ModelResponse by converting it to appropriate ACP notifications."""
694
+ from pydantic_ai import TextPart, ThinkingPart, ToolCallPart
695
+
696
+ for part in response.parts:
697
+ match part:
698
+ case TextPart(content=content):
699
+ await self.send_agent_text(content)
700
+
701
+ case ThinkingPart(content=content):
702
+ await self.send_agent_thought(content)
703
+
704
+ case ToolCallPart(tool_call_id=tool_call_id):
705
+ # Store tool call inputs for later use with ToolReturnPart
706
+ tool_input = part.args_as_dict()
707
+ self._tool_call_inputs[tool_call_id] = tool_input
708
+ # Skip sending notification - ACP protocol overrides previous
709
+ # tool call state
710
+
711
+ case _:
712
+ typ = type(part).__name__
713
+ self.log.debug("Unhandled response part type", part_type=typ)
714
+
715
+ async def send_agent_image(
716
+ self,
717
+ data: str | bytes,
718
+ mime_type: str,
719
+ *,
720
+ uri: str | None = None,
721
+ audience: Audience | None = None,
722
+ last_modified: datetime | str | None = None,
723
+ priority: float | None = None,
724
+ ) -> None:
725
+ """Send an image message notification.
726
+
727
+ Args:
728
+ data: Base64-encoded image data
729
+ mime_type: MIME type of the image (e.g., 'image/png')
730
+ uri: Optional URI reference for the image
731
+ audience: Optional audience for the image
732
+ last_modified: Optional last modified timestamp for the image
733
+ priority: Optional priority for the image
734
+ """
735
+ update = AgentMessageChunk.image(
736
+ data=data,
737
+ mime_type=mime_type,
738
+ uri=uri,
739
+ audience=audience,
740
+ last_modified=last_modified,
741
+ priority=priority,
742
+ )
743
+ notification = SessionNotification(session_id=self.id, update=update)
744
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
745
+
746
+ async def update_session_mode(self, mode_id: str) -> None:
747
+ """Send a session mode update notification.
748
+
749
+ Args:
750
+ mode_id: Unique identifier for the session mode
751
+ """
752
+ update = CurrentModeUpdate(current_mode_id=mode_id)
753
+ notification = SessionNotification(session_id=self.id, update=update)
754
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
755
+
756
+ # async def update_session_model(self, model_id: str) -> None:
757
+ # """Send a session model update notification.
758
+
759
+ # Args:
760
+ # model_id: Unique identifier for the model
761
+ # """
762
+ # update = CurrentModelUpdate(current_model_id=model_id)
763
+ # notification = SessionNotification(session_id=self.id, update=update)
764
+ # await self.client.session_update(notification)
765
+
766
+ async def send_agent_audio(
767
+ self,
768
+ data: str | bytes,
769
+ mime_type: str,
770
+ *,
771
+ audience: Audience | None = None,
772
+ last_modified: datetime | str | None = None,
773
+ priority: float | None = None,
774
+ ) -> None:
775
+ """Send an audio message notification.
776
+
777
+ Args:
778
+ data: Base64-encoded audio data
779
+ mime_type: MIME type of the audio (e.g., 'audio/wav')
780
+ audience: Optional audience for the audio
781
+ last_modified: Optional last modified timestamp for the audio
782
+ priority: Optional priority for the audio
783
+ """
784
+ update = AgentMessageChunk.audio(
785
+ data=data,
786
+ mime_type=mime_type,
787
+ last_modified=last_modified,
788
+ priority=priority,
789
+ audience=audience,
790
+ )
791
+ notification = SessionNotification(session_id=self.id, update=update)
792
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]
793
+
794
+ async def send_agent_resource(
795
+ self,
796
+ name: str,
797
+ uri: str,
798
+ *,
799
+ title: str | None = None,
800
+ description: str | None = None,
801
+ mime_type: str | None = None,
802
+ size: int | None = None,
803
+ audience: Audience | None = None,
804
+ last_modified: datetime | str | None = None,
805
+ priority: float | None = None,
806
+ ) -> None:
807
+ """Send a resource reference message notification.
808
+
809
+ Args:
810
+ name: Name of the resource
811
+ uri: URI of the resource
812
+ title: Optional title for the resource
813
+ description: Optional description of the resource
814
+ mime_type: Optional MIME type of the resource
815
+ size: Optional size of the resource in bytes
816
+ audience: Optional audience for the resource
817
+ last_modified: Optional last modified timestamp for the resource
818
+ priority: Optional priority for the resource
819
+ """
820
+ update = AgentMessageChunk.resource(
821
+ name=name,
822
+ uri=uri,
823
+ title=title,
824
+ description=description,
825
+ mime_type=mime_type,
826
+ size=size,
827
+ audience=audience,
828
+ last_modified=last_modified,
829
+ priority=priority,
830
+ )
831
+ notification = SessionNotification(session_id=self.id, update=update)
832
+ await self.client.session_update(notification) # pyright: ignore[reportArgumentType] # ty: ignore[invalid-argument-type]