agentpool 2.1.9__py3-none-any.whl → 2.2.3__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 (174) hide show
  1. acp/__init__.py +13 -0
  2. acp/bridge/README.md +15 -2
  3. acp/bridge/__init__.py +3 -2
  4. acp/bridge/__main__.py +60 -19
  5. acp/bridge/ws_server.py +173 -0
  6. acp/bridge/ws_server_cli.py +89 -0
  7. acp/notifications.py +2 -1
  8. acp/stdio.py +39 -9
  9. acp/transports.py +362 -2
  10. acp/utils.py +15 -2
  11. agentpool/__init__.py +4 -1
  12. agentpool/agents/__init__.py +2 -0
  13. agentpool/agents/acp_agent/acp_agent.py +203 -88
  14. agentpool/agents/acp_agent/acp_converters.py +46 -21
  15. agentpool/agents/acp_agent/client_handler.py +157 -3
  16. agentpool/agents/acp_agent/session_state.py +4 -1
  17. agentpool/agents/agent.py +314 -107
  18. agentpool/agents/agui_agent/__init__.py +0 -2
  19. agentpool/agents/agui_agent/agui_agent.py +90 -21
  20. agentpool/agents/agui_agent/agui_converters.py +0 -131
  21. agentpool/agents/base_agent.py +163 -1
  22. agentpool/agents/claude_code_agent/claude_code_agent.py +626 -179
  23. agentpool/agents/claude_code_agent/converters.py +71 -3
  24. agentpool/agents/claude_code_agent/history.py +474 -0
  25. agentpool/agents/context.py +40 -0
  26. agentpool/agents/events/__init__.py +2 -0
  27. agentpool/agents/events/builtin_handlers.py +2 -1
  28. agentpool/agents/events/event_emitter.py +29 -2
  29. agentpool/agents/events/events.py +20 -0
  30. agentpool/agents/modes.py +54 -0
  31. agentpool/agents/tool_call_accumulator.py +213 -0
  32. agentpool/common_types.py +21 -0
  33. agentpool/config_resources/__init__.py +38 -1
  34. agentpool/config_resources/claude_code_agent.yml +3 -0
  35. agentpool/delegation/pool.py +37 -29
  36. agentpool/delegation/team.py +1 -0
  37. agentpool/delegation/teamrun.py +1 -0
  38. agentpool/diagnostics/__init__.py +53 -0
  39. agentpool/diagnostics/lsp_manager.py +1593 -0
  40. agentpool/diagnostics/lsp_proxy.py +41 -0
  41. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  42. agentpool/diagnostics/models.py +398 -0
  43. agentpool/mcp_server/__init__.py +0 -2
  44. agentpool/mcp_server/client.py +12 -3
  45. agentpool/mcp_server/manager.py +25 -31
  46. agentpool/mcp_server/registries/official_registry_client.py +25 -0
  47. agentpool/mcp_server/tool_bridge.py +78 -66
  48. agentpool/messaging/__init__.py +0 -2
  49. agentpool/messaging/compaction.py +72 -197
  50. agentpool/messaging/message_history.py +12 -0
  51. agentpool/messaging/messages.py +52 -9
  52. agentpool/messaging/processing.py +3 -1
  53. agentpool/models/acp_agents/base.py +0 -22
  54. agentpool/models/acp_agents/mcp_capable.py +8 -148
  55. agentpool/models/acp_agents/non_mcp.py +129 -72
  56. agentpool/models/agents.py +35 -13
  57. agentpool/models/claude_code_agents.py +33 -2
  58. agentpool/models/manifest.py +43 -0
  59. agentpool/repomap.py +1 -1
  60. agentpool/resource_providers/__init__.py +9 -1
  61. agentpool/resource_providers/aggregating.py +52 -3
  62. agentpool/resource_providers/base.py +57 -1
  63. agentpool/resource_providers/mcp_provider.py +23 -0
  64. agentpool/resource_providers/plan_provider.py +130 -41
  65. agentpool/resource_providers/pool.py +2 -0
  66. agentpool/resource_providers/static.py +2 -0
  67. agentpool/sessions/__init__.py +2 -1
  68. agentpool/sessions/manager.py +31 -2
  69. agentpool/sessions/models.py +50 -0
  70. agentpool/skills/registry.py +13 -8
  71. agentpool/storage/manager.py +217 -1
  72. agentpool/testing.py +537 -19
  73. agentpool/utils/file_watcher.py +269 -0
  74. agentpool/utils/identifiers.py +121 -0
  75. agentpool/utils/pydantic_ai_helpers.py +46 -0
  76. agentpool/utils/streams.py +690 -1
  77. agentpool/utils/subprocess_utils.py +155 -0
  78. agentpool/utils/token_breakdown.py +461 -0
  79. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/METADATA +27 -7
  80. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/RECORD +170 -112
  81. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/WHEEL +1 -1
  82. agentpool_cli/__main__.py +4 -0
  83. agentpool_cli/serve_acp.py +41 -20
  84. agentpool_cli/serve_agui.py +87 -0
  85. agentpool_cli/serve_opencode.py +119 -0
  86. agentpool_commands/__init__.py +30 -0
  87. agentpool_commands/agents.py +74 -1
  88. agentpool_commands/history.py +62 -0
  89. agentpool_commands/mcp.py +176 -0
  90. agentpool_commands/models.py +56 -3
  91. agentpool_commands/tools.py +57 -0
  92. agentpool_commands/utils.py +51 -0
  93. agentpool_config/builtin_tools.py +77 -22
  94. agentpool_config/commands.py +24 -1
  95. agentpool_config/compaction.py +258 -0
  96. agentpool_config/mcp_server.py +131 -1
  97. agentpool_config/storage.py +46 -1
  98. agentpool_config/tools.py +7 -1
  99. agentpool_config/toolsets.py +92 -148
  100. agentpool_server/acp_server/acp_agent.py +134 -150
  101. agentpool_server/acp_server/commands/acp_commands.py +216 -51
  102. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +10 -10
  103. agentpool_server/acp_server/server.py +23 -79
  104. agentpool_server/acp_server/session.py +181 -19
  105. agentpool_server/opencode_server/.rules +95 -0
  106. agentpool_server/opencode_server/ENDPOINTS.md +362 -0
  107. agentpool_server/opencode_server/__init__.py +27 -0
  108. agentpool_server/opencode_server/command_validation.py +172 -0
  109. agentpool_server/opencode_server/converters.py +869 -0
  110. agentpool_server/opencode_server/dependencies.py +24 -0
  111. agentpool_server/opencode_server/input_provider.py +269 -0
  112. agentpool_server/opencode_server/models/__init__.py +228 -0
  113. agentpool_server/opencode_server/models/agent.py +53 -0
  114. agentpool_server/opencode_server/models/app.py +60 -0
  115. agentpool_server/opencode_server/models/base.py +26 -0
  116. agentpool_server/opencode_server/models/common.py +23 -0
  117. agentpool_server/opencode_server/models/config.py +37 -0
  118. agentpool_server/opencode_server/models/events.py +647 -0
  119. agentpool_server/opencode_server/models/file.py +88 -0
  120. agentpool_server/opencode_server/models/mcp.py +25 -0
  121. agentpool_server/opencode_server/models/message.py +162 -0
  122. agentpool_server/opencode_server/models/parts.py +190 -0
  123. agentpool_server/opencode_server/models/provider.py +81 -0
  124. agentpool_server/opencode_server/models/pty.py +43 -0
  125. agentpool_server/opencode_server/models/session.py +99 -0
  126. agentpool_server/opencode_server/routes/__init__.py +25 -0
  127. agentpool_server/opencode_server/routes/agent_routes.py +442 -0
  128. agentpool_server/opencode_server/routes/app_routes.py +139 -0
  129. agentpool_server/opencode_server/routes/config_routes.py +241 -0
  130. agentpool_server/opencode_server/routes/file_routes.py +392 -0
  131. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  132. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  133. agentpool_server/opencode_server/routes/message_routes.py +705 -0
  134. agentpool_server/opencode_server/routes/pty_routes.py +299 -0
  135. agentpool_server/opencode_server/routes/session_routes.py +1205 -0
  136. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  137. agentpool_server/opencode_server/server.py +430 -0
  138. agentpool_server/opencode_server/state.py +121 -0
  139. agentpool_server/opencode_server/time_utils.py +8 -0
  140. agentpool_storage/__init__.py +16 -0
  141. agentpool_storage/base.py +103 -0
  142. agentpool_storage/claude_provider.py +907 -0
  143. agentpool_storage/file_provider.py +129 -0
  144. agentpool_storage/memory_provider.py +61 -0
  145. agentpool_storage/models.py +3 -0
  146. agentpool_storage/opencode_provider.py +730 -0
  147. agentpool_storage/project_store.py +325 -0
  148. agentpool_storage/session_store.py +6 -0
  149. agentpool_storage/sql_provider/__init__.py +4 -2
  150. agentpool_storage/sql_provider/models.py +48 -0
  151. agentpool_storage/sql_provider/sql_provider.py +134 -1
  152. agentpool_storage/sql_provider/utils.py +10 -1
  153. agentpool_storage/text_log_provider.py +1 -0
  154. agentpool_toolsets/builtin/__init__.py +0 -8
  155. agentpool_toolsets/builtin/code.py +95 -56
  156. agentpool_toolsets/builtin/debug.py +16 -21
  157. agentpool_toolsets/builtin/execution_environment.py +99 -103
  158. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  159. agentpool_toolsets/builtin/skills.py +86 -4
  160. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  161. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  162. agentpool_toolsets/fsspec_toolset/grep.py +74 -2
  163. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  164. agentpool_toolsets/fsspec_toolset/toolset.py +159 -38
  165. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  166. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  167. agentpool_toolsets/mcp_discovery/toolset.py +454 -0
  168. agentpool_toolsets/mcp_run_toolset.py +84 -6
  169. agentpool_toolsets/builtin/agent_management.py +0 -239
  170. agentpool_toolsets/builtin/history.py +0 -36
  171. agentpool_toolsets/builtin/integration.py +0 -85
  172. agentpool_toolsets/builtin/tool_management.py +0 -90
  173. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/entry_points.txt +0 -0
  174. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,176 @@
1
+ """MCP server management commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any, Literal
6
+
7
+ from pydantic import HttpUrl
8
+ from slashed import CommandContext, CommandError # noqa: TC002
9
+
10
+ from agentpool_commands.base import NodeCommand
11
+
12
+
13
+ if TYPE_CHECKING:
14
+ from agentpool.agents.context import AgentContext
15
+ from agentpool.messaging import MessageNode
16
+ from agentpool_config.mcp_server import MCPServerConfig
17
+
18
+
19
+ class AddMCPServerCommand(NodeCommand):
20
+ """Add a local MCP server via stdio transport.
21
+
22
+ Registers a new MCP server that runs as a subprocess.
23
+
24
+ Options:
25
+ --args "arg1|arg2" Pipe-separated command arguments
26
+ --env "KEY=val|KEY2=val" Pipe-separated environment variables
27
+
28
+ Examples:
29
+ /add-mcp-server myserver npx --args "mcp-server-filesystem|/tmp"
30
+ /add-mcp-server graphql npx --args "mcp-graphql" --env "ENDPOINT=https://api.example.com/graphql"
31
+ """
32
+
33
+ name = "add-mcp-server"
34
+ category = "integrations"
35
+
36
+ @classmethod
37
+ def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
38
+ from agentpool import Agent
39
+
40
+ return isinstance(node, Agent)
41
+
42
+ async def execute_command(
43
+ self,
44
+ ctx: CommandContext[AgentContext],
45
+ name: str,
46
+ command: str,
47
+ *,
48
+ args: str | None = None,
49
+ env: str | None = None,
50
+ ) -> None:
51
+ """Add a local MCP server.
52
+
53
+ Args:
54
+ ctx: Command context
55
+ name: Unique name for the MCP server
56
+ command: Command to execute for the server
57
+ args: Pipe-separated command arguments
58
+ env: Pipe-separated environment variables (KEY=value format)
59
+ """
60
+ from agentpool_config.mcp_server import StdioMCPServerConfig
61
+
62
+ try:
63
+ # Parse arguments
64
+ arg_list = [a.strip() for a in args.split("|")] if args else []
65
+ # Parse environment variables
66
+ env_dict: dict[str, str] = {}
67
+ if env:
68
+ for pair in env.split("|"):
69
+ if "=" in pair:
70
+ key, value = pair.split("=", 1)
71
+ env_dict[key.strip()] = value.strip()
72
+
73
+ config = StdioMCPServerConfig(name=name, command=command, args=arg_list, env=env_dict)
74
+ await ctx.context.agent.mcp.setup_server(config, add_to_config=True)
75
+ await ctx.print(f"**Added MCP server** `{name}` with command: `{command}`")
76
+
77
+ except Exception as e:
78
+ msg = f"Failed to add MCP server: {e}"
79
+ raise CommandError(msg) from e
80
+
81
+
82
+ class AddRemoteMCPServerCommand(NodeCommand):
83
+ """Add a remote MCP server via HTTP transport.
84
+
85
+ Connects to an MCP server running on a remote URL.
86
+
87
+ Options:
88
+ --transport sse|streamable-http Transport type (default: streamable-http)
89
+
90
+ Examples:
91
+ /add-remote-mcp-server myapi https://api.example.com/mcp
92
+ /add-remote-mcp-server legacy https://old.api.com/mcp --transport sse
93
+ """
94
+
95
+ name = "add-remote-mcp-server"
96
+ category = "integrations"
97
+
98
+ @classmethod
99
+ def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
100
+ from agentpool import Agent
101
+
102
+ return isinstance(node, Agent)
103
+
104
+ async def execute_command(
105
+ self,
106
+ ctx: CommandContext[AgentContext],
107
+ name: str,
108
+ url: str,
109
+ *,
110
+ transport: Literal["sse", "streamable-http"] = "streamable-http",
111
+ ) -> None:
112
+ """Add a remote MCP server.
113
+
114
+ Args:
115
+ ctx: Command context
116
+ name: Unique name for the MCP server
117
+ url: Server URL endpoint
118
+ transport: HTTP transport type (sse or streamable-http)
119
+ """
120
+ from agentpool_config.mcp_server import SSEMCPServerConfig, StreamableHTTPMCPServerConfig
121
+
122
+ try:
123
+ config: MCPServerConfig
124
+ if transport == "sse":
125
+ config = SSEMCPServerConfig(name=name, url=HttpUrl(url))
126
+ else:
127
+ config = StreamableHTTPMCPServerConfig(name=name, url=HttpUrl(url))
128
+
129
+ await ctx.context.agent.mcp.setup_server(config, add_to_config=True)
130
+ await ctx.print(f"**Added remote MCP server** `{name}` at `{url}` using {transport}")
131
+
132
+ except Exception as e:
133
+ msg = f"Failed to add remote MCP server: {e}"
134
+ raise CommandError(msg) from e
135
+
136
+
137
+ class ListMCPServersCommand(NodeCommand):
138
+ """List all configured MCP servers.
139
+
140
+ Shows server name, type, and connection status.
141
+ """
142
+
143
+ name = "list-mcp-servers"
144
+ category = "integrations"
145
+
146
+ @classmethod
147
+ def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
148
+ from agentpool import Agent
149
+
150
+ return isinstance(node, Agent)
151
+
152
+ async def execute_command(self, ctx: CommandContext[AgentContext]) -> None:
153
+ """List all MCP servers.
154
+
155
+ Args:
156
+ ctx: Command context
157
+ """
158
+ from agentpool_commands.markdown_utils import format_table
159
+
160
+ servers = ctx.context.agent.mcp.providers
161
+ if not servers:
162
+ await ctx.print("No MCP servers configured.")
163
+ return
164
+
165
+ rows = []
166
+ for server in servers:
167
+ server_type = type(server.server).__name__.replace("MCPServerConfig", "")
168
+ rows.append({
169
+ "Name": server.name,
170
+ "Type": server_type,
171
+ "Status": "connected" if server.client.connected else "disconnected",
172
+ })
173
+
174
+ headers = ["Name", "Type", "Status"]
175
+ table = format_table(headers, rows)
176
+ await ctx.print(f"## MCP Servers\n\n{table}")
@@ -16,6 +16,59 @@ if TYPE_CHECKING:
16
16
  from agentpool.messaging import MessageNode
17
17
 
18
18
 
19
+ class ListModelsCommand(NodeCommand):
20
+ """List available models for the current agent.
21
+
22
+ Displays all models that can be used with the current agent,
23
+ formatted as a markdown table with model ID, name, and description.
24
+
25
+ Examples:
26
+ /list-models
27
+ """
28
+
29
+ name = "list-models"
30
+ category = "model"
31
+
32
+ async def execute_command(
33
+ self,
34
+ ctx: CommandContext[AgentContext],
35
+ ) -> None:
36
+ """List available models.
37
+
38
+ Args:
39
+ ctx: Command context
40
+ """
41
+ agent = ctx.context.agent
42
+ models = await agent.get_available_models()
43
+
44
+ if not models:
45
+ await ctx.print("No models available for this agent.")
46
+ return
47
+
48
+ # Build markdown table
49
+ lines = [
50
+ "| Model ID | Name | Description |",
51
+ "|----------|------|-------------|",
52
+ ]
53
+
54
+ for model in models:
55
+ model_id = model.id_override if model.id_override else model.id
56
+ name = model.name or ""
57
+ description = model.description or ""
58
+ # Escape pipe characters in fields
59
+ name = name.replace("|", "\\|")
60
+ description = description.replace("|", "\\|")
61
+ lines.append(f"| `{model_id}` | {name} | {description} |")
62
+
63
+ await ctx.print("\n".join(lines))
64
+
65
+ @classmethod
66
+ def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
67
+ from agentpool import ACPAgent, Agent, ClaudeCodeAgent
68
+
69
+ return isinstance(node, Agent | ACPAgent | ClaudeCodeAgent)
70
+
71
+
19
72
  class SetModelCommand(NodeCommand):
20
73
  """Change the language model for the current conversation.
