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
@@ -6,12 +6,16 @@ from collections.abc import Callable
6
6
  import inspect
7
7
  from pathlib import Path
8
8
  import re
9
- from typing import Annotated, Any, Literal
9
+ from typing import TYPE_CHECKING, Annotated, Any, Literal
10
10
 
11
11
  from pydantic import ConfigDict, Field, ImportString
12
12
  from schemez import Schema
13
13
 
14
14
 
15
+ if TYPE_CHECKING:
16
+ from slashed import Command
17
+
18
+
15
19
  class BaseCommandConfig(Schema):
16
20
  """Base configuration for commands."""
17
21
 
@@ -43,6 +47,25 @@ class BaseCommandConfig(Schema):
43
47
  """
44
48
  raise NotImplementedError
45
49
 
50
+ def get_slashed_command(self, category: str = "manifest") -> Command:
51
+ """Create a slashed Command from this configuration.
52
+
53
+ Args:
54
+ category: Category to assign to the command
55
+
56
+ Returns:
57
+ A slashed Command instance ready for registration
58
+ """
59
+ from slashed import Command
60
+
61
+ func = self.get_callable()
62
+ return Command(
63
+ func,
64
+ name=self.name,
65
+ description=self.description,
66
+ category=category,
67
+ )
68
+
46
69
 
47
70
  class StaticCommandConfig(BaseCommandConfig):
48
71
  """Static command with inline content."""
@@ -0,0 +1,258 @@
1
+ """Compaction configuration for message history management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Annotated, Any, Literal
6
+
7
+ from pydantic import Field
8
+ from schemez import Schema
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from agentpool.messaging.compaction import CompactionPipeline, CompactionStep
13
+
14
+
15
+ class FilterThinkingConfig(Schema):
16
+ """Configuration for FilterThinking step."""
17
+
18
+ type: Literal["filter_thinking"] = "filter_thinking"
19
+
20
+ def build(self) -> CompactionStep:
21
+ from agentpool.messaging.compaction import FilterThinking
22
+
23
+ return FilterThinking()
24
+
25
+
26
+ class FilterRetryPromptsConfig(Schema):
27
+ """Configuration for FilterRetryPrompts step."""
28
+
29
+ type: Literal["filter_retry_prompts"] = "filter_retry_prompts"
30
+
31
+ def build(self) -> CompactionStep:
32
+ from agentpool.messaging.compaction import FilterRetryPrompts
33
+
34
+ return FilterRetryPrompts()
35
+
36
+
37
+ class FilterBinaryContentConfig(Schema):
38
+ """Configuration for FilterBinaryContent step."""
39
+
40
+ type: Literal["filter_binary"] = "filter_binary"
41
+ keep_references: bool = False
42
+
43
+ def build(self) -> CompactionStep:
44
+ from agentpool.messaging.compaction import FilterBinaryContent
45
+
46
+ return FilterBinaryContent(keep_references=self.keep_references)
47
+
48
+
49
+ class FilterToolCallsConfig(Schema):
50
+ """Configuration for FilterToolCalls step."""
51
+
52
+ type: Literal["filter_tools"] = "filter_tools"
53
+ exclude_tools: list[str] = Field(default_factory=list)
54
+ include_only: list[str] | None = None
55
+
56
+ def build(self) -> CompactionStep:
57
+ from agentpool.messaging.compaction import FilterToolCalls
58
+
59
+ return FilterToolCalls(exclude_tools=self.exclude_tools, include_only=self.include_only)
60
+
61
+
62
+ class FilterEmptyMessagesConfig(Schema):
63
+ """Configuration for FilterEmptyMessages step."""
64
+
65
+ type: Literal["filter_empty"] = "filter_empty"
66
+
67
+ def build(self) -> CompactionStep:
68
+ from agentpool.messaging.compaction import FilterEmptyMessages
69
+
70
+ return FilterEmptyMessages()
71
+
72
+
73
+ class TruncateToolOutputsConfig(Schema):
74
+ """Configuration for TruncateToolOutputs step."""
75
+
76
+ type: Literal["truncate_tool_outputs"] = "truncate_tool_outputs"
77
+ max_length: int = 2000
78
+ suffix: str = "\n... [truncated]"
79
+
80
+ def build(self) -> CompactionStep:
81
+ from agentpool.messaging.compaction import TruncateToolOutputs
82
+
83
+ return TruncateToolOutputs(max_length=self.max_length, suffix=self.suffix)
84
+
85
+
86
+ class TruncateTextPartsConfig(Schema):
87
+ """Configuration for TruncateTextParts step."""
88
+
89
+ type: Literal["truncate_text"] = "truncate_text"
90
+ max_length: int = 5000
91
+ suffix: str = "\n... [truncated]"
92
+
93
+ def build(self) -> CompactionStep:
94
+ from agentpool.messaging.compaction import TruncateTextParts
95
+
96
+ return TruncateTextParts(max_length=self.max_length, suffix=self.suffix)
97
+
98
+
99
+ class KeepLastMessagesConfig(Schema):
100
+ """Configuration for KeepLastMessages step."""
101
+
102
+ type: Literal["keep_last"] = "keep_last"
103
+ count: int = 10
104
+ count_pairs: bool = True
105
+
106
+ def build(self) -> CompactionStep:
107
+ from agentpool.messaging.compaction import KeepLastMessages
108
+
109
+ return KeepLastMessages(count=self.count, count_pairs=self.count_pairs)
110
+
111
+
112
+ class KeepFirstMessagesConfig(Schema):
113
+ """Configuration for KeepFirstMessages step."""
114
+
115
+ type: Literal["keep_first"] = "keep_first"
116
+ count: int = 2
117
+
118
+ def build(self) -> CompactionStep:
119
+ from agentpool.messaging.compaction import KeepFirstMessages
120
+
121
+ return KeepFirstMessages(count=self.count)
122
+
123
+
124
+ class KeepFirstAndLastConfig(Schema):
125
+ """Configuration for KeepFirstAndLast step."""
126
+
127
+ type: Literal["keep_first_last"] = "keep_first_last"
128
+ first_count: int = 2
129
+ last_count: int = 5
130
+
131
+ def build(self) -> CompactionStep:
132
+ from agentpool.messaging.compaction import KeepFirstAndLast
133
+
134
+ return KeepFirstAndLast(first_count=self.first_count, last_count=self.last_count)
135
+
136
+
137
+ class TokenBudgetConfig(Schema):
138
+ """Configuration for TokenBudget step."""
139
+
140
+ type: Literal["token_budget"] = "token_budget"
141
+ max_tokens: int = 4000
142
+ model: str = "gpt-4o"
143
+
144
+ def build(self) -> CompactionStep:
145
+ from agentpool.messaging.compaction import TokenBudget
146
+
147
+ return TokenBudget(max_tokens=self.max_tokens, model=self.model)
148
+
149
+
150
+ class SummarizeConfig(Schema):
151
+ """Configuration for Summarize step."""
152
+
153
+ type: Literal["summarize"] = "summarize"
154
+ model: str = "openai:gpt-4o-mini"
155
+ threshold: int = 15
156
+ keep_recent: int = 5
157
+ summary_prompt: str | None = None
158
+
159
+ def build(self) -> CompactionStep:
160
+ from agentpool.messaging.compaction import Summarize
161
+
162
+ kwargs: dict[str, Any] = {
163
+ "model": self.model,
164
+ "threshold": self.threshold,
165
+ "keep_recent": self.keep_recent,
166
+ }
167
+ if self.summary_prompt:
168
+ kwargs["summary_prompt"] = self.summary_prompt
169
+ return Summarize(**kwargs)
170
+
171
+
172
+ class WhenMessageCountExceedsConfig(Schema):
173
+ """Configuration for WhenMessageCountExceeds wrapper."""
174
+
175
+ type: Literal["when_count_exceeds"] = "when_count_exceeds"
176
+ threshold: int = 20
177
+ step: "CompactionStepConfig"
178
+
179
+ def build(self) -> CompactionStep:
180
+ from agentpool.messaging.compaction import WhenMessageCountExceeds
181
+
182
+ return WhenMessageCountExceeds(step=self.step.build(), threshold=self.threshold)
183
+
184
+
185
+ # Union of all config types with discriminator
186
+ CompactionStepConfig = Annotated[
187
+ FilterThinkingConfig
188
+ | FilterRetryPromptsConfig
189
+ | FilterBinaryContentConfig
190
+ | FilterToolCallsConfig
191
+ | FilterEmptyMessagesConfig
192
+ | TruncateToolOutputsConfig
193
+ | TruncateTextPartsConfig
194
+ | KeepLastMessagesConfig
195
+ | KeepFirstMessagesConfig
196
+ | KeepFirstAndLastConfig
197
+ | TokenBudgetConfig
198
+ | SummarizeConfig
199
+ | WhenMessageCountExceedsConfig,
200
+ Field(discriminator="type"),
201
+ ]
202
+
203
+ # Update forward reference
204
+ WhenMessageCountExceedsConfig.model_rebuild()
205
+
206
+
207
+ class CompactionConfig(Schema):
208
+ """Configuration for message compaction/summarization.
209
+
210
+ Example YAML:
211
+ ```yaml
212
+ compaction:
213
+ steps:
214
+ - type: filter_thinking
215
+ - type: truncate_tool_outputs
216
+ max_length: 1000
217
+ - type: keep_last
218
+ count: 10
219
+ ```
220
+
221
+ Or use a preset:
222
+ ```yaml
223
+ compaction:
224
+ preset: balanced
225
+ ```
226
+ """
227
+
228
+ preset: Literal["minimal", "balanced", "summarizing"] | None = None
229
+ """Use a predefined compaction pipeline.
230
+
231
+ - minimal: Aggressively minimize context (filter thinking, truncate, keep last 10)
232
+ - balanced: Balance context preservation with size (filter thinking, truncate,
233
+ keep first 2 + last 8)
234
+ - summarizing: Summarize old messages (filter thinking, summarize when > 20 messages)
235
+ """
236
+
237
+ steps: list[CompactionStepConfig] = Field(default_factory=list)
238
+ """Ordered list of compaction steps to apply (ignored if preset is set)."""
239
+
240
+ def build(self) -> CompactionPipeline:
241
+ """Build a CompactionPipeline from this configuration."""
242
+ from agentpool.messaging.compaction import (
243
+ CompactionPipeline,
244
+ balanced_context,
245
+ minimal_context,
246
+ summarizing_context,
247
+ )
248
+
249
+ if self.preset:
250
+ match self.preset:
251
+ case "minimal":
252
+ return minimal_context()
253
+ case "balanced":
254
+ return balanced_context()
255
+ case "summarizing":
256
+ return summarizing_context()
257
+
258
+ return CompactionPipeline(steps=[step.build() for step in self.steps])
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from docler.configs import ConverterConfig
5
+ from docler_config import ConverterConfig
6
6
  from pydantic import ConfigDict, Field
