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
@@ -3,13 +3,11 @@
3
3
  from agentpool_toolsets.config_creation import ConfigCreationTools
4
4
  from agentpool_toolsets.fsspec_toolset import FSSpecTools
5
5
  from agentpool_toolsets.notifications import NotificationsTools
6
- from agentpool_toolsets.semantic_memory_toolset import SemanticMemoryTools
7
6
  from agentpool_toolsets.vfs_toolset import VFSTools
8
7
 
9
8
  __all__ = [
10
9
  "ConfigCreationTools",
11
10
  "FSSpecTools",
12
11
  "NotificationsTools",
13
- "SemanticMemoryTools",
14
12
  "VFSTools",
15
13
  ]
@@ -4,30 +4,20 @@ from __future__ import annotations
4
4
 
5
5
 
6
6
  # Import provider classes
7
- from agentpool_toolsets.builtin.agent_management import AgentManagementTools
8
7
  from agentpool_toolsets.builtin.code import CodeTools
9
8
  from agentpool_toolsets.builtin.debug import DebugTools
10
- from agentpool_toolsets.builtin.execution_environment import ExecutionEnvironmentTools
11
- from agentpool_toolsets.builtin.history import HistoryTools
12
- from agentpool_toolsets.builtin.integration import IntegrationTools
9
+ from agentpool_toolsets.builtin.execution_environment import ProcessManagementTools
13
10
  from agentpool_toolsets.builtin.skills import SkillsTools
14
11
  from agentpool_toolsets.builtin.subagent_tools import SubagentTools
15
- from agentpool_toolsets.builtin.tool_management import ToolManagementTools
16
- from agentpool_toolsets.builtin.user_interaction import UserInteractionTools
17
12
  from agentpool_toolsets.builtin.workers import WorkersTools
18
13
 
19
14
 
20
15
  __all__ = [
21
16
  # Provider classes
22
- "AgentManagementTools",
23
17
  "CodeTools",
24
18
  "DebugTools",
25
- "ExecutionEnvironmentTools",
26
- "HistoryTools",
27
- "IntegrationTools",
19
+ "ProcessManagementTools",
28
20
  "SkillsTools",
29
21
  "SubagentTools",
30
- "ToolManagementTools",
31
- "UserInteractionTools",
32
22
  "WorkersTools",
33
23
  ]
@@ -13,15 +13,21 @@ from upathtools import is_directory
13
13
  from agentpool.agents.context import AgentContext # noqa: TC001
14
14
  from agentpool.log import get_logger
15
15
  from agentpool.resource_providers import ResourceProvider
16
- from agentpool_toolsets.fsspec_toolset.diagnostics import DiagnosticsManager
16
+ from agentpool_toolsets.fsspec_toolset.diagnostics import (
17
+ DiagnosticsManager,
18
+ format_diagnostics_table,
19
+ format_run_summary,
20
+ )
17
21
 
18
22
 
19
23
  if TYPE_CHECKING:
20
- from anyenv.lsp_servers import Diagnostic
24
+ from collections.abc import Sequence
25
+
21
26
  from exxec.base import ExecutionEnvironment
27
+ from fsspec.asyn import AsyncFileSystem
22
28
 
23
29
  from agentpool.tools.base import Tool
24
-
30
+ from agentpool_toolsets.fsspec_toolset.diagnostics import DiagnosticsConfig
25
31
 
26
32
  logger = get_logger(__name__)
27
33
 
@@ -87,51 +93,63 @@ class CodeTools(ResourceProvider):
87
93
  env: ExecutionEnvironment | None = None,
88
94
  name: str = "code",
89
95
  cwd: str | None = None,
96
+ diagnostics_config: DiagnosticsConfig | None = None,
90
97
  ) -> None:
91
98
  """Initialize with an optional execution environment.
92
99
 
93
100
  Args:
94
101
  env: Execution environment to operate on. If None, falls back to agent.env
95
102
  name: Name for this toolset provider
96
- cwd: Optional cwd to resolve relative paths against
103
+ cwd: Optional cwd to resolve relative paths against (falls back to env.cwd)
104
+ diagnostics_config: Configuration for diagnostic tools (server selection, etc.)
97
105
  """
106
+ super().__init__(name=name)
107
+
108
+ self._explicit_env = env
109
+ self._explicit_cwd = cwd
110
+ self._diagnostics_config = diagnostics_config
111
+ self._tools: list[Tool] | None = None
112
+
113
+ def _get_env(self, agent_ctx: AgentContext) -> ExecutionEnvironment | None:
114
+ """Get execution environment (explicit or from agent)."""
115
+ return self._explicit_env or agent_ctx.agent.env
116
+
117
+ def _get_cwd(self, agent_ctx: AgentContext) -> str | None:
118
+ """Get working directory (explicit, from env, or from agent.env)."""
119
+ if self._explicit_cwd:
120
+ return self._explicit_cwd
121
+ env = self._get_env(agent_ctx)
122
+ return env.cwd if env else None
123
+
124
+ def _get_fs(self, agent_ctx: AgentContext) -> AsyncFileSystem:
125
+ """Get filesystem (from env or fallback to local)."""
98
126
  from fsspec.asyn import AsyncFileSystem
99
127
  from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper
100
128
  from morefs.asyn_local import AsyncLocalFileSystem
101
129
 
102
- super().__init__(name=name)
103
-
104
- self.execution_env = env
105
- self.cwd = cwd or (env.cwd if env else None)
130
+ env = self._get_env(agent_ctx)
106
131
  fs = env.get_fs() if env else None
107
132
  match fs:
108
133
  case AsyncFileSystem():
109
- self.fs = fs
134
+ return fs
110
135
  case AbstractFileSystem():
111
- self.fs = AsyncFileSystemWrapper(fs)
112
- case None:
113
- self.fs = AsyncLocalFileSystem()
114
- self._tools: list[Tool] | None = None
115
- self._diagnostics: DiagnosticsManager | None = None
116
-
117
- def _get_env(self, agent_ctx: AgentContext) -> ExecutionEnvironment | None:
118
- """Get execution environment (explicit or from agent)."""
119
- return self.execution_env or agent_ctx.agent.env
136
+ return AsyncFileSystemWrapper(fs)
137
+ case _:
138
+ return AsyncLocalFileSystem()
120
139
 
121
140
  def _resolve_path(self, path: str, agent_ctx: AgentContext) -> str:
122
141
  """Resolve a potentially relative path to an absolute path.
123
142
 
124
- Gets cwd from self.cwd, execution_env.cwd, or agent.env.cwd.
143
+ Gets cwd from explicit cwd, env.cwd, or agent.env.cwd.
125
144
  If cwd is set and path is relative, resolves relative to cwd.
126
145
  Otherwise returns the path as-is.
127
146
  """
128
- env = self._get_env(agent_ctx)
129
- cwd = self.cwd or (env.cwd if env else None)
147
+ cwd = self._get_cwd(agent_ctx)
130
148
  if cwd and not (path.startswith("/") or (len(path) > 1 and path[1] == ":")):
131
149
  return str(Path(cwd) / path)
132
150
  return path
133
151
 
134
- async def get_tools(self) -> list[Tool]:
152
+ async def get_tools(self) -> Sequence[Tool]:
135
153
  """Get code analysis tools."""
136
154
  if self._tools is not None:
137
155
  return self._tools
@@ -160,8 +178,9 @@ class CodeTools(ResourceProvider):
160
178
  from anyenv.language_formatters import FormatterRegistry
161
179
 
162
180
  resolved = self._resolve_path(path, agent_ctx)
181
+ fs = self._get_fs(agent_ctx)
163
182
  try:
164
- content = await self.fs._cat_file(resolved)
183
+ content = await fs._cat_file(resolved)
165
184
  code = content.decode("utf-8") if isinstance(content, bytes) else content
166
185
  except FileNotFoundError:
167
186
  return f"❌ File not found: {path}"
@@ -189,7 +208,7 @@ class CodeTools(ResourceProvider):
189
208
  if result.success:
190
209
  # Write back if formatted
191
210
  if result.format_result.formatted and result.format_result.output:
192
- await self.fs._pipe_file(resolved, result.format_result.output.encode("utf-8"))
211
+ await fs._pipe_file(resolved, result.format_result.output.encode("utf-8"))
193
212
  changes = "formatted and saved"
194
213
  else:
195
214
  changes = "no changes needed"
@@ -271,6 +290,7 @@ class CodeTools(ResourceProvider):
271
290
  from ast_grep_py import SgRoot
272
291
 
273
292
  resolved = self._resolve_path(path, agent_ctx)
293
+ fs = self._get_fs(agent_ctx)
274
294
 
275
295
  # Detect language from extension
276
296
  language = _detect_language(path)
@@ -279,7 +299,7 @@ class CodeTools(ResourceProvider):
279
299
 
280
300
  # Read file
281
301
  try:
282
- content = await self.fs._cat_file(resolved)
302
+ content = await fs._cat_file(resolved)
283
303
  code = content.decode("utf-8") if isinstance(content, bytes) else content
284
304
  except FileNotFoundError:
285
305
  return {"error": f"File not found: {path}"}
@@ -318,7 +338,7 @@ class CodeTools(ResourceProvider):
318
338
  result["dry_run"] = dry_run
319
339
 
320
340
  if not dry_run:
321
- await self.fs._pipe_file(resolved, fixed_code.encode("utf-8"))
341
+ await fs._pipe_file(resolved, fixed_code.encode("utf-8"))
322
342
  result["written"] = True
323
343
 
324
344
  return result
@@ -343,29 +363,46 @@ class CodeTools(ResourceProvider):
343
363
  ones based on file extensions.
344
364
  """
345
365
  resolved = self._resolve_path(path, agent_ctx)
366
+ fs = self._get_fs(agent_ctx)
367
+ env = self._get_env(agent_ctx)
346
368
 
347
- # Get or create diagnostics manager
348
- if self._diagnostics is None:
349
- env = self._get_env(agent_ctx)
350
- if not env:
351
- return "Diagnostics unavailable: no execution environment configured"
352
- self._diagnostics = DiagnosticsManager(env)
369
+ if not env:
370
+ return "Diagnostics unavailable: no execution environment configured"
371
+
372
+ # Create diagnostics manager with config
373
+ manager = DiagnosticsManager(env, config=self._diagnostics_config)
374
+
375
+ # Progress callback that emits tool_call_progress events
376
+ async def progress_callback(
377
+ message: str,
378
+ *,
379
+ server_id: str | None = None,
380
+ command: str | None = None,
381
+ status: str = "running",
382
+ ) -> None:
383
+ items = []
384
+ if command:
385
+ items.append(f"```bash\n{command}\n```")
386
+ await agent_ctx.events.tool_call_progress(
387
+ message,
388
+ status="in_progress",
389
+ items=items if items else None,
390
+ )
353
391
 
354
392
  # Check if path is directory or file
355
393
  try:
356
- is_dir = await self.fs._isdir(resolved)
394
+ is_dir = await fs._isdir(resolved)
357
395
  except Exception: # noqa: BLE001
358
396
  is_dir = False
359
397
 
360
398
  if is_dir:
361
399
  # Collect all files in directory
362
400
  try:
363
- files = await self.fs._find(resolved, detail=True)
364
- # Filter to only include files (not directories)
401
+ files = await fs._find(resolved, detail=True)
365
402
  file_paths = [
366
- path
367
- for path, info in files.items() # pyright: ignore[reportAttributeAccessIssue]
368
- if not await is_directory(self.fs, path, entry_type=info["type"])
403
+ p
404
+ for p, info in files.items() # pyright: ignore[reportAttributeAccessIssue]
405
+ if not await is_directory(fs, p, entry_type=info["type"])
369
406
  ]
370
407
  except Exception as e: # noqa: BLE001
371
408
  return f"Error scanning directory: {e}"
@@ -373,26 +410,28 @@ class CodeTools(ResourceProvider):
373
410
  if not file_paths:
374
411
  return f"No files found in: {path}"
375
412
 
376
- all_diagnostics: list[Diagnostic] = []
377
- for file_path in file_paths:
378
- diagnostics = await self._diagnostics.run_for_file(file_path)
379
- all_diagnostics.extend(diagnostics)
413
+ result = await manager.run_for_files(file_paths, progress=progress_callback)
414
+ else:
415
+ # Single file
416
+ try:
417
+ result = await manager.run_for_file(resolved, progress=progress_callback)
418
+ except FileNotFoundError:
419
+ return f"File not found: {path}"
420
+ except Exception as e: # noqa: BLE001
421
+ return f"Error running diagnostics: {e}"
380
422
 
381
- if not all_diagnostics:
382
- return f"No issues found in {len(file_paths)} files"
423
+ # Format output
424
+ if not result.diagnostics:
425
+ summary = format_run_summary(result)
426
+ return f"No issues found. {summary}"
383
427
 
384
- formatted = self._diagnostics.format_diagnostics(all_diagnostics)
385
- return f"Found {len(all_diagnostics)} issues:\n{formatted}"
386
- # Single file
387
- try:
388
- diagnostics = await self._diagnostics.run_for_file(resolved)
389
- except FileNotFoundError:
390
- return f"File not found: {path}"
391
- except Exception as e: # noqa: BLE001
392
- return f"Error running diagnostics: {e}"
428
+ formatted = format_diagnostics_table(result.diagnostics)
429
+ summary = format_run_summary(result)
393
430
 
394
- if not diagnostics:
395
- return f"No issues found in: {path}"
431
+ await agent_ctx.events.tool_call_progress(
432
+ summary,
433
+ status="in_progress",
434
+ items=[formatted],
435
+ )
396
436
 
397
- formatted = self._diagnostics.format_diagnostics(diagnostics)
398
- return f"Found {len(diagnostics)} issues:\n{formatted}"
437
+ return f"{summary}\n\n{formatted}"
@@ -4,15 +4,23 @@ from __future__ import annotations
4
4
 
5
5
  from collections import deque
6
6
  from dataclasses import dataclass, field
7
+ from datetime import UTC, datetime
8
+ import json
7
9
  import logging
8
- from typing import Any, Literal
10
+ from typing import TYPE_CHECKING, Any, Literal
9
11
 
12
+ from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper
13
+ from fsspec.implementations.memory import MemoryFileSystem
10
14
  from pydantic_ai import RunContext # noqa: TC002
11
15
 
12
16
  from agentpool.agents.context import AgentContext # noqa: TC001
13
17
  from agentpool.resource_providers import StaticResourceProvider
14
18
 
15
19
 
20
+ if TYPE_CHECKING:
21
+ from fsspec.asyn import AsyncFileSystem
22
+
23
+
16
24
  LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
17
25
 
18
26
 
@@ -131,6 +139,8 @@ Available in namespace:
131
139
  - ctx: AgentContext (ctx.agent, ctx.pool, ctx.config, ctx.definition, etc.)
132
140
  - run_ctx: pydantic-ai RunContext (current run state)
133
141
  - me: Shortcut for ctx.agent (your Agent instance)
142
+ - save(key, value): Save an object to persist between calls
143
+ - state: Dict of saved objects from previous calls
134
144
 
135
145
  You can inspect yourself, the pool, other agents, your tools, and more.
136
146
  Write an async main() function that returns the result.
@@ -138,8 +148,8 @@ Write an async main() function that returns the result.
138
148
  Example - inspect your own tools:
139
149
  ```python
140
150
  async def main():
141
- tools = me.tools.list_tools()
142
- return [t.name for t in tools]
151
+ tools = await me.tools.list_tools()
152
+ return list(tools.keys())
143
153
  ```
144
154
 
145
155
  Example - check pool state:
@@ -159,30 +169,6 @@ async def main():
159
169
  """