21
74
 
@@ -46,7 +99,7 @@ class SetModelCommand(NodeCommand):
46
99
  """
47
100
  try:
48
101
  # Create new session with model override
49
- ctx.context.native_agent.set_model(model)
102
+ await ctx.context.agent.set_model(model)
50
103
  await ctx.print(f"✅ **Model changed to:** `{model}`")
51
104
  except Exception as e: # noqa: BLE001
52
105
  await ctx.print(f"❌ **Failed to change model:** {e}")
@@ -57,6 +110,6 @@ class SetModelCommand(NodeCommand):
57
110
 
58
111
  @classmethod
59
112
  def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
60
- from agentpool import Agent
113
+ from agentpool import ACPAgent, Agent, ClaudeCodeAgent
61
114
 
62
- return isinstance(node, Agent)
115
+ return isinstance(node, Agent | ACPAgent | ClaudeCodeAgent)
@@ -264,6 +264,63 @@ class RegisterToolCommand(NodeCommand):
264
264
  raise CommandError(msg) from e
265
265
 
266
266
 
267
+ class RegisterCodeToolCommand(NodeCommand):
268
+ """Register a new tool from Python code.
269
+
270
+ The code should define a function that will become the tool.
271
+ The function name becomes the tool name unless overridden.
272
+
273
+ Options:
274
+ --name tool_name Custom name for the tool
275
+ --description "text" Custom description
276
+
277
+ Examples:
278
+ /register-code-tool "def greet(name: str) -> str: return f'Hello {name}'"
279
+ /register-code-tool "def add(a: int, b: int) -> int: return a + b" --name sum
280
+ """
281
+
282
+ name = "register-code-tool"
283
+ category = "tools"
284
+
285
+ @classmethod
286
+ def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
287
+ from agentpool import Agent
288
+
289
+ return isinstance(node, Agent)
290
+
291
+ async def execute_command(
292
+ self,
293
+ ctx: CommandContext[AgentContext],
294
+ code: str,
295
+ *,
296
+ name: str | None = None,
297
+ description: str | None = None,
298
+ ) -> None:
299
+ """Register a new tool from code string.
300
+
301
+ Args:
302
+ ctx: Command context
303
+ code: Python code defining a function
304
+ name: Optional custom name for the tool
305
+ description: Optional custom description
306
+ """
307
+ from agentpool.tools.base import Tool
308
+
309
+ try:
310
+ tool_obj = Tool.from_code(code, name=name, description=description)
311
+ tool_obj.enabled = True
312
+ tool_obj.source = "dynamic"
313
+
314
+ registered = ctx.context.agent.tools.register_tool(tool_obj)
315
+ await ctx.print(
316
+ f"**Tool registered:** `{registered.name}`"
317
+ f" - {registered.description or '*No description*'}"
318
+ )
319
+ except Exception as e:
320
+ msg = f"Failed to register code tool: {e}"
321
+ raise CommandError(msg) from e
322
+
323
+
267
324
  async def get_tool_names(ctx: CompletionContext[AgentContext]) -> list[str]:
268
325
  manager = ctx.command_context.context.agent.tools
269
326
  return list({t.name for t in await manager.get_tools()})
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import importlib.util
6
+ from typing import Literal
6
7
  import webbrowser
7
8
 
8
9
  from anyenv.text_sharing import TextSharerStr, Visibility # noqa: TC002
@@ -12,6 +13,9 @@ from agentpool.messaging.context import NodeContext # noqa: TC001
12
13
  from agentpool_commands.base import NodeCommand
13
14
 
14
15
 
16
+ LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
17
+
18
+
15
19
  class CopyClipboardCommand(NodeCommand):
16
20
  """Copy messages from conversation history to system clipboard.