7
7
  from schemez import Schema
8
8
 
@@ -392,6 +392,47 @@ class CallbackEventHandlerConfig(BaseEventHandlerConfig):
392
392
  return import_callable(self.import_path)
393
393
 
394
394
 
395
+ class FileStreamEventHandlerConfig(BaseEventHandlerConfig):
396
+ """Configuration for streaming agent output to a file."""
397
+
398
+ model_config = ConfigDict(title="File Stream Event Handler")
399
+
400
+ type: Literal["file"] = Field("file", init=False)
401
+ """File stream event handler."""
402
+
403
+ path: str = Field(
404
+ examples=["~/agent_output.txt", "/tmp/agent.log", "./output.md"],
405
+ )
406
+ """Path to the output file. Supports ~ expansion."""
407
+
408
+ mode: Literal["w", "a"] = Field(
409
+ default="a",
410
+ examples=["w", "a"],
411
+ )
412
+ """File open mode.
413
+
414
+ - w: Overwrite file on each run
415
+ - a: Append to existing file
416
+ """
417
+
418
+ include_tools: bool = Field(default=False)
419
+ """Whether to include tool call and result information."""
420
+
421
+ include_thinking: bool = Field(default=False)
422
+ """Whether to include thinking/reasoning content."""
423
+
424
+ def get_handler(self) -> IndividualEventHandler:
425
+ """Create and return the file stream handler."""
426
+ from agentpool.agents.events.builtin_handlers import create_file_stream_handler
427
+
428
+ return create_file_stream_handler(
429
+ path=self.path,
430
+ mode=self.mode,
431
+ include_tools=self.include_tools,
432
+ include_thinking=self.include_thinking,
433
+ )
434
+
435
+
395
436
  class TTSEventHandlerConfig(BaseEventHandlerConfig):
396
437
  """Configuration for Text-to-Speech event handler with OpenAI streaming."""
397
438
 
@@ -578,6 +619,7 @@ class EdgeTTSEventHandlerConfig(BaseEventHandlerConfig):
578
619
  EventHandlerConfig = Annotated[
579
620
  StdoutEventHandlerConfig
580
621
  | CallbackEventHandlerConfig
622
+ | FileStreamEventHandlerConfig
581
623
  | TTSEventHandlerConfig
582
624
  | EdgeTTSEventHandlerConfig,
583
625
  Field(discriminator="type"),
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING, Annotated, Any, Literal
6
6
 
7
- from evented.configs import (
7
+ from evented_config import (
8
8
  EmailConfig,
9
9
  EventSourceConfig,
10
10
  FileWatchConfig,
@@ -118,9 +118,6 @@ class NodeConnectionConfig(ConnectionConfig):
118
118
 
119
119
  DEFAULT_MESSAGE_TEMPLATE = """
120
120
  [{{ message.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}] {{ message.name }}: {{ message.content }}
121
- {%- if message.forwarded_from %}
122
- (via: {{ message.forwarded_from|join(' -> ') }})
123
- {%- endif %}
124
121
  """
125
122
 
126
123
 
@@ -135,7 +132,7 @@ class FileConnectionConfig(ConnectionConfig):
135
132
  - role: Message role (user/assistant/system)
136
133
  - model: Model used (if any)
137
134
  - cost_info: Token usage and cost info
138
- - forwarded_from: Chain of message forwarding
135
+ - parent_id: ID of parent message for tracking chains
139
136
  """
140
137
 
141
138
  model_config = ConfigDict(json_schema_extra={"title": "File Connection Configuration"})
agentpool_config/jinja.py CHANGED
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any, Literal
7
7
  from pydantic import BaseModel, ConfigDict, Field
8
8
  from schemez import Schema
9
9
 
10
- from agentpool_config.tools import ToolConfig
10
+ from agentpool_config import ToolConfig
11
11
 
12
12
 
13
13
  if TYPE_CHECKING:
@@ -141,9 +141,9 @@ class Jinja2EnvironmentConfig(Schema):
141
141
  try:
142
142
  # Convert filters - use tool name as filter name
143
143
  tools = [cfg.get_tool() for cfg in self.filters]
144
- kwargs["filters"] = {tool.name: tool.callable for tool in tools}
144
+ kwargs["filters"] = {tool.name: tool.get_callable() for tool in tools}
145
145
  tools = [cfg.get_tool() for cfg in self.tests]
146
- kwargs["tests"] = {tool.name: tool.callable for tool in tools}
146
+ kwargs["tests"] = {tool.name: tool.get_callable() for tool in tools}
147
147
 
148
148
  except Exception as exc:
149
149
  msg = f"Failed to import Jinja2 filters/tests: {exc}"
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import os
6
6
  from typing import TYPE_CHECKING, Annotated, Literal, Self
7
7
 
8
- from pydantic import ConfigDict, Field, HttpUrl
8
+ from pydantic import ConfigDict, Field, HttpUrl, model_validator
9
9
  from schemez import Schema
10
10
 
11
11
 
@@ -73,6 +73,62 @@ class BaseMCPServerConfig(Schema):
73
73
  )
74
74
  """Timeout for the server process in seconds."""
75
75
 
76
+ enabled_tools: list[str] | None = Field(
77
+ default=None,
78
+ examples=[["read_file", "list_directory"], ["search", "fetch"]],
79
+ title="Enabled tools",
80
+ )
81
+ """If set, only these tools will be available (whitelist).
82
+ Mutually exclusive with disabled_tools."""
83
+
84
+ disabled_tools: list[str] | None = Field(
85
+ default=None,
86
+ examples=[["delete_file", "write_file"], ["dangerous_tool"]],
87
+ title="Disabled tools",
88
+ )
89
+ """Tools to exclude from this server (blacklist). Mutually exclusive with enabled_tools."""
90
+
91
+ @model_validator(mode="after")
92
+ def _validate_tool_filters(self) -> Self:
93
+ """Validate that enabled_tools and disabled_tools are mutually exclusive."""
94
+ if self.enabled_tools is not None and self.disabled_tools is not None:
95
+ msg = "Cannot specify both 'enabled_tools' and 'disabled_tools'"
96
+ raise ValueError(msg)
97
+ return self
98
+
99
+ def is_tool_allowed(self, tool_name: str) -> bool:
100
+ """Check if a tool is allowed based on enabled/disabled lists.
101
+
102
+ Args:
103
+ tool_name: Name of the tool to check
104
+
105
+ Returns:
106
+ True if the tool is allowed, False otherwise
107
+ """
108
+ if self.enabled_tools is not None:
109
+ return tool_name in self.enabled_tools
110
+ if self.disabled_tools is not None:
111
+ return tool_name not in self.disabled_tools
112
+ return True
113
+
114
+ def needs_tool_filtering(self) -> bool:
115
+ """Check if this config has tool filtering configured."""
116
+ return self.enabled_tools is not None or self.disabled_tools is not None
117
+
118
+ def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
119
+ """Wrap this MCP server with mcp-filter for tool filtering.
120
+
121
+ Creates a new StdioMCPServerConfig that runs mcp-filter as a proxy,
122
+ applying the configured enabled_tools/disabled_tools filtering.
123
+
124
+ Returns:
125
+ A new StdioMCPServerConfig that wraps the original server with mcp-filter
126
+
127
+ Raises:
128
+ NotImplementedError: Subclasses must implement this method
129
+ """
130
+ raise NotImplementedError
131
+
76
132
  def get_env_vars(self) -> dict[str, str]:
77
133
  """Get environment variables for the server process."""
78
134
  env = os.environ.copy()
@@ -82,11 +138,7 @@ class BaseMCPServerConfig(Schema):
82
138
  return env
83
139
 
84
140
  def to_pydantic_ai(self) -> MCPServer:
85
- """Convert to pydantic-ai MCP server instance.
86
-
87
- Returns:
88
- A pydantic-ai MCP server instance
89
- """
141
+ """Convert to pydantic-ai MCP server instance."""
90
142
  raise NotImplementedError
91
143
 
92
144
  @property
@@ -142,6 +194,34 @@ class StdioMCPServerConfig(BaseMCPServerConfig):
142
194
  """Generate a unique client ID for this stdio server configuration."""
143
195
  return f"{self.command}_{' '.join(self.args)}"
144
196
 
197
+ def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
198
+ """Wrap this stdio MCP server with mcp-filter for tool filtering.
199
+
200
+ Returns:
201
+ A new StdioMCPServerConfig that wraps this server with mcp-filter
202
+ """
203
+ filter_args = ["mcp-filter", "run", "-t", "stdio", "--stdio-command", self.command]
204
+
205
+ # Add original args as a single --stdio-arg
206
+ if self.args:
207
+ filter_args.extend(["--stdio-arg", " ".join(self.args)])
208
+
209
+ # Add allowlist (exact tool names)
210
+ if self.enabled_tools:
211
+ filter_args.extend(["-a", ",".join(self.enabled_tools)])
212
+
213
+ # Add denylist (regex patterns)
214
+ if self.disabled_tools:
215
+ filter_args.extend(["-d", ",".join(self.disabled_tools)])
216
+
217
+ return StdioMCPServerConfig(
218
+ name=self.name,
219
+ command="uvx",
220
+ args=filter_args,
221
+ env=self.env,
222
+ timeout=self.timeout,
223
+ )
224
+
145
225
  def to_pydantic_ai(self) -> MCPServerStdio:
146
226
  """Convert to pydantic-ai MCPServerStdio instance."""
147
227
  from pydantic_ai.mcp import MCPServerStdio
@@ -186,6 +266,29 @@ class SSEMCPServerConfig(BaseMCPServerConfig):
186
266
  """Generate a unique client ID for this SSE server configuration."""
187
267
  return f"sse_{self.url}"
188
268
 
269
+ def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
270
+ """Wrap this SSE MCP server with mcp-filter for tool filtering.
271
+
272
+ Returns:
273
+ A new StdioMCPServerConfig that wraps this server with mcp-filter
274
+ """
275
+ filter_args = ["mcp-filter", "run", "-t", "http", "--http-url", str(self.url)]
276
+
277
+ # Add allowlist (exact tool names)
278
+ if self.enabled_tools:
279
+ filter_args.extend(["-a", ",".join(self.enabled_tools)])
280
+
281
+ # Add denylist (regex patterns)
282
+ if self.disabled_tools:
283
+ filter_args.extend(["-d", ",".join(self.disabled_tools)])
284
+
285
+ return StdioMCPServerConfig(
286
+ name=self.name,
287
+ command="uvx",
288
+ args=filter_args,
289
+ timeout=self.timeout,
290
+ )
291
+
189
292
  def to_pydantic_ai(self) -> MCPServerSSE:
190
293
  """Convert to pydantic-ai MCPServerSSE instance."""
191
294
  from pydantic_ai.mcp import MCPServerSSE
@@ -225,6 +328,29 @@ class StreamableHTTPMCPServerConfig(BaseMCPServerConfig):
225
328
  """Generate a unique client ID for this streamable HTTP server configuration."""
226
329
  return f"streamable_http_{self.url}"
227
330
 
331
+ def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
332
+ """Wrap this HTTP MCP server with mcp-filter for tool filtering.
333
+
334
+ Returns:
335
+ A new StdioMCPServerConfig that wraps this server with mcp-filter
336
+ """
337
+ filter_args = ["mcp-filter", "run", "-t", "http", "--http-url", str(self.url)]
338
+
339
+ # Add allowlist (exact tool names)
340
+ if self.enabled_tools:
341
+ filter_args.extend(["-a", ",".join(self.enabled_tools)])
342
+
343
+ # Add denylist (regex patterns)
344
+ if self.disabled_tools:
345
+ filter_args.extend(["-d", ",".join(self.disabled_tools)])
346
+
347
+ return StdioMCPServerConfig(
348
+ name=self.name,
349
+ command="uvx",
350
+ args=filter_args,
351
+ timeout=self.timeout,
352
+ )
353
+
228
354
  def to_pydantic_ai(self) -> MCPServerStreamableHTTP:
229
355
  """Convert to pydantic-ai MCPServerStreamableHTTP instance."""
230
356
  from pydantic_ai.mcp import MCPServerStreamableHTTP
agentpool_config/nodes.py CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING, Any, Literal
6
6
 
7
- from evented.configs import EventConfig
7
+ from evented_config import EventConfig
8
8
  from pydantic import ConfigDict, Field, ImportString
9
9
  from schemez import Schema
10
10
 
@@ -149,6 +149,49 @@ class ArizePhoenixObservabilityConfig(BaseObservabilityConfig):
149
149
  object.__setattr__(self, "_headers", headers)
150
150
 
151
151
 
152
+ class AxiomObservabilityConfig(BaseObservabilityConfig):
153
+ """Configuration for Axiom endpoint."""
154
+
155
+ model_config = ConfigDict(json_schema_extra={"x-doc-title": "Axiom"})
156
+
157
+ type: Literal["axiom"] = "axiom"
158
+ """Axiom observability configuration."""
159
+
160
+ api_token: SecretStr | None = Field(default=None, title="Axiom API token")
161
+ """Axiom API token with ingest permissions."""
162
+
163
+ dataset: str = Field(
164
+ examples=["traces", "otel-traces", "my-service-traces"],
165
+ title="Dataset name",
166
+ )
167
+ """Axiom dataset name where traces are sent."""
168
+
169
+ region: Literal["us", "eu"] | None = Field(
170
+ default=None,
171
+ examples=["us", "eu"],
172
+ title="Region",
173
+ )
174
+ """Axiom region. If not set, uses default cloud endpoint."""
175
+
176
+ _endpoint: str = PrivateAttr()
177
+ _headers: dict[str, str] = PrivateAttr(default_factory=dict)
178
+
179
+ def model_post_init(self, __context: Any, /) -> None:
180
+ """Compute private attributes from user config."""
181
+ # Axiom uses /v1/traces endpoint for OTLP
182
+ if self.region == "eu":
183
+ endpoint = "https://api.eu.axiom.co/v1/traces"
184
+ else:
185
+ endpoint = "https://api.axiom.co/v1/traces"
186
+ object.__setattr__(self, "_endpoint", endpoint)
187
+
188
+ headers: dict[str, str] = {}
189
+ if self.api_token:
190
+ headers["Authorization"] = f"Bearer {self.api_token.get_secret_value()}"
191
+ headers["X-Axiom-Dataset"] = self.dataset
192
+ object.__setattr__(self, "_headers", headers)
193
+
194
+
152
195
  class CustomObservabilityConfig(BaseObservabilityConfig):
153
196
  """Configuration for custom OTEL endpoint."""
154
197
 
@@ -173,6 +216,7 @@ ObservabilityProviderConfig = Annotated[
173
216
  | LangsmithObservabilityConfig
174
217
  | AgentOpsObservabilityConfig
175
218
  | ArizePhoenixObservabilityConfig
219
+ | AxiomObservabilityConfig
176
220
  | CustomObservabilityConfig,
177
221
  Field(discriminator="type"),
178
222
  ]
@@ -103,9 +103,6 @@ class SessionQuery(Schema):
103
103
  limit: int | None = Field(default=None, examples=[10, 50, 100], title="Message limit")
104
104
  """Maximum number of messages to return."""
105
105
 
106
- include_forwarded: bool = Field(default=True, title="Include forwarded messages")
107
- """Whether to include messages forwarded through agents."""
108
-
109
106
  model_config = ConfigDict(frozen=True)
110
107
 
111
108
  def get_time_cutoff(self) -> datetime | None: