agentpool 2.1.9__py3-none-any.whl → 2.5.0__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.
Files changed (311) hide show
  1. acp/__init__.py +13 -4
  2. acp/acp_requests.py +20 -77
  3. acp/agent/connection.py +8 -0
  4. acp/agent/implementations/debug_server/debug_server.py +6 -2
  5. acp/agent/protocol.py +6 -0
  6. acp/bridge/README.md +15 -2
  7. acp/bridge/__init__.py +3 -2
  8. acp/bridge/__main__.py +60 -19
  9. acp/bridge/ws_server.py +173 -0
  10. acp/bridge/ws_server_cli.py +89 -0
  11. acp/client/connection.py +38 -29
  12. acp/client/implementations/default_client.py +3 -2
  13. acp/client/implementations/headless_client.py +2 -2
  14. acp/connection.py +2 -2
  15. acp/notifications.py +20 -50
  16. acp/schema/__init__.py +2 -0
  17. acp/schema/agent_responses.py +21 -0
  18. acp/schema/client_requests.py +3 -3
  19. acp/schema/session_state.py +63 -29
  20. acp/stdio.py +39 -9
  21. acp/task/supervisor.py +2 -2
  22. acp/transports.py +362 -2
  23. acp/utils.py +17 -4
  24. agentpool/__init__.py +6 -1
  25. agentpool/agents/__init__.py +2 -0
  26. agentpool/agents/acp_agent/acp_agent.py +407 -277
  27. agentpool/agents/acp_agent/acp_converters.py +196 -38
  28. agentpool/agents/acp_agent/client_handler.py +191 -26
  29. agentpool/agents/acp_agent/session_state.py +17 -6
  30. agentpool/agents/agent.py +607 -572
  31. agentpool/agents/agui_agent/__init__.py +0 -2
  32. agentpool/agents/agui_agent/agui_agent.py +176 -110
  33. agentpool/agents/agui_agent/agui_converters.py +0 -131
  34. agentpool/agents/agui_agent/helpers.py +3 -4
  35. agentpool/agents/base_agent.py +632 -17
  36. agentpool/agents/claude_code_agent/FORKING.md +191 -0
  37. agentpool/agents/claude_code_agent/__init__.py +13 -1
  38. agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
  39. agentpool/agents/claude_code_agent/converters.py +74 -143
  40. agentpool/agents/claude_code_agent/history.py +474 -0
  41. agentpool/agents/claude_code_agent/models.py +77 -0
  42. agentpool/agents/claude_code_agent/static_info.py +100 -0
  43. agentpool/agents/claude_code_agent/usage.py +242 -0
  44. agentpool/agents/context.py +40 -0
  45. agentpool/agents/events/__init__.py +24 -0
  46. agentpool/agents/events/builtin_handlers.py +67 -1
  47. agentpool/agents/events/event_emitter.py +32 -2
  48. agentpool/agents/events/events.py +104 -3
  49. agentpool/agents/events/infer_info.py +145 -0
  50. agentpool/agents/events/processors.py +254 -0
  51. agentpool/agents/interactions.py +41 -6
  52. agentpool/agents/modes.py +67 -0
  53. agentpool/agents/slashed_agent.py +5 -4
  54. agentpool/agents/tool_call_accumulator.py +213 -0
  55. agentpool/agents/tool_wrapping.py +18 -6
  56. agentpool/common_types.py +56 -21
  57. agentpool/config_resources/__init__.py +38 -1
  58. agentpool/config_resources/acp_assistant.yml +2 -2
  59. agentpool/config_resources/agents.yml +3 -0
  60. agentpool/config_resources/agents_template.yml +1 -0
  61. agentpool/config_resources/claude_code_agent.yml +10 -6
  62. agentpool/config_resources/external_acp_agents.yml +2 -1
  63. agentpool/delegation/base_team.py +4 -30
  64. agentpool/delegation/pool.py +136 -289
  65. agentpool/delegation/team.py +58 -57
  66. agentpool/delegation/teamrun.py +51 -55
  67. agentpool/diagnostics/__init__.py +53 -0
  68. agentpool/diagnostics/lsp_manager.py +1593 -0
  69. agentpool/diagnostics/lsp_proxy.py +41 -0
  70. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  71. agentpool/diagnostics/models.py +398 -0
  72. agentpool/functional/run.py +10 -4
  73. agentpool/mcp_server/__init__.py +0 -2
  74. agentpool/mcp_server/client.py +76 -32
  75. agentpool/mcp_server/conversions.py +54 -13
  76. agentpool/mcp_server/manager.py +34 -54
  77. agentpool/mcp_server/registries/official_registry_client.py +35 -1
  78. agentpool/mcp_server/tool_bridge.py +186 -139
  79. agentpool/messaging/__init__.py +0 -2
  80. agentpool/messaging/compaction.py +72 -197
  81. agentpool/messaging/connection_manager.py +11 -10
  82. agentpool/messaging/event_manager.py +5 -5
  83. agentpool/messaging/message_container.py +6 -30
  84. agentpool/messaging/message_history.py +99 -8
  85. agentpool/messaging/messagenode.py +52 -14
  86. agentpool/messaging/messages.py +54 -35
  87. agentpool/messaging/processing.py +12 -22
  88. agentpool/models/__init__.py +1 -1
  89. agentpool/models/acp_agents/base.py +6 -24
  90. agentpool/models/acp_agents/mcp_capable.py +126 -157
  91. agentpool/models/acp_agents/non_mcp.py +129 -95
  92. agentpool/models/agents.py +98 -76
  93. agentpool/models/agui_agents.py +1 -1
  94. agentpool/models/claude_code_agents.py +144 -19
  95. agentpool/models/file_parsing.py +0 -1
  96. agentpool/models/manifest.py +113 -50
  97. agentpool/prompts/conversion_manager.py +1 -1
  98. agentpool/prompts/prompts.py +5 -2
  99. agentpool/repomap.py +1 -1
  100. agentpool/resource_providers/__init__.py +11 -1
  101. agentpool/resource_providers/aggregating.py +56 -5
  102. agentpool/resource_providers/base.py +70 -4
  103. agentpool/resource_providers/codemode/code_executor.py +72 -5
  104. agentpool/resource_providers/codemode/helpers.py +2 -2
  105. agentpool/resource_providers/codemode/provider.py +64 -12
  106. agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
  107. agentpool/resource_providers/codemode/remote_provider.py +9 -12
  108. agentpool/resource_providers/filtering.py +3 -1
  109. agentpool/resource_providers/mcp_provider.py +89 -12
  110. agentpool/resource_providers/plan_provider.py +228 -46
  111. agentpool/resource_providers/pool.py +7 -3
  112. agentpool/resource_providers/resource_info.py +111 -0
  113. agentpool/resource_providers/static.py +4 -2
  114. agentpool/sessions/__init__.py +4 -1
  115. agentpool/sessions/manager.py +33 -5
  116. agentpool/sessions/models.py +59 -6
  117. agentpool/sessions/protocol.py +28 -0
  118. agentpool/sessions/session.py +11 -55
  119. agentpool/skills/registry.py +13 -8
  120. agentpool/storage/manager.py +572 -49
  121. agentpool/talk/registry.py +4 -4
  122. agentpool/talk/talk.py +9 -10
  123. agentpool/testing.py +538 -20
  124. agentpool/tool_impls/__init__.py +6 -0
  125. agentpool/tool_impls/agent_cli/__init__.py +42 -0
  126. agentpool/tool_impls/agent_cli/tool.py +95 -0
  127. agentpool/tool_impls/bash/__init__.py +64 -0
  128. agentpool/tool_impls/bash/helpers.py +35 -0
  129. agentpool/tool_impls/bash/tool.py +171 -0
  130. agentpool/tool_impls/delete_path/__init__.py +70 -0
  131. agentpool/tool_impls/delete_path/tool.py +142 -0
  132. agentpool/tool_impls/download_file/__init__.py +80 -0
  133. agentpool/tool_impls/download_file/tool.py +183 -0
  134. agentpool/tool_impls/execute_code/__init__.py +55 -0
  135. agentpool/tool_impls/execute_code/tool.py +163 -0
  136. agentpool/tool_impls/grep/__init__.py +80 -0
  137. agentpool/tool_impls/grep/tool.py +200 -0
  138. agentpool/tool_impls/list_directory/__init__.py +73 -0
  139. agentpool/tool_impls/list_directory/tool.py +197 -0
  140. agentpool/tool_impls/question/__init__.py +42 -0
  141. agentpool/tool_impls/question/tool.py +127 -0
  142. agentpool/tool_impls/read/__init__.py +104 -0
  143. agentpool/tool_impls/read/tool.py +305 -0
  144. agentpool/tools/__init__.py +2 -1
  145. agentpool/tools/base.py +114 -34
  146. agentpool/tools/manager.py +57 -1
  147. agentpool/ui/base.py +2 -2
  148. agentpool/ui/mock_provider.py +2 -2
  149. agentpool/ui/stdlib_provider.py +2 -2
  150. agentpool/utils/file_watcher.py +269 -0
  151. agentpool/utils/identifiers.py +121 -0
  152. agentpool/utils/pydantic_ai_helpers.py +46 -0
  153. agentpool/utils/streams.py +616 -2
  154. agentpool/utils/subprocess_utils.py +155 -0
  155. agentpool/utils/token_breakdown.py +461 -0
  156. agentpool/vfs_registry.py +7 -2
  157. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/METADATA +41 -27
  158. agentpool-2.5.0.dist-info/RECORD +579 -0
  159. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
  160. agentpool_cli/__main__.py +24 -0
  161. agentpool_cli/create.py +1 -1
  162. agentpool_cli/serve_acp.py +100 -21
  163. agentpool_cli/serve_agui.py +87 -0
  164. agentpool_cli/serve_opencode.py +119 -0
  165. agentpool_cli/ui.py +557 -0
  166. agentpool_commands/__init__.py +42 -5
  167. agentpool_commands/agents.py +75 -2
  168. agentpool_commands/history.py +62 -0
  169. agentpool_commands/mcp.py +176 -0
  170. agentpool_commands/models.py +56 -3
  171. agentpool_commands/pool.py +260 -0
  172. agentpool_commands/session.py +1 -1
  173. agentpool_commands/text_sharing/__init__.py +119 -0
  174. agentpool_commands/text_sharing/base.py +123 -0
  175. agentpool_commands/text_sharing/github_gist.py +80 -0
  176. agentpool_commands/text_sharing/opencode.py +462 -0
  177. agentpool_commands/text_sharing/paste_rs.py +59 -0
  178. agentpool_commands/text_sharing/pastebin.py +116 -0
  179. agentpool_commands/text_sharing/shittycodingagent.py +112 -0
  180. agentpool_commands/tools.py +57 -0
  181. agentpool_commands/utils.py +80 -30
  182. agentpool_config/__init__.py +30 -2
  183. agentpool_config/agentpool_tools.py +498 -0
  184. agentpool_config/builtin_tools.py +77 -22
  185. agentpool_config/commands.py +24 -1
  186. agentpool_config/compaction.py +258 -0
  187. agentpool_config/converters.py +1 -1
  188. agentpool_config/event_handlers.py +42 -0
  189. agentpool_config/events.py +1 -1
  190. agentpool_config/forward_targets.py +1 -4
  191. agentpool_config/jinja.py +3 -3
  192. agentpool_config/mcp_server.py +132 -6
  193. agentpool_config/nodes.py +1 -1
  194. agentpool_config/observability.py +44 -0
  195. agentpool_config/session.py +0 -3
  196. agentpool_config/storage.py +82 -38
  197. agentpool_config/task.py +3 -3
  198. agentpool_config/tools.py +11 -22
  199. agentpool_config/toolsets.py +109 -233
  200. agentpool_server/a2a_server/agent_worker.py +307 -0
  201. agentpool_server/a2a_server/server.py +23 -18
  202. agentpool_server/acp_server/acp_agent.py +234 -181
  203. agentpool_server/acp_server/commands/acp_commands.py +151 -156
  204. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
  205. agentpool_server/acp_server/event_converter.py +651 -0
  206. agentpool_server/acp_server/input_provider.py +53 -10
  207. agentpool_server/acp_server/server.py +24 -90
  208. agentpool_server/acp_server/session.py +173 -331
  209. agentpool_server/acp_server/session_manager.py +8 -34
  210. agentpool_server/agui_server/server.py +3 -1
  211. agentpool_server/mcp_server/server.py +5 -2
  212. agentpool_server/opencode_server/.rules +95 -0
  213. agentpool_server/opencode_server/ENDPOINTS.md +401 -0
  214. agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
  215. agentpool_server/opencode_server/__init__.py +19 -0
  216. agentpool_server/opencode_server/command_validation.py +172 -0
  217. agentpool_server/opencode_server/converters.py +975 -0
  218. agentpool_server/opencode_server/dependencies.py +24 -0
  219. agentpool_server/opencode_server/input_provider.py +421 -0
  220. agentpool_server/opencode_server/models/__init__.py +250 -0
  221. agentpool_server/opencode_server/models/agent.py +53 -0
  222. agentpool_server/opencode_server/models/app.py +72 -0
  223. agentpool_server/opencode_server/models/base.py +26 -0
  224. agentpool_server/opencode_server/models/common.py +23 -0
  225. agentpool_server/opencode_server/models/config.py +37 -0
  226. agentpool_server/opencode_server/models/events.py +821 -0
  227. agentpool_server/opencode_server/models/file.py +88 -0
  228. agentpool_server/opencode_server/models/mcp.py +44 -0
  229. agentpool_server/opencode_server/models/message.py +179 -0
  230. agentpool_server/opencode_server/models/parts.py +323 -0
  231. agentpool_server/opencode_server/models/provider.py +81 -0
  232. agentpool_server/opencode_server/models/pty.py +43 -0
  233. agentpool_server/opencode_server/models/question.py +56 -0
  234. agentpool_server/opencode_server/models/session.py +111 -0
  235. agentpool_server/opencode_server/routes/__init__.py +29 -0
  236. agentpool_server/opencode_server/routes/agent_routes.py +473 -0
  237. agentpool_server/opencode_server/routes/app_routes.py +202 -0
  238. agentpool_server/opencode_server/routes/config_routes.py +302 -0
  239. agentpool_server/opencode_server/routes/file_routes.py +571 -0
  240. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  241. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  242. agentpool_server/opencode_server/routes/message_routes.py +761 -0
  243. agentpool_server/opencode_server/routes/permission_routes.py +63 -0
  244. agentpool_server/opencode_server/routes/pty_routes.py +300 -0
  245. agentpool_server/opencode_server/routes/question_routes.py +128 -0
  246. agentpool_server/opencode_server/routes/session_routes.py +1276 -0
  247. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  248. agentpool_server/opencode_server/server.py +475 -0
  249. agentpool_server/opencode_server/state.py +151 -0
  250. agentpool_server/opencode_server/time_utils.py +8 -0
  251. agentpool_storage/__init__.py +12 -0
  252. agentpool_storage/base.py +184 -2
  253. agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
  254. agentpool_storage/claude_provider/__init__.py +42 -0
  255. agentpool_storage/claude_provider/provider.py +1089 -0
  256. agentpool_storage/file_provider.py +278 -15
  257. agentpool_storage/memory_provider.py +193 -12
  258. agentpool_storage/models.py +3 -0
  259. agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
  260. agentpool_storage/opencode_provider/__init__.py +16 -0
  261. agentpool_storage/opencode_provider/helpers.py +414 -0
  262. agentpool_storage/opencode_provider/provider.py +895 -0
  263. agentpool_storage/project_store.py +325 -0
  264. agentpool_storage/session_store.py +26 -6
  265. agentpool_storage/sql_provider/__init__.py +4 -2
  266. agentpool_storage/sql_provider/models.py +48 -0
  267. agentpool_storage/sql_provider/sql_provider.py +269 -3
  268. agentpool_storage/sql_provider/utils.py +12 -13
  269. agentpool_storage/zed_provider/__init__.py +16 -0
  270. agentpool_storage/zed_provider/helpers.py +281 -0
  271. agentpool_storage/zed_provider/models.py +130 -0
  272. agentpool_storage/zed_provider/provider.py +442 -0
  273. agentpool_storage/zed_provider.py +803 -0
  274. agentpool_toolsets/__init__.py +0 -2
  275. agentpool_toolsets/builtin/__init__.py +2 -12
  276. agentpool_toolsets/builtin/code.py +96 -57
  277. agentpool_toolsets/builtin/debug.py +118 -48
  278. agentpool_toolsets/builtin/execution_environment.py +115 -230
  279. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  280. agentpool_toolsets/builtin/skills.py +9 -4
  281. agentpool_toolsets/builtin/subagent_tools.py +64 -51
  282. agentpool_toolsets/builtin/workers.py +4 -2
  283. agentpool_toolsets/composio_toolset.py +2 -2
  284. agentpool_toolsets/entry_points.py +3 -1
  285. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  286. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  287. agentpool_toolsets/fsspec_toolset/grep.py +99 -7
  288. agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
  289. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  290. agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
  291. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  292. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  293. agentpool_toolsets/mcp_discovery/toolset.py +511 -0
  294. agentpool_toolsets/mcp_run_toolset.py +87 -12
  295. agentpool_toolsets/notifications.py +33 -33
  296. agentpool_toolsets/openapi.py +3 -1
  297. agentpool_toolsets/search_toolset.py +3 -1
  298. agentpool-2.1.9.dist-info/RECORD +0 -474
  299. agentpool_config/resources.py +0 -33
  300. agentpool_server/acp_server/acp_tools.py +0 -43
  301. agentpool_server/acp_server/commands/spawn.py +0 -210
  302. agentpool_storage/text_log_provider.py +0 -275
  303. agentpool_toolsets/builtin/agent_management.py +0 -239
  304. agentpool_toolsets/builtin/chain.py +0 -288
  305. agentpool_toolsets/builtin/history.py +0 -36
  306. agentpool_toolsets/builtin/integration.py +0 -85
  307. agentpool_toolsets/builtin/tool_management.py +0 -90
  308. agentpool_toolsets/builtin/user_interaction.py +0 -52
  309. agentpool_toolsets/semantic_memory_toolset.py +0 -536
  310. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
  311. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,7 +2,6 @@