160
170
 
161
171
 
162
- async def execute_introspection(ctx: AgentContext, run_ctx: RunContext[Any], code: str) -> str: # noqa: D417
163
- """Execute Python code with access to your own runtime context.
164
-
165
- This is a debugging/development tool that gives you full access to
166
- inspect and interact with your runtime environment.
167
-
168
- Args:
169
- code: Python code with async main() function to execute
170
-
171
- Returns:
172
- Result of execution or error message
173
- """
174
- # Build namespace with runtime context
175
- namespace: dict[str, Any] = {"ctx": ctx, "run_ctx": run_ctx, "me": ctx.agent}
176
- try:
177
- exec(code, namespace)
178
- if "main" not in namespace:
179
- return "Error: Code must define an async main() function"
180
- result = await namespace["main"]()
181
- return str(result) if result is not None else "Code executed successfully (no return value)"
182
- except Exception as e: # noqa: BLE001
183
- return f"Error executing code: {type(e).__name__}: {e}"
184
-
185
-
186
172
  # =============================================================================
187
173
  # Log Tools
188
174
  # =============================================================================
@@ -216,18 +202,6 @@ async def get_logs(
216
202
  return "\n".join(lines)
217
203
 
218
204
 
219
- async def clear_logs() -> str:
220
- """Clear all captured log entries from memory.
221
-
222
- Returns:
223
- Confirmation message
224
- """
225
- handler = get_memory_handler()
226
- count = len(handler.records)
227
- handler.clear()
228
- return f"Cleared {count} log entries"
229
-
230
-
231
205
  # =============================================================================
232
206
  # Path Tools
233
207
  # =============================================================================
@@ -268,24 +242,120 @@ class DebugTools(StaticResourceProvider):
268
242
  - Self-introspection via code execution with runtime context access
269
243
  - Log inspection and management
270
244
  - Platform path discovery
245
+ - Stateful namespace for persisting objects between introspection calls
271
246
  """
272
247
 
273
- def __init__(self, name: str = "debug", install_log_handler: bool = True) -> None:
248
+ def __init__(self, name: str = "debug") -> None:
274
249
  """Initialize debug tools.
275
250
 
276
251
  Args:
277
252
  name: Toolset name/namespace
278
- install_log_handler: Whether to install the memory log handler
279
253
  """
280
254
  super().__init__(name=name)
255
+ self._namespace_storage: dict[str, Any] = {} # Stateful storage for introspection
256
+ # Wrap MemoryFileSystem for async support
257
+ self._memory_fs = MemoryFileSystem()
258
+ self._fs = AsyncFileSystemWrapper(self._memory_fs)
281
259
 
282
- if install_log_handler:
283
- install_memory_handler()
284
-
285
- desc = (execute_introspection.__doc__ or "") + "\n\n" + INTROSPECTION_USAGE
260
+ desc = (self.execute_introspection.__doc__ or "") + "\n\n" + INTROSPECTION_USAGE
286
261
  self._tools = [
287
- self.create_tool(execute_introspection, category="other", description_override=desc),
288
- self.create_tool(get_logs, category="other", read_only=True, idempotent=True),
289
- self.create_tool(clear_logs, category="other"),
262
+ self.create_tool(
263
+ self.execute_introspection, category="other", description_override=desc
264
+ ),
290
265
  self.create_tool(get_platform_paths, category="other", read_only=True, idempotent=True),
291
266
  ]
267
+
268
+ def get_fs(self) -> AsyncFileSystem:
269
+ """Get filesystem view of script history and state.
270
+
271
+ Returns:
272
+ AsyncFileSystem containing:
273
+ - scripts/{timestamp}_{title}.py - Executed scripts
274
+ - scripts/{timestamp}_{title}.json - Execution metadata
275
+ """
276
+ return self._fs
277
+
278
+ async def execute_introspection( # noqa: D417
279
+ self, ctx: AgentContext, run_ctx: RunContext[Any], code: str, title: str
280
+ ) -> str:
281
+ """Execute Python code with access to your own runtime context.
282
+
283
+ This is a debugging/development tool that gives you full access to
284
+ inspect and interact with your runtime environment.
285
+
286
+ Args:
287
+ code: Python code with async main() function to execute
288
+ title: Short descriptive title for this script (3-4 words)
289
+
290
+ Returns:
291
+ Result of execution or error message
292
+ """
293
+ # Emit progress with the code being executed
294
+ await ctx.events.tool_call_progress(
295
+ title="Executing introspection code",
296
+ status="in_progress",
297
+ items=[f"```python\n{code}\n```"],
298
+ )
299
+
300
+ # Build namespace with runtime context and stateful storage
301
+ def save(key: str, value: Any) -> None:
302
+ """Save a value to persist between introspection calls."""
303
+ self._namespace_storage[key] = value
304
+
305
+ state = self._namespace_storage.copy()
306
+
307
+ namespace: dict[str, Any] = {
308
+ "ctx": ctx,
309
+ "run_ctx": run_ctx,
310
+ "me": ctx.agent,
311
+ "save": save,
312
+ "state": state,
313
+ }
314
+ start_time = datetime.now(UTC)
315
+ exit_code = 0
316
+ error_msg = None
317
+ result_str = None
318
+
319
+ try:
320
+ exec(code, namespace)
321
+ if "main" not in namespace:
322
+ return "Error: Code must define an async main() function"
323
+ result = await namespace["main"]()
324
+ result_str = (
325
+ str(result)
326
+ if result is not None
327
+ else "Code executed successfully (no return value)"
328
+ )
329
+
330
+ await ctx.events.tool_call_progress(
331
+ title="Executed introspection code successfully",
332
+ status="in_progress",
333
+ items=[f"```python\n{code}\n```\n\n```terminal\n{result_str}\n```"],
334
+ )
335
+ except Exception as e: # noqa: BLE001
336
+ exit_code = 1
337
+ error_msg = f"{type(e).__name__}: {e}"
338
+ result_str = error_msg
339
+ finally:
340
+ # Save to filesystem
341
+ end_time = datetime.now(UTC)
342
+ duration = (end_time - start_time).total_seconds()
343
+ timestamp = start_time.strftime("%Y%m%d_%H%M%S")
344
+
345
+ # Write script file
346
+ script_path = f"scripts/{timestamp}_{title}.py"
347
+ self._memory_fs.pipe(script_path, code.encode("utf-8"))
348
+
349
+ # Write metadata file
350
+ metadata = {
351
+ "title": title,
352
+ "timestamp": start_time.isoformat(),
353
+ "exit_code": exit_code,
354
+ "duration": duration,
355
+ "result": result_str,
356
+ "error": error_msg,
357
+ }
358
+ metadata_path = f"scripts/{timestamp}_{title}.json"
359
+ self._memory_fs.pipe(metadata_path, json.dumps(metadata, indent=2).encode("utf-8"))
360
+
361
+ return result_str