17
21
 
@@ -187,3 +191,50 @@ class ShareHistoryCommand(NodeCommand):
187
191
  def condition(cls) -> bool:
188
192
  """Check if anyenv text_sharing is available."""
189
193
  return importlib.util.find_spec("anyenv.text_sharing") is not None
194
+
195
+
196
+ class GetLogsCommand(NodeCommand):
197
+ """Get recent log entries from memory.
198
+
199
+ Shows captured log entries with filtering options:
200
+ - Filter by minimum log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
201
+ - Filter by logger name substring
202
+ - Limit number of entries returned
203
+
204
+ Logs are shown newest first.
205
+ """
206
+
207
+ name = "get-logs"
208
+ category = "debug"
209
+
210
+ async def execute_command(
211
+ self,
212
+ ctx: CommandContext[NodeContext],
213
+ *,
214
+ level: LogLevel = "INFO",
215
+ logger_filter: str | None = None,
216
+ limit: int | str = 50,
217
+ ) -> None:
218
+ """Get recent log entries.
219
+
220
+ Args:
221
+ ctx: Command context
222
+ level: Minimum log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
223
+ logger_filter: Only show logs from loggers containing this string
224
+ limit: Maximum number of log entries to return
225
+ """
226
+ from agentpool_toolsets.builtin.debug import get_memory_handler
227
+
228
+ # Convert limit to int (slashed passes args as strings)
229
+ limit_int = int(limit) if isinstance(limit, str) else limit
230
+
231
+ handler = get_memory_handler()
232
+ records = handler.get_records(level=level, logger_filter=logger_filter, limit=limit_int)
233
+
234
+ if not records:
235
+ await ctx.print("No log entries found matching criteria")
236
+ return
237
+
238
+ await ctx.print(f"## 📋 {len(records)} Log Entries (newest first)\n")
239
+ for record in records:
240
+ await ctx.print(record.message)
@@ -25,17 +25,33 @@ if TYPE_CHECKING:
25
25
  class BaseBuiltinToolConfig(BaseToolConfig):
26
26
  """Base configuration for PydanticAI builtin tools."""
27
27
 
28
+ type: Literal["builtin"] = Field("builtin", init=False)
29
+ """Top-level discriminator - always 'builtin' for builtin tools."""
30
+
31
+ builtin_type: str = Field(init=False)
32
+ """Sub-discriminator for specific builtin tool type."""
33
+
28
34
  def get_builtin_tool(self) -> AbstractBuiltinTool:
29
35
  """Convert config to PydanticAI builtin tool instance."""
30
36
  raise NotImplementedError
31
37
 
32
38
 
33
39
  class WebSearchToolConfig(BaseBuiltinToolConfig):
34
- """Configuration for PydanticAI web search builtin tool."""
40
+ """Configuration for PydanticAI web search builtin tool.
41
+
42
+ Example:
43
+ ```yaml
44
+ tools:
45
+ - type: builtin
46
+ builtin_type: web_search
47
+ search_context_size: high
48
+ blocked_domains: ["spam.com"]
49
+ ```
50
+ """
35
51
 
36
52
  model_config = ConfigDict(title="Web Search Tool")
37
53
 
38
- type: Literal["web_search"] = Field("web_search", init=False)
54
+ builtin_type: Literal["web_search"] = Field("web_search", init=False)
39
55
  """Web search builtin tool."""
40
56
 
41
57
  search_context_size: Literal["low", "medium", "high"] = Field(
@@ -77,11 +93,19 @@ class WebSearchToolConfig(BaseBuiltinToolConfig):
77
93
 
78
94
 
79
95
  class CodeExecutionToolConfig(BaseBuiltinToolConfig):
80
- """Configuration for PydanticAI code execution builtin tool."""
96
+ """Configuration for PydanticAI code execution builtin tool.
97
+
98
+ Example:
99
+ ```yaml
100
+ tools:
101
+ - type: builtin
102
+ builtin_type: code_execution
103
+ ```
104
+ """
81
105
 
82
106
  model_config = ConfigDict(title="Code Execution Tool")
83
107
 
84
- type: Literal["code_execution"] = Field("code_execution", init=False)
108
+ builtin_type: Literal["code_execution"] = Field("code_execution", init=False)
85
109
  """Code execution builtin tool."""
86
110
 
87
111
  def get_builtin_tool(self) -> CodeExecutionTool:
@@ -90,12 +114,20 @@ class CodeExecutionToolConfig(BaseBuiltinToolConfig):
90
114
 
91
115
 
92
116
  class WebFetchToolConfig(BaseBuiltinToolConfig):
93
- """Configuration for PydanticAI URL context builtin tool."""
117
+ """Configuration for PydanticAI web fetch builtin tool.
118
+
119
+ Example:
120
+ ```yaml
121
+ tools:
122
+ - type: builtin
123
+ builtin_type: web_fetch
124
+ ```
125
+ """
94
126
 
95
- model_config = ConfigDict(title="Url Context Tool")
127
+ model_config = ConfigDict(title="Web Fetch Tool")
96
128
 
97
- type: Literal["web_fetch"] = Field("web_fetch", init=False)
98
- """URL context builtin tool."""
129
+ builtin_type: Literal["web_fetch"] = Field("web_fetch", init=False)
130
+ """Web fetch builtin tool."""
99
131
 
100
132
  def get_builtin_tool(self) -> WebFetchTool:
101
133
  """Convert config to WebFetchTool instance."""
@@ -103,11 +135,21 @@ class WebFetchToolConfig(BaseBuiltinToolConfig):
103
135
 
104
136
 
105
137
  class ImageGenerationToolConfig(BaseBuiltinToolConfig):
106
- """Configuration for PydanticAI image generation builtin tool."""
138
+ """Configuration for PydanticAI image generation builtin tool.
139
+
140
+ Example:
141
+ ```yaml
142
+ tools:
143
+ - type: builtin
144
+ builtin_type: image_generation
145
+ quality: high
146
+ size: 1024x1024
147
+ ```
148
+ """
107
149
 
108
150
  model_config = ConfigDict(title="Image Generation Tool")
109
151
 
110
- type: Literal["image_generation"] = Field("image_generation", init=False)
152
+ builtin_type: Literal["image_generation"] = Field("image_generation", init=False)
111
153
  """Image generation builtin tool."""
112
154
 
113
155
  background: Literal["transparent", "opaque", "auto"] = Field(
@@ -184,11 +226,19 @@ class ImageGenerationToolConfig(BaseBuiltinToolConfig):
184
226
 
185
227
 
186
228
  class MemoryToolConfig(BaseBuiltinToolConfig):
187
- """Configuration for PydanticAI memory builtin tool."""
229
+ """Configuration for PydanticAI memory builtin tool.
230
+
231
+ Example:
232
+ ```yaml
233
+ tools:
234
+ - type: builtin
235
+ builtin_type: memory
236
+ ```
237
+ """
188
238
 
189
239
  model_config = ConfigDict(title="Memory Tool")
190
240
 
191
- type: Literal["memory"] = Field("memory", init=False)
241
+ builtin_type: Literal["memory"] = Field("memory", init=False)
192
242
  """Memory builtin tool."""
193
243
 
194
244
  def get_builtin_tool(self) -> MemoryTool:
@@ -197,11 +247,21 @@ class MemoryToolConfig(BaseBuiltinToolConfig):
197
247
 
198
248
 
199
249
  class MCPServerToolConfig(BaseBuiltinToolConfig):
200
- """Configuration for PydanticAI MCP server builtin tool."""
250
+ """Configuration for PydanticAI MCP server builtin tool.
251
+
252
+ Example:
253
+ ```yaml
254
+ tools:
255
+ - type: builtin
256
+ builtin_type: mcp_server
257
+ id: my_server
258
+ url: https://api.example.com/mcp
259
+ ```
260
+ """
201
261
 
202
262
  model_config = ConfigDict(title="MCP Server Tool")
203
263
 
204
- type: Literal["mcp_server"] = Field("mcp_server", init=False)
264
+ builtin_type: Literal["mcp_server"] = Field("mcp_server", init=False)
205
265
  """MCP server builtin tool."""
206
266
 
207
267
  server_id: str = Field(
@@ -224,12 +284,7 @@ class MCPServerToolConfig(BaseBuiltinToolConfig):
224
284
  )
225
285
  """Authorization header to use when making requests to the MCP server."""
226
286
 
227
- description: str | None = Field(
228
- default=None,
229
- examples=["External API tools", "Code execution server"],
230
- title="Server description",
231
- )
232
- """A description of the MCP server."""
287
+ # description is inherited from BaseToolConfig
233
288
 
234
289
  allowed_tools: list[str] | None = Field(
235
290
  default=None,
@@ -253,7 +308,7 @@ class MCPServerToolConfig(BaseBuiltinToolConfig):
253
308
  )
254
309
 
255
310
 
256
- # Union type for builtin tool configs
311
+ # Union type for builtin tool configs (sub-discriminated by builtin_type)
257
312
  BuiltinToolConfig = Annotated[
258
313
  WebSearchToolConfig
259
314
  | CodeExecutionToolConfig
@@ -261,5 +316,5 @@ BuiltinToolConfig = Annotated[
261
316
  | ImageGenerationToolConfig
262
317
  | MemoryToolConfig
263
318
  | MCPServerToolConfig,
264
- Field(discriminator="type"),
319
+ Field(discriminator="builtin_type"),
265
320
  ]
@@ -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."""