2
2
 
3
3
  from agentpool.agents.agui_agent.agui_agent import AGUIAgent
4
4
  from agentpool.agents.agui_agent.agui_converters import (
5
- ToolCallAccumulator,
6
5
  agui_to_native_event,
7
6
  to_agui_input_content,
8
7
  to_agui_tool,
@@ -12,7 +11,6 @@ from agentpool.agents.agui_agent.chunk_transformer import ChunkTransformer
12
11
  __all__ = [
13
12
  "AGUIAgent",
14
13
  "ChunkTransformer",
15
- "ToolCallAccumulator",
16
14
  "agui_to_native_event",
17
15
  "to_agui_input_content",
18
16
  "to_agui_tool",
@@ -31,11 +31,11 @@ from agentpool.agents.agui_agent.chunk_transformer import ChunkTransformer
31
31
  from agentpool.agents.agui_agent.helpers import execute_tool_calls, parse_sse_stream
32
32
  from agentpool.agents.base_agent import BaseAgent
33
33
  from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent
34
+ from agentpool.agents.events.processors import FileTracker
34
35
  from agentpool.log import get_logger
35
36
  from agentpool.messaging import ChatMessage
36
- from agentpool.messaging.processing import prepare_prompts
37
- from agentpool.talk.stats import MessageStats
38
37
  from agentpool.tools import ToolManager
38
+ from agentpool.utils.token_breakdown import calculate_usage_from_parts
39
39
 
40
40
 
41
41
  if TYPE_CHECKING:
@@ -44,22 +44,26 @@ if TYPE_CHECKING:
44
44
  from types import TracebackType
45
45
 
46
46
  from ag_ui.core import Message, ToolMessage
47
- from evented.configs import EventConfig
47
+ from evented_config import EventConfig
48
+ from pydantic_ai import UserContent
49
+ from slashed import BaseCommand
50
+ from tokonomics.model_discovery.model_info import ModelInfo
48
51
 
49
- from agentpool.agents.base_agent import ToolConfirmationMode
50
52
  from agentpool.agents.context import AgentContext
51
53
  from agentpool.agents.events import RichAgentStreamEvent
54
+ from agentpool.agents.modes import ModeCategory, ModeInfo
52
55
  from agentpool.common_types import (
53
56
  BuiltinEventHandlerType,
54
57
  IndividualEventHandler,
55
- PromptCompatible,
56
58
  ToolType,
57
59
  )
58
60
  from agentpool.delegation import AgentPool
59
61
  from agentpool.messaging import MessageHistory
62
+ from agentpool.models.agui_agents import AGUIAgentConfig
60
63
  from agentpool.tools import Tool
61
64
  from agentpool.ui.base import InputProvider
62
65
  from agentpool_config.mcp_server import MCPServerConfig
66
+ from agentpool_config.nodes import ToolConfirmationMode
63
67
 
64
68
 
65
69
  logger = get_logger(__name__)
@@ -132,6 +136,7 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
132
136
  event_configs: Sequence[EventConfig] | None = None,
133
137
  event_handlers: Sequence[IndividualEventHandler | BuiltinEventHandlerType] | None = None,
134
138
  tool_confirmation_mode: ToolConfirmationMode = "per_tool",
139
+ commands: Sequence[BaseCommand] | None = None,
135
140
  ) -> None:
136
141
  """Initialize AG-UI agent client.
137
142
 
@@ -153,6 +158,7 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
153
158
  event_configs: Event trigger configurations
154
159
  event_handlers: Sequence of event handlers to register
155
160
  tool_confirmation_mode: Tool confirmation mode
161
+ commands: Slash commands
156
162
  """
157
163
  super().__init__(
158
164
  name=name,
@@ -164,6 +170,7 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
164
170
  event_configs=event_configs,
165
171
  tool_confirmation_mode=tool_confirmation_mode,
166
172
  event_handlers=event_handlers,
173
+ commands=commands,
167
174
  )
168
175
 
169
176
  # AG-UI specific configuration
@@ -187,6 +194,50 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
187
194
  # Chunk transformer for normalizing CHUNK events
188
195
  self._chunk_transformer = ChunkTransformer()
189
196
 
197
+ @classmethod
198
+ def from_config(
199
+ cls,
200
+ config: AGUIAgentConfig,
201
+ *,
202
+ event_handlers: Sequence[IndividualEventHandler | BuiltinEventHandlerType] | None = None,
203
+ input_provider: InputProvider | None = None,
204
+ agent_pool: AgentPool[Any] | None = None,
205
+ ) -> Self:
206
+ """Create an AGUIAgent from a config object.
207
+
208
+ This is the preferred way to instantiate an AGUIAgent from configuration.
209
+
210
+ Args:
211
+ config: AG-UI agent configuration
212
+ event_handlers: Optional event handlers (merged with config handlers)
213
+ input_provider: Optional input provider for user interactions
214
+ agent_pool: Optional agent pool for coordination
215
+
216
+ Returns:
217
+ Configured AGUIAgent instance
218
+ """
219
+ # Merge config-level handlers with provided handlers
220
+ config_handlers = config.get_event_handlers()
221
+ merged_handlers: list[IndividualEventHandler | BuiltinEventHandlerType] = [
222
+ *config_handlers,
223
+ *(event_handlers or []),
224
+ ]
225
+ return cls(
226
+ endpoint=config.endpoint,
227
+ name=config.name or "agui-agent",
228
+ description=config.description,
229
+ display_name=config.display_name,
230
+ event_handlers=merged_handlers or None,
231
+ timeout=config.timeout,
232
+ headers=config.headers,
233
+ startup_command=config.startup_command,
234
+ startup_delay=config.startup_delay,
235
+ tools=[tool_config.get_tool() for tool_config in config.tools],
236
+ mcp_servers=config.mcp_servers,
237
+ tool_confirmation_mode=config.requires_tool_confirmation,
238
+ agent_pool=agent_pool,
239
+ )
240
+
190
241
  def get_context(self, data: Any = None) -> AgentContext:
191
242
  """Create a new context for this agent.
192
243
 
@@ -200,7 +251,7 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
200
251
  from agentpool.models.agui_agents import AGUIAgentConfig
201
252
  from agentpool.models.manifest import AgentsManifest
202
253
 
203
- cfg = AGUIAgentConfig(
254
+ cfg = AGUIAgentConfig( # type: ignore[call-arg]
204
255
  name=self.name,
205
256
  description=self.description,
206
257
  display_name=self.display_name,
@@ -212,7 +263,14 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
212
263
  startup_delay=self._startup_delay,
213
264
  )
214
265
  defn = self.agent_pool.manifest if self.agent_pool else AgentsManifest()
215
- return AgentContext(node=self, pool=self.agent_pool, config=cfg, definition=defn, data=data)
266
+ return AgentContext(
267
+ node=self,
268
+ pool=self.agent_pool,
269
+ config=cfg,
270
+ definition=defn,
271
+ input_provider=self._input_provider,
272
+ data=data,
273
+ )
216
274
 
217
275
  async def __aenter__(self) -> Self:
218
276
  """Enter async context - initialize client and base resources."""
@@ -302,71 +360,38 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
302
360
  self._startup_process = None
303
361
  self.log.info("AG-UI server stopped")
304
362
 
305
- async def run(
306
- self,
307
- *prompts: PromptCompatible,
308
- message_id: str | None = None,
309
- input_provider: InputProvider | None = None,
310
- message_history: MessageHistory | None = None,
311
- **kwargs: Any,
312
- ) -> ChatMessage[str]:
313
- """Execute prompt against AG-UI agent.
314
-
315
- Sends the prompt to the AG-UI server and waits for completion.
316
- Events are collected via run_stream and event handlers are called.
317
- The final text content is returned as a ChatMessage.
318
-
319
- Args:
320
- prompts: Prompts to send (will be joined with spaces)
321
- message_id: Optional message id for the returned message
322
- input_provider: Optional input provider for tool confirmation requests
323
- message_history: Optional MessageHistory to use instead of agent's own
324
- **kwargs: Additional arguments (ignored for compatibility)
325
-
326
- Returns:
327
- ChatMessage containing the agent's aggregated text response
328
- """
329
- final_message: ChatMessage[str] | None = None
330
- async for event in self.run_stream(
331
- *prompts,
332
- message_id=message_id,
333
- input_provider=input_provider,
334
- message_history=message_history,
335
- ):
336
- if isinstance(event, StreamCompleteEvent):
337
- final_message = event.message
338
-
339
- if final_message is None:
340
- raise RuntimeError("No final message received from stream")
341
- return final_message
342
-
343
- async def run_stream( # noqa: PLR0915
363
+ async def _stream_events( # noqa: PLR0915
344
364
  self,
345
- *prompts: PromptCompatible,
365
+ prompts: list[UserContent],
366
+ *,
367
+ user_msg: ChatMessage[Any],
368
+ effective_parent_id: str | None,
346
369
  message_id: str | None = None,
370
+ conversation_id: str | None = None,
371
+ parent_id: str | None = None,
347
372
  input_provider: InputProvider | None = None,
348
373
  message_history: MessageHistory | None = None,
349
- **kwargs: Any,
374
+ deps: TDeps | None = None,
375
+ event_handlers: Sequence[IndividualEventHandler | BuiltinEventHandlerType] | None = None,
376
+ wait_for_connections: bool | None = None,
377
+ store_history: bool = True,
350
378
  ) -> AsyncIterator[RichAgentStreamEvent[str]]:
351
- """Execute prompt with streaming events.
352
-
353
- Sends the prompt to the remote AG-UI agent along with any registered tools.
354
- When the agent requests a tool call, the tool is executed locally and the
355
- result is sent back in a continuation request.
356
-
357
- Args:
358
- prompts: Prompts to send
359
- message_id: Optional message ID
360
- input_provider: Optional input provider for tool confirmation requests
361
- message_history: Optional MessageHistory to use instead of agent's own
362
- **kwargs: Additional arguments (ignored for compatibility)
363
-
364
- Yields:
365
- Native streaming events converted from AG-UI protocol
366
- """
367
379
  # Update input provider if provided
368
380
  if input_provider is not None:
369
381
  self._input_provider = input_provider
382
+
383
+ # Use provided event handlers or fall back to agent's handlers
384
+ if event_handlers is not None:
385
+ from anyenv import MultiEventHandler
386
+
387
+ from agentpool.agents.events import resolve_event_handlers
388
+
389
+ handler: MultiEventHandler[IndividualEventHandler] = MultiEventHandler(
390
+ resolve_event_handlers(event_handlers)
391
+ )
392
+ else:
393
+ handler = self.event_handler
394
+
370
395
  from ag_ui.core import (
371
396
  RunAgentInput,
372
397
  TextMessageChunkEvent,
@@ -379,22 +404,27 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
379
404
  )
380
405
 
381
406
  from agentpool.agents.agui_agent.agui_converters import (
382
- ToolCallAccumulator,
383
407
  agui_to_native_event,
384
408
  to_agui_input_content,
385
409
  to_agui_tool,
386
410
  )
411
+ from agentpool.agents.tool_call_accumulator import ToolCallAccumulator
387
412
 
388
- if not self._client or not self._thread_id:
413
+ if not self._client:
389
414
  msg = "Agent not initialized - use async context manager"
390
415
  raise RuntimeError(msg)
391
416
 
392
417
  # Reset cancellation state
393
418
  self._cancelled = False
394
- self._current_stream_task = asyncio.current_task()
419
+
420
+ # Conversation ID initialization handled by BaseAgent
421
+
422
+ # Set thread_id from conversation_id (needed for AG-UI protocol)
423
+ if self._thread_id is None:
424
+ self._thread_id = self.conversation_id
395
425
 
396
426
  conversation = message_history if message_history is not None else self.conversation
397
- user_msg, processed_prompts, _original_message = await prepare_prompts(*prompts)
427
+ processed_prompts = prompts
398
428
  self._run_id = str(uuid4()) # New run ID for each run
399
429
  self._chunk_transformer.reset() # Reset chunk transformer
400
430
  # Track messages in pydantic-ai format: ModelRequest -> ModelResponse -> ModelRequest...
@@ -406,15 +436,15 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
406
436
  current_response_parts: list[TextPart | ThinkingPart | ToolCallPart] = []
407
437
  text_chunks: list[str] = [] # For final content string
408
438
 
439
+ assert self.conversation_id is not None # Initialized by BaseAgent.run_stream()
409
440
  run_started = RunStartedEvent(
410
441
  thread_id=self._thread_id or self.conversation_id,
411
442
  run_id=self._run_id or str(uuid4()),
412
443
  agent_name=self.name,
413
444
  )
414
- for handler in self.event_handler._wrapped_handlers:
415
- await handler(None, run_started)
416
- yield run_started
417
445
 
446
+ await handler(None, run_started)
447
+ yield run_started
418
448
  # Get pending parts from conversation and convert them
419
449
  pending_parts = conversation.get_pending_parts()
420
450
  pending_content = to_agui_input_content(pending_parts)
@@ -432,6 +462,8 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
432
462
  tool_accumulator = ToolCallAccumulator()
433
463
  pending_tool_results: list[ToolMessage] = []
434
464
  self.log.debug("Sending prompt to AG-UI agent", tool_names=[t.name for t in agui_tools])
465
+ # Track files modified during this run
466
+ file_tracker = FileTracker()
435
467
  # Loop to handle tool calls - agent may request multiple rounds
436
468
  try:
437
469
  while True:
@@ -509,25 +541,24 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
509
541
 
510
542
  # Convert to native event and distribute to handlers
511
543
  if native_event := agui_to_native_event(event):
544
+ # Track file modifications
545
+ file_tracker.process_event(native_event)
512
546
  # Check for queued custom events first
513
547
  while not self._event_queue.empty():
514
548
  try:
515
549
  custom_event = self._event_queue.get_nowait()
516
- for handler in self.event_handler._wrapped_handlers:
517
- await handler(None, custom_event)
550
+ await handler(None, custom_event)
518
551
  yield custom_event
519
552
  except asyncio.QueueEmpty:
520
553
  break
521
554
  # Distribute to handlers
522
- for handler in self.event_handler._wrapped_handlers:
523
- await handler(None, native_event)
555
+ await handler(None, native_event)
524
556
  yield native_event
525
557
 
526
558
  # Flush any pending chunk events at end of stream
527
559
  for event in self._chunk_transformer.flush():
528
560
  if native_event := agui_to_native_event(event):
529
- for handler in self.event_handler._wrapped_handlers:
530
- await handler(None, native_event)
561
+ await handler(None, native_event)
531
562
  yield native_event
532
563
 
533
564
  except httpx.HTTPError:
@@ -592,14 +623,14 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
592
623
  name=self.name,
593
624
  message_id=message_id or str(uuid4()),
594
625
  conversation_id=self.conversation_id,
626
+ parent_id=user_msg.message_id,
595
627
  messages=model_messages,
596
628
  finish_reason="stop",
629
+ metadata=file_tracker.get_metadata(),
597
630
  )
598
631
  complete_event = StreamCompleteEvent(message=final_message)
599
- for handler in self.event_handler._wrapped_handlers:
600
- await handler(None, complete_event)
632
+ await handler(None, complete_event)
601
633
  yield complete_event
602
- self._current_stream_task = None
603
634
  return
604
635
 
605
636
  # Flush any remaining response parts
@@ -610,56 +641,91 @@ class AGUIAgent[TDeps = None](BaseAgent[TDeps, str]):
610
641
  while not self._event_queue.empty():
611
642
  try:
612
643
  queued_event = self._event_queue.get_nowait()
613
- for handler in self.event_handler._wrapped_handlers:
614
- await handler(None, queued_event)
644
+ await handler(None, queued_event)
615
645
  yield queued_event
616
646
  except asyncio.QueueEmpty:
617
647
  break
618
648
 
619
649
  text_content = "".join(text_chunks)
650
+
651
+ # Calculate approximate token usage from what we can observe
652
+ input_parts = [*processed_prompts, *pending_parts]
653
+ usage, cost_info = await calculate_usage_from_parts(
654
+ input_parts=input_parts,
655
+ response_parts=current_response_parts,
656
+ text_content=text_content,
657
+ model_name=self.model_name,
658
+ )
659
+
620
660
  final_message = ChatMessage[str](
621
661
  content=text_content,
622
662
  role="assistant",
623
663
  name=self.name,
624
664
  message_id=message_id or str(uuid4()),
625
665
  conversation_id=self.conversation_id,
666
+ parent_id=user_msg.message_id,
626
667
  messages=model_messages,
668
+ metadata=file_tracker.get_metadata(),
669
+ usage=usage,
670
+ cost_info=cost_info,
627
671
  )
628
672
  complete_event = StreamCompleteEvent(message=final_message)
629
- for handler in self.event_handler._wrapped_handlers:
630
- await handler(None, complete_event)
631
- yield complete_event
632
- # Record to conversation history
633
- conversation.add_chat_messages([user_msg, final_message])
634
-
635
- async def run_iter(
636
- self,
637
- *prompt_groups: Sequence[PromptCompatible],
638
- message_id: str | None = None,
639
- **kwargs: Any,
640
- ) -> AsyncIterator[ChatMessage[str]]:
641
- """Execute multiple prompt groups sequentially.
642
-
643
- Args:
644
- prompt_groups: Groups of prompts to execute
645
- message_id: Optional message ID base
646
- **kwargs: Additional arguments (ignored for compatibility)
647
-
648
- Yields:
649
- ChatMessage for each completed prompt group
650
- """
651
- for i, prompts in enumerate(prompt_groups):
652
- mid = f"{message_id or 'msg'}_{i}" if message_id else None
653
- yield await self.run(*prompts, message_id=mid)
673
+ await handler(None, complete_event)
674
+ yield complete_event # Post-processing handled by base class
654
675
 
655
676
  @property
656
677
  def model_name(self) -> str | None:
657
678
  """Get model name (AG-UI doesn't expose this)."""
658
679
  return None
659
680
 
660
- async def get_stats(self) -> MessageStats:
661
- """Get message statistics for this node."""
662
- return MessageStats()
681
+ async def set_model(self, model: str) -> None:
682
+ """Set model (no-op for AG-UI as model is controlled by remote server)."""
683
+ # AG-UI agents don't support model selection - the model is
684
+ # determined by the remote server configuration
685
+
686
+ async def get_available_models(self) -> list[ModelInfo] | None:
687
+ """Get available models for AG-UI agent.
688
+
689
+ AG-UI doesn't expose model information, so returns a placeholder model
690
+ indicating the model is determined by the remote server.
691
+
692
+ Returns:
693
+ List with a single placeholder ModelInfo
694
+ """
695
+ from tokonomics.model_discovery.model_info import ModelInfo
696
+
697
+ return [
698
+ ModelInfo(
699
+ id="server-determined",
700
+ name="Determined by server",
701
+ description="The model is determined by the remote AG-UI server",
702
+ )
703
+ ]
704
+
705
+ async def get_modes(self) -> list[ModeCategory]:
706
+ """Get available modes for AG-UI agent.
707
+
708
+ AG-UI doesn't expose any configurable modes - model is server-controlled.
709
+
710
+ Returns:
711
+ Empty list - no modes supported
712
+ """
713
+ return []
714
+
715
+ async def set_mode(self, mode: ModeInfo | str, category_id: str | None = None) -> None:
716
+ """Set a mode for AG-UI agent.
717
+
718
+ AG-UI doesn't support mode switching - model is controlled by remote server.
719
+
720
+ Args:
721
+ mode: The mode to set (not supported)
722
+ category_id: Category ID (not supported)
723
+
724
+ Raises:
725
+ ValueError: Always - AG-UI doesn't support modes
726
+ """
727
+ msg = "AG-UI agent does not support mode switching - model is controlled by remote server"
728
+ raise ValueError(msg)
663
729
 
664
730
 
665
731
  if __name__ == "__main__":
@@ -12,7 +12,6 @@ from __future__ import annotations
12
12
  import base64
13
13
  from typing import TYPE_CHECKING, Any
14
14
 
15
- import anyenv
16
15
  from pydantic_ai import (
17
16
  AudioUrl,
18
17
  BinaryContent,
@@ -291,133 +290,3 @@ def to_agui_tool(tool: Tool) -> AGUITool:
291
290
  description=func_schema.get("description", ""),
292
291
  parameters=func_schema.get("parameters", {"type": "object", "properties": {}}),
293
292
  )
294
-
295
-
296
- def _repair_partial_json(buffer: str) -> str:
297
- """Attempt to repair truncated JSON for preview purposes.
298
-
299
- Handles common truncation cases:
300
- - Unclosed strings
301
- - Missing closing braces/brackets
302
- - Trailing commas
303
-
304
- Args:
305
- buffer: Potentially incomplete JSON string
306
-
307
- Returns:
308
- Repaired JSON string (may still be invalid in edge cases)
309
- """
310
- if not buffer:
311
- return "{}"
312
-
313
- result = buffer.rstrip()
314
-
315
- # Check if we're in the middle of a string by counting unescaped quotes
316
- in_string = False
317
- i = 0
318
- while i < len(result):
319
- char = result[i]
320
- if char == "\\" and i + 1 < len(result):
321
- i += 2 # Skip escaped character
322
- continue
323
- if char == '"':
324
- in_string = not in_string
325
- i += 1
326
-
327
- # Close unclosed string
328
- if in_string:
329
- result += '"'
330
-
331
- # Remove trailing comma (invalid JSON)
332
- result = result.rstrip()
333
- if result.endswith(","):
334
- result = result[:-1]
335
-
336
- # Balance braces and brackets
337
- open_braces = result.count("{") - result.count("}")
338
- open_brackets = result.count("[") - result.count("]")
339
-
340
- result += "]" * max(0, open_brackets)
341
- result += "}" * max(0, open_braces)
342
-
343
- return result
344
-
345
-
346
- class ToolCallAccumulator:
347
- """Accumulates streamed tool call arguments.
348
-
349
- AG-UI streams tool call arguments as deltas, this class accumulates them
350
- and provides the complete arguments when the tool call ends.
351
- """
352
-
353
- def __init__(self) -> None:
354
- self._calls: dict[str, dict[str, Any]] = {}
355
-
356
- def start(self, tool_call_id: str, tool_name: str) -> None:
357
- """Start tracking a new tool call."""
358
- self._calls[tool_call_id] = {"name": tool_name, "args_buffer": ""}
359
-
360
- def add_args(self, tool_call_id: str, delta: str) -> None:
361
- """Add argument delta to a tool call."""
362
- if tool_call_id in self._calls:
363
- self._calls[tool_call_id]["args_buffer"] += delta
364
-
365
- def complete(self, tool_call_id: str) -> tuple[str, dict[str, Any]] | None:
366
- """Complete a tool call and return (tool_name, parsed_args).
367
-
368
- Returns:
369
- Tuple of (tool_name, args_dict) or None if call not found
370
- """
371
- if tool_call_id not in self._calls:
372
- return None
373
-
374
- call_data = self._calls.pop(tool_call_id)
375
- args_str = call_data["args_buffer"]
376
- try:
377
- args = anyenv.load_json(args_str) if args_str else {}
378
- except anyenv.JsonLoadError:
379
- args = {"raw": args_str}
380
- return call_data["name"], args
381
-
382
- def get_pending(self, tool_call_id: str) -> tuple[str, str] | None:
383
- """Get pending call data (tool_name, args_buffer) without completing."""
384
- if tool_call_id not in self._calls:
385
- return None
386
- data = self._calls[tool_call_id]
387
- return data["name"], data["args_buffer"]
388
-
389
- def get_partial_args(self, tool_call_id: str) -> dict[str, Any]:
390
- """Get best-effort parsed args from incomplete JSON stream.
391
-
392
- Uses heuristics to complete truncated JSON for preview purposes.
393
- Handles unclosed strings, missing braces/brackets, and trailing commas.
394
-
395
- Args:
396
- tool_call_id: Tool call ID
397
-
398
- Returns:
399
- Partially parsed arguments or empty dict
400
- """
401
- if tool_call_id not in self._calls:
402
- return {}
403
-
404
- buffer = self._calls[tool_call_id]["args_buffer"]
405
- if not buffer:
406
- return {}
407
-
408
- # Try direct parse first
409
- try:
410
- return anyenv.load_json(buffer)
411
- except anyenv.JsonLoadError:
412
- pass
413
-
414
- # Try to repair truncated JSON
415
- try:
416
- repaired = _repair_partial_json(buffer)
417
- return anyenv.load_json(repaired)
418
- except anyenv.JsonLoadError:
419
- return {}
420
-
421
- def clear(self) -> None:
422
- """Clear all pending tool calls."""
423
- self._calls.clear()
@@ -16,8 +16,7 @@ if TYPE_CHECKING:
16
16
  from ag_ui.core import Event, ToolMessage
17
17
  import httpx
18
18
 
19
- from agentpool.agents.context import ConfirmationResult
20
- from agentpool.messaging.context import NodeContext
19
+ from agentpool.agents.context import AgentContext, ConfirmationResult
21
20
  from agentpool.tools import Tool
22
21
  from agentpool.ui.base import InputProvider
23
22
  from agentpool_config.nodes import ToolConfirmationMode
@@ -51,7 +50,7 @@ async def _get_tool_confirmation(
51
50
  tool: Tool,
52
51
  args: dict[str, Any],
53
52
  input_provider: InputProvider,
54
- context: NodeContext[Any],
53
+ context: AgentContext[Any],
55
54
  ) -> ConfirmationResult:
56
55
  """Get confirmation for tool execution.
57
56
 
@@ -78,7 +77,7 @@ async def execute_tool_calls(
78
77
  *,
79
78
  confirmation_mode: ToolConfirmationMode = "never",
80
79
  input_provider: InputProvider | None = None,
81
- context: NodeContext[Any] | None = None,
80
+ context: AgentContext[Any] | None = None,
82
81
  ) -> list[ToolMessage]:
83
82
  """Execute tool calls locally and return results.
84
83