agentpool 2.2.3__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 (250) hide show
  1. acp/__init__.py +0 -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/client/connection.py +38 -29
  7. acp/client/implementations/default_client.py +3 -2
  8. acp/client/implementations/headless_client.py +2 -2
  9. acp/connection.py +2 -2
  10. acp/notifications.py +18 -49
  11. acp/schema/__init__.py +2 -0
  12. acp/schema/agent_responses.py +21 -0
  13. acp/schema/client_requests.py +3 -3
  14. acp/schema/session_state.py +63 -29
  15. acp/task/supervisor.py +2 -2
  16. acp/utils.py +2 -2
  17. agentpool/__init__.py +2 -0
  18. agentpool/agents/acp_agent/acp_agent.py +278 -263
  19. agentpool/agents/acp_agent/acp_converters.py +150 -17
  20. agentpool/agents/acp_agent/client_handler.py +35 -24
  21. agentpool/agents/acp_agent/session_state.py +14 -6
  22. agentpool/agents/agent.py +471 -643
  23. agentpool/agents/agui_agent/agui_agent.py +104 -107
  24. agentpool/agents/agui_agent/helpers.py +3 -4
  25. agentpool/agents/base_agent.py +485 -32
  26. agentpool/agents/claude_code_agent/FORKING.md +191 -0
  27. agentpool/agents/claude_code_agent/__init__.py +13 -1
  28. agentpool/agents/claude_code_agent/claude_code_agent.py +654 -334
  29. agentpool/agents/claude_code_agent/converters.py +4 -141
  30. agentpool/agents/claude_code_agent/models.py +77 -0
  31. agentpool/agents/claude_code_agent/static_info.py +100 -0
  32. agentpool/agents/claude_code_agent/usage.py +242 -0
  33. agentpool/agents/events/__init__.py +22 -0
  34. agentpool/agents/events/builtin_handlers.py +65 -0
  35. agentpool/agents/events/event_emitter.py +3 -0
  36. agentpool/agents/events/events.py +84 -3
  37. agentpool/agents/events/infer_info.py +145 -0
  38. agentpool/agents/events/processors.py +254 -0
  39. agentpool/agents/interactions.py +41 -6
  40. agentpool/agents/modes.py +13 -0
  41. agentpool/agents/slashed_agent.py +5 -4
  42. agentpool/agents/tool_wrapping.py +18 -6
  43. agentpool/common_types.py +35 -21
  44. agentpool/config_resources/acp_assistant.yml +2 -2
  45. agentpool/config_resources/agents.yml +3 -0
  46. agentpool/config_resources/agents_template.yml +1 -0
  47. agentpool/config_resources/claude_code_agent.yml +9 -8
  48. agentpool/config_resources/external_acp_agents.yml +2 -1
  49. agentpool/delegation/base_team.py +4 -30
  50. agentpool/delegation/pool.py +104 -265
  51. agentpool/delegation/team.py +57 -57
  52. agentpool/delegation/teamrun.py +50 -55
  53. agentpool/functional/run.py +10 -4
  54. agentpool/mcp_server/client.py +73 -38
  55. agentpool/mcp_server/conversions.py +54 -13
  56. agentpool/mcp_server/manager.py +9 -23
  57. agentpool/mcp_server/registries/official_registry_client.py +10 -1
  58. agentpool/mcp_server/tool_bridge.py +114 -79
  59. agentpool/messaging/connection_manager.py +11 -10
  60. agentpool/messaging/event_manager.py +5 -5
  61. agentpool/messaging/message_container.py +6 -30
  62. agentpool/messaging/message_history.py +87 -8
  63. agentpool/messaging/messagenode.py +52 -14
  64. agentpool/messaging/messages.py +2 -26
  65. agentpool/messaging/processing.py +10 -22
  66. agentpool/models/__init__.py +1 -1
  67. agentpool/models/acp_agents/base.py +6 -2
  68. agentpool/models/acp_agents/mcp_capable.py +124 -15
  69. agentpool/models/acp_agents/non_mcp.py +0 -23
  70. agentpool/models/agents.py +66 -66
  71. agentpool/models/agui_agents.py +1 -1
  72. agentpool/models/claude_code_agents.py +111 -17
  73. agentpool/models/file_parsing.py +0 -1
  74. agentpool/models/manifest.py +70 -50
  75. agentpool/prompts/conversion_manager.py +1 -1
  76. agentpool/prompts/prompts.py +5 -2
  77. agentpool/resource_providers/__init__.py +2 -0
  78. agentpool/resource_providers/aggregating.py +4 -2
  79. agentpool/resource_providers/base.py +13 -3
  80. agentpool/resource_providers/codemode/code_executor.py +72 -5
  81. agentpool/resource_providers/codemode/helpers.py +2 -2
  82. agentpool/resource_providers/codemode/provider.py +64 -12
  83. agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
  84. agentpool/resource_providers/codemode/remote_provider.py +9 -12
  85. agentpool/resource_providers/filtering.py +3 -1
  86. agentpool/resource_providers/mcp_provider.py +66 -12
  87. agentpool/resource_providers/plan_provider.py +111 -18
  88. agentpool/resource_providers/pool.py +5 -3
  89. agentpool/resource_providers/resource_info.py +111 -0
  90. agentpool/resource_providers/static.py +2 -2
  91. agentpool/sessions/__init__.py +2 -0
  92. agentpool/sessions/manager.py +2 -3
  93. agentpool/sessions/models.py +9 -6
  94. agentpool/sessions/protocol.py +28 -0
  95. agentpool/sessions/session.py +11 -55
  96. agentpool/storage/manager.py +361 -54
  97. agentpool/talk/registry.py +4 -4
  98. agentpool/talk/talk.py +9 -10
  99. agentpool/testing.py +1 -1
  100. agentpool/tool_impls/__init__.py +6 -0
  101. agentpool/tool_impls/agent_cli/__init__.py +42 -0
  102. agentpool/tool_impls/agent_cli/tool.py +95 -0
  103. agentpool/tool_impls/bash/__init__.py +64 -0
  104. agentpool/tool_impls/bash/helpers.py +35 -0
  105. agentpool/tool_impls/bash/tool.py +171 -0
  106. agentpool/tool_impls/delete_path/__init__.py +70 -0
  107. agentpool/tool_impls/delete_path/tool.py +142 -0
  108. agentpool/tool_impls/download_file/__init__.py +80 -0
  109. agentpool/tool_impls/download_file/tool.py +183 -0
  110. agentpool/tool_impls/execute_code/__init__.py +55 -0
  111. agentpool/tool_impls/execute_code/tool.py +163 -0
  112. agentpool/tool_impls/grep/__init__.py +80 -0
  113. agentpool/tool_impls/grep/tool.py +200 -0
  114. agentpool/tool_impls/list_directory/__init__.py +73 -0
  115. agentpool/tool_impls/list_directory/tool.py +197 -0
  116. agentpool/tool_impls/question/__init__.py +42 -0
  117. agentpool/tool_impls/question/tool.py +127 -0
  118. agentpool/tool_impls/read/__init__.py +104 -0
  119. agentpool/tool_impls/read/tool.py +305 -0
  120. agentpool/tools/__init__.py +2 -1
  121. agentpool/tools/base.py +114 -34
  122. agentpool/tools/manager.py +57 -1
  123. agentpool/ui/base.py +2 -2
  124. agentpool/ui/mock_provider.py +2 -2
  125. agentpool/ui/stdlib_provider.py +2 -2
  126. agentpool/utils/streams.py +21 -96
  127. agentpool/vfs_registry.py +7 -2
  128. {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/METADATA +16 -22
  129. {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/RECORD +242 -195
  130. {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
  131. agentpool_cli/__main__.py +20 -0
  132. agentpool_cli/create.py +1 -1
  133. agentpool_cli/serve_acp.py +59 -1
  134. agentpool_cli/serve_opencode.py +1 -1
  135. agentpool_cli/ui.py +557 -0
  136. agentpool_commands/__init__.py +12 -5
  137. agentpool_commands/agents.py +1 -1
  138. agentpool_commands/pool.py +260 -0
  139. agentpool_commands/session.py +1 -1
  140. agentpool_commands/text_sharing/__init__.py +119 -0
  141. agentpool_commands/text_sharing/base.py +123 -0
  142. agentpool_commands/text_sharing/github_gist.py +80 -0
  143. agentpool_commands/text_sharing/opencode.py +462 -0
  144. agentpool_commands/text_sharing/paste_rs.py +59 -0
  145. agentpool_commands/text_sharing/pastebin.py +116 -0
  146. agentpool_commands/text_sharing/shittycodingagent.py +112 -0
  147. agentpool_commands/utils.py +31 -32
  148. agentpool_config/__init__.py +30 -2
  149. agentpool_config/agentpool_tools.py +498 -0
  150. agentpool_config/converters.py +1 -1
  151. agentpool_config/event_handlers.py +42 -0
  152. agentpool_config/events.py +1 -1
  153. agentpool_config/forward_targets.py +1 -4
  154. agentpool_config/jinja.py +3 -3
  155. agentpool_config/mcp_server.py +1 -5
  156. agentpool_config/nodes.py +1 -1
  157. agentpool_config/observability.py +44 -0
  158. agentpool_config/session.py +0 -3
  159. agentpool_config/storage.py +38 -39
  160. agentpool_config/task.py +3 -3
  161. agentpool_config/tools.py +11 -28
  162. agentpool_config/toolsets.py +22 -90
  163. agentpool_server/a2a_server/agent_worker.py +307 -0
  164. agentpool_server/a2a_server/server.py +23 -18
  165. agentpool_server/acp_server/acp_agent.py +125 -56
  166. agentpool_server/acp_server/commands/acp_commands.py +46 -216
  167. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +8 -7
  168. agentpool_server/acp_server/event_converter.py +651 -0
  169. agentpool_server/acp_server/input_provider.py +53 -10
  170. agentpool_server/acp_server/server.py +1 -11
  171. agentpool_server/acp_server/session.py +90 -410
  172. agentpool_server/acp_server/session_manager.py +8 -34
  173. agentpool_server/agui_server/server.py +3 -1
  174. agentpool_server/mcp_server/server.py +5 -2
  175. agentpool_server/opencode_server/ENDPOINTS.md +53 -14
  176. agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
  177. agentpool_server/opencode_server/__init__.py +0 -8
  178. agentpool_server/opencode_server/converters.py +132 -26
  179. agentpool_server/opencode_server/input_provider.py +160 -8
  180. agentpool_server/opencode_server/models/__init__.py +42 -20
  181. agentpool_server/opencode_server/models/app.py +12 -0
  182. agentpool_server/opencode_server/models/events.py +203 -29
  183. agentpool_server/opencode_server/models/mcp.py +19 -0
  184. agentpool_server/opencode_server/models/message.py +18 -1
  185. agentpool_server/opencode_server/models/parts.py +134 -1
  186. agentpool_server/opencode_server/models/question.py +56 -0
  187. agentpool_server/opencode_server/models/session.py +13 -1
  188. agentpool_server/opencode_server/routes/__init__.py +4 -0
  189. agentpool_server/opencode_server/routes/agent_routes.py +33 -2
  190. agentpool_server/opencode_server/routes/app_routes.py +66 -3
  191. agentpool_server/opencode_server/routes/config_routes.py +66 -5
  192. agentpool_server/opencode_server/routes/file_routes.py +184 -5
  193. agentpool_server/opencode_server/routes/global_routes.py +1 -1
  194. agentpool_server/opencode_server/routes/lsp_routes.py +1 -1
  195. agentpool_server/opencode_server/routes/message_routes.py +122 -66
  196. agentpool_server/opencode_server/routes/permission_routes.py +63 -0
  197. agentpool_server/opencode_server/routes/pty_routes.py +23 -22
  198. agentpool_server/opencode_server/routes/question_routes.py +128 -0
  199. agentpool_server/opencode_server/routes/session_routes.py +139 -68
  200. agentpool_server/opencode_server/routes/tui_routes.py +1 -1
  201. agentpool_server/opencode_server/server.py +47 -2
  202. agentpool_server/opencode_server/state.py +30 -0
  203. agentpool_storage/__init__.py +0 -4
  204. agentpool_storage/base.py +81 -2
  205. agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
  206. agentpool_storage/claude_provider/__init__.py +42 -0
  207. agentpool_storage/{claude_provider.py → claude_provider/provider.py} +190 -8
  208. agentpool_storage/file_provider.py +149 -15
  209. agentpool_storage/memory_provider.py +132 -12
  210. agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
  211. agentpool_storage/opencode_provider/__init__.py +16 -0
  212. agentpool_storage/opencode_provider/helpers.py +414 -0
  213. agentpool_storage/opencode_provider/provider.py +895 -0
  214. agentpool_storage/session_store.py +20 -6
  215. agentpool_storage/sql_provider/sql_provider.py +135 -2
  216. agentpool_storage/sql_provider/utils.py +2 -12
  217. agentpool_storage/zed_provider/__init__.py +16 -0
  218. agentpool_storage/zed_provider/helpers.py +281 -0
  219. agentpool_storage/zed_provider/models.py +130 -0
  220. agentpool_storage/zed_provider/provider.py +442 -0
  221. agentpool_storage/zed_provider.py +803 -0
  222. agentpool_toolsets/__init__.py +0 -2
  223. agentpool_toolsets/builtin/__init__.py +2 -4
  224. agentpool_toolsets/builtin/code.py +4 -4
  225. agentpool_toolsets/builtin/debug.py +115 -40
  226. agentpool_toolsets/builtin/execution_environment.py +54 -165
  227. agentpool_toolsets/builtin/skills.py +0 -77
  228. agentpool_toolsets/builtin/subagent_tools.py +64 -51
  229. agentpool_toolsets/builtin/workers.py +4 -2
  230. agentpool_toolsets/composio_toolset.py +2 -2
  231. agentpool_toolsets/entry_points.py +3 -1
  232. agentpool_toolsets/fsspec_toolset/grep.py +25 -5
  233. agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
  234. agentpool_toolsets/fsspec_toolset/toolset.py +350 -66
  235. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  236. agentpool_toolsets/mcp_discovery/toolset.py +74 -17
  237. agentpool_toolsets/mcp_run_toolset.py +8 -11
  238. agentpool_toolsets/notifications.py +33 -33
  239. agentpool_toolsets/openapi.py +3 -1
  240. agentpool_toolsets/search_toolset.py +3 -1
  241. agentpool_config/resources.py +0 -33
  242. agentpool_server/acp_server/acp_tools.py +0 -43
  243. agentpool_server/acp_server/commands/spawn.py +0 -210
  244. agentpool_storage/opencode_provider.py +0 -730
  245. agentpool_storage/text_log_provider.py +0 -276
  246. agentpool_toolsets/builtin/chain.py +0 -288
  247. agentpool_toolsets/builtin/user_interaction.py +0 -52
  248. agentpool_toolsets/semantic_memory_toolset.py +0 -536
  249. {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
  250. {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from collections.abc import Callable, Mapping
6
+ from contextlib import asynccontextmanager
6
7
  from typing import TYPE_CHECKING, Any, Literal, cast, overload
7
8
 
8
9
  from schemez import Schema
@@ -12,7 +13,7 @@ from agentpool.messaging import ChatMessage
12
13
 
13
14
 
14
15
  if TYPE_CHECKING:
15
- from collections.abc import Sequence
16
+ from collections.abc import AsyncIterator, Sequence
16
17
 
17
18
  from toprompt import AnyPromptType
18
19
 
@@ -90,6 +91,33 @@ class Interactions:
90
91
  def __init__(self, agent: SupportsStructuredOutput) -> None:
91
92
  self.agent = agent
92
93
 
94
+ @asynccontextmanager
95
+ async def _with_structured_output[T](
96
+ self, output_type: type[T]
97
+ ) -> AsyncIterator[SupportsStructuredOutput]:
98
+ """Context manager to temporarily set structured output type and restore afterwards.
99
+
100
+ Args:
101
+ output_type: The type to structure output as
102
+
103
+ Yields:
104
+ The agent configured for structured output
105
+
106
+ Example:
107
+ async with interactions._with_structured_output(MyType) as agent:
108
+ result = await agent.run(prompt)
109
+ """
110
+ # Save original output type
111
+ old_output_type = getattr(self.agent, "_output_type", None)
112
+ try:
113
+ # Configure structured output
114
+ structured_agent = self.agent.to_structured(output_type)
115
+ yield structured_agent
116
+ finally:
117
+ # Restore original output type
118
+ if hasattr(self.agent, "_output_type"):
119
+ self.agent._output_type = old_output_type
120
+
93
121
  # async def conversation(
94
122
  # self,
95
123
  # other: MessageNode[Any, Any],
@@ -218,8 +246,9 @@ Available options:
218
246
 
219
247
  Select ONE option by its exact label."""
220
248
 
221
- # Get LLM's string-based decision
222
- result = await self.agent.run(prompt or default_prompt, output_type=LLMPick)
249
+ # Get LLM's string-based decision using structured output
250
+ async with self._with_structured_output(LLMPick) as structured_agent:
251
+ result = await structured_agent.run(prompt or default_prompt)
223
252
 
224
253
  # Convert to type-safe decision
225
254
  if result.content.selection not in label_map:
@@ -328,7 +357,9 @@ Available options:
328
357
  {picks_info} options by their exact labels.
329
358
  List your selections, one per line, followed by your reasoning."""
330
359
 
331
- result = await self.agent.run(prompt or default_prompt, output_type=LLMMultiPick)
360
+ # Get LLM's multi-selection using structured output
361
+ async with self._with_structured_output(LLMMultiPick) as structured_agent:
362
+ result = await structured_agent.run(prompt or default_prompt)
332
363
 
333
364
  # Validate selections
334
365
  invalid = [s for s in result.content.selections if s not in label_map]
@@ -368,7 +399,9 @@ List your selections, one per line, followed by your reasoning."""
368
399
  instance: item_model # type: ignore
369
400
  # explanation: str | None = None
370
401
 
371
- result = await self.agent.run(final_prompt, output_type=Extraction)
402
+ # Use structured output via context manager
403
+ async with self._with_structured_output(Extraction) as structured_agent:
404
+ result = await structured_agent.run(final_prompt)
372
405
  return as_type(**result.content.instance.model_dump())
373
406
 
374
407
  async def extract_multiple[T](
@@ -404,7 +437,9 @@ List your selections, one per line, followed by your reasoning."""
404
437
  instances: list[item_model] # type: ignore
405
438
  # explanation: str | None = None
406
439
 
407
- result = await self.agent.run(final_prompt, output_type=Extraction)
440
+ # Use structured output via context manager
441
+ async with self._with_structured_output(Extraction) as structured_agent:
442
+ result = await structured_agent.run(final_prompt)
408
443
  num_instances = len(result.content.instances) # Validate counts
409
444
  if len(result.content.instances) < min_items:
410
445
  msg = f"Found only {num_instances} instances, need {min_items}"
agentpool/agents/modes.py CHANGED
@@ -7,6 +7,7 @@ to clients. Each agent type can define its own mode categories.
7
7
  from __future__ import annotations
8
8
 
9
9
  from dataclasses import dataclass, field
10
+ from typing import Literal
10
11
 
11
12
 
12
13
  @dataclass
@@ -52,3 +53,15 @@ class ModeCategory:
52
53
 
53
54
  current_mode_id: str = ""
54
55
  """ID of the currently active mode."""
56
+
57
+ category: Literal["mode", "model", "thought_level", "other"] | None = None
58
+ """Optional semantic category for UX purposes (keyboard shortcuts, icons, placement).
59
+
60
+ This helps clients distinguish common selector types:
61
+ - 'mode': Session mode selector
62
+ - 'model': Model selector
63
+ - 'thought_level': Thought/reasoning level selector
64
+ - 'other': Unknown/uncategorized
65
+
66
+ MUST NOT be required for correctness. Clients should handle gracefully.
67
+ """
@@ -7,10 +7,6 @@ import re
7
7
  from typing import TYPE_CHECKING, Any, cast
8
8
 
9
9
  import anyio
10
- from slashed.events import (
11
- CommandExecutedEvent,
12
- CommandOutputEvent as SlashedCommandOutputEvent,
13
- )
14
10
 
15
11
  from agentpool.agents.events import CommandCompleteEvent, CommandOutputEvent
16
12
  from agentpool.log import get_logger
@@ -112,6 +108,11 @@ class SlashedAgent[TDeps, OutputDataT]:
112
108
  Yields:
113
109
  Command output and completion events
114
110
  """
111
+ from slashed.events import (
112
+ CommandExecutedEvent,
113
+ CommandOutputEvent as SlashedCommandOutputEvent,
114
+ )
115
+
115
116
  parsed = _parse_slash_command(command_text)
116
117
  if not parsed:
117
118
  logger.warning("Invalid slash command", command=command_text)
@@ -9,9 +9,11 @@ import time
9
9
  from typing import TYPE_CHECKING, Any
10
10
 
11
11
  from pydantic_ai import RunContext
12
+ from pydantic_ai.messages import ToolReturn
12
13
 
13
14
  from agentpool.agents.context import AgentContext
14
15
  from agentpool.tasks import ChainAbortedError, RunAbortedError, ToolSkippedError
16
+ from agentpool.tools.base import ToolResult
15
17
  from agentpool.utils.inspection import execute, get_argument_key
16
18
  from agentpool.utils.signatures import create_modified_signature, update_signature
17
19
 
@@ -27,7 +29,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
27
29
  tool: Tool[TReturn],
28
30
  agent_ctx: AgentContext,
29
31
  hooks: AgentHooks | None = None,
30
- ) -> Callable[..., Awaitable[TReturn | None]]:
32
+ ) -> Callable[..., Awaitable[TReturn | ToolReturn | None]]:
31
33
  """Wrap tool with confirmation handling and hooks.
32
34
 
33
35
  Strategy:
@@ -41,7 +43,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
41
43
  agent_ctx: Agent context for confirmation handling and dependency injection.
42
44
  hooks: Optional AgentHooks for pre/post tool execution hooks.
43
45
  """
44
- fn = tool.callable
46
+ fn = tool.get_callable()
45
47
  run_ctx_key = get_argument_key(fn, RunContext)
46
48
  agent_ctx_key = get_argument_key(fn, AgentContext)
47
49
 
@@ -58,7 +60,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
58
60
  tool_input: dict[str, Any],
59
61
  *args: Any,
60
62
  **kwargs: Any,
61
- ) -> TReturn | None:
63
+ ) -> TReturn | None | ToolReturn:
62
64
  """Execute tool with pre/post hooks."""
63
65
  # Pre-tool hooks
64
66
  if hooks:
@@ -79,9 +81,17 @@ def wrap_tool[TReturn]( # noqa: PLR0915
79
81
 
80
82
  # Execute the tool
81
83
  start_time = time.perf_counter()
82
- result = await execute_fn(*args, **kwargs)
84
+ result: TReturn | ToolResult | ToolReturn = await execute_fn(*args, **kwargs)
83
85
  duration_ms = (time.perf_counter() - start_time) * 1000
84
86
 
87
+ # Convert AgentPool ToolResult to pydantic-ai ToolReturn
88
+ if isinstance(result, ToolResult):
89
+ result = ToolReturn(
90
+ return_value=result.structured_content or result.content,
91
+ content=result.content,
92
+ metadata=result.metadata,
93
+ )
94
+
85
95
  # Post-tool hooks
86
96
  if hooks:
87
97
  await hooks.run_post_tool_hooks(
@@ -97,7 +107,9 @@ def wrap_tool[TReturn]( # noqa: PLR0915
97
107
 
98
108
  if run_ctx_key or agent_ctx_key:
99
109
  # Tool has RunContext and/or AgentContext
100
- async def wrapped(ctx: RunContext, *args: Any, **kwargs: Any) -> TReturn | None: # pyright: ignore
110
+ async def wrapped(
111
+ ctx: RunContext, *args: Any, **kwargs: Any
112
+ ) -> TReturn | None | ToolReturn: # pyright: ignore
101
113
  result = await agent_ctx.handle_confirmation(tool, kwargs)
102
114
  if result == "allow":
103
115
  # Populate AgentContext with RunContext data if needed
@@ -135,7 +147,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
135
147
 
136
148
  else:
137
149
  # Tool has no context - normal function call
138
- async def wrapped(*args: Any, **kwargs: Any) -> TReturn | None: # type: ignore[misc]
150
+ async def wrapped(*args: Any, **kwargs: Any) -> TReturn | None | ToolReturn: # type: ignore[misc]
139
151
  result = await agent_ctx.handle_confirmation(tool, kwargs)
140
152
  if result == "allow":
141
153
  tool_input = kwargs.copy()
agentpool/common_types.py CHANGED
@@ -28,15 +28,13 @@ if TYPE_CHECKING:
28
28
  from uuid import UUID
29
29
 
30
30
  from agentpool.agents.events import RichAgentStreamEvent
31
- from agentpool.messaging import ChatMessage
32
- from agentpool.messaging.messagenode import MessageNode
33
31
  from agentpool.tools.base import Tool
34
32
 
35
- # Type alias for team streaming return type (node + event tuples)
36
- type TeamStreamEvent = tuple[MessageNode[Any, Any], RichAgentStreamEvent[Any]]
37
33
  type AnyTransformFn[T] = Callable[[T], T | Awaitable[T]]
38
34
  type OptionalAwaitable[T] = T | Awaitable[T]
39
- type ToolType = str | AnyCallable | Tool
35
+ # Import path string for dynamic tool loading (e.g., "mymodule:my_tool")
36
+ type ImportPathString = str
37
+ type ToolType = ImportPathString | AnyCallable | Tool
40
38
  # Define what we consider JSON-serializable
41
39
  type JsonPrimitive = None | bool | int | float | str
42
40
  type SessionIdType = str | UUID | None
@@ -76,7 +74,7 @@ TeamName = str
76
74
  AgentName = str
77
75
  MessageRole = Literal["user", "assistant"]
78
76
  PartType = Literal["text", "image", "audio", "video"]
79
- ModelType = Model | ModelId | str | None
77
+ ModelType = Model | ModelId | str
80
78
  EnvironmentType = Literal["file", "inline"]
81
79
  ToolSource = Literal["agent", "builtin", "dynamic", "task", "mcp", "toolset"]
82
80
  AnyCallable = Callable[..., Any]
@@ -99,30 +97,46 @@ QueueStrategy = Literal["concat", "latest", "buffer"]
99
97
  """
100
98
 
101
99
 
102
- class SupportsStructuredOutput(Protocol):
103
- """Protocol for nodes that support structured output via run().
104
-
105
- This protocol is used for components that need to call run() with
106
- an output_type parameter (e.g., picker agents, Interactions).
107
- """
108
-
109
- async def run(self, *prompts: Any, output_type: Any = ...) -> ChatMessage[Any]: ...
110
-
111
-
112
100
  @runtime_checkable
113
101
  class SupportsRunStream[TResult](Protocol):
114
102
  """Protocol for nodes that support streaming via run_stream().
115
103
 
116
104
  Used by Team and TeamRun to check if a node can be streamed.
117
-
118
- Return type is a union because:
119
- - Agent returns `AsyncIterator[RichAgentStreamEvent[TResult]]`
120
- - Team/TeamRun return `AsyncIterator[tuple[MessageNode, RichAgentStreamEvent]]`
105
+ All streaming nodes return RichAgentStreamEvent, with subagent/team
106
+ activity wrapped in SubAgentEvent.
121
107
  """
122
108
 
123
109
  def run_stream(
124
110
  self, *prompts: Any, **kwargs: Any
125
- ) -> AsyncIterator[RichAgentStreamEvent[TResult] | TeamStreamEvent]: ...
111
+ ) -> AsyncIterator[RichAgentStreamEvent[TResult]]: ...
112
+
113
+
114
+ @runtime_checkable
115
+ class SupportsStructuredOutput(Protocol):
116
+ """Protocol for agents that support structured output via to_structured().
117
+
118
+ Used by Interactions class for pick/extract operations that require
119
+ structured output from agents.
120
+ """
121
+
122
+ def to_structured[T](self, output_type: type[T]) -> SupportsStructuredOutput:
123
+ """Create a copy of this agent configured for structured output.
124
+
125
+ Args:
126
+ output_type: The type to structure output as
127
+
128
+ Returns:
129
+ New agent instance configured for structured output
130
+ """
131
+ ...
132
+
133
+ async def run(self, *prompts: Any, **kwargs: Any) -> Any:
134
+ """Run the agent with the given prompts.
135
+
136
+ Returns:
137
+ ChatMessage with content typed according to output_type from to_structured
138
+ """
139
+ ...
126
140
 
127
141
 
128
142
  class BaseCode(BaseModel):
@@ -17,8 +17,8 @@ agents:
17
17
  You are a helpful AI assistant with access to the file system and can execute code.
18
18
  You can help with coding, writing, analysis, file operations, and more.
19
19
  Be concise but thorough in your responses.
20
- toolsets:
21
- - type: execution
20
+ tools:
21
+ - type: process_management
22
22
  - type: file_access
23
23
  - type: code
24
24
  - type: search
@@ -47,6 +47,7 @@ responses:
47
47
 
48
48
  agents:
49
49
  url_opener:
50
+ type: native
50
51
  tools:
51
52
  - type: import
52
53
  import_path: "webbrowser.open"
@@ -63,6 +64,7 @@ agents:
63
64
  Always confirm what you're about to open.
64
65
 
65
66
  system_inspector:
67
+ type: native
66
68
  tools:
67
69
  - type: import
68
70
  import_path: "platform.platform"
@@ -84,6 +86,7 @@ agents:
84
86
  Format it using the template from the system_template resource.
85
87
 
86
88
  file_explorer:
89
+ type: native
87
90
  tools:
88
91
  - type: import
89
92
  import_path: "os.listdir"
@@ -1,6 +1,7 @@
1
1
  # yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json
2
2
  agents:
3
3
  simple_agent:
4
+ type: native
4
5
  description: "Basic agent with one tool to open webbrowsers."
5
6
  model: openai:gpt-5-mini
6
7
  tools:
@@ -1,19 +1,20 @@
1
1
  # yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json
2
- # Default ACP assistant configuration
3
- # Used when starting ACP server without explicit config file
4
2
 
5
3
  agents:
6
4
  claude:
7
5
  type: claude_code
8
6
  display_name: "AI Assistant"
9
- builtin_tools: []
10
- env:
11
- ANTHROPIC_API_KEY: ""
7
+ builtin_tools: [] # we use our own tools.
8
+ use_subscription: true
12
9
  description: "Claude code agent with enhanced tools"
13
- toolsets:
14
- - type: execution
10
+ tools:
11
+ - type: bash
12
+ - type: agent_cli
13
+ - type: process_management
15
14
  - type: file_access
16
15
  edit_tool: batch
17
- - type: code
16
+ # - type: code
18
17
  - type: search
19
18
  - type: plan
19
+ - type: question
20
+ # - type: debug
@@ -9,9 +9,10 @@ agents:
9
9
  env: # override to make sure subscription is used
10
10
  ANTHROPIC_API_KEY: ""
11
11
 
12
- toolsets:
12
+ tools:
13
13
  - type: code
14
14
  - type: subagent
15
+ - type: plan
15
16
  codex:
16
17
  type: acp
17
18
  provider: codex
@@ -22,7 +22,7 @@ from agentpool_config.teams import TeamConfig
22
22
  if TYPE_CHECKING:
23
23
  from collections.abc import AsyncIterator, Iterator, Sequence
24
24
 
25
- from evented.configs import EventConfig
25
+ from evented_config import EventConfig
26
26
  from psygnal.containers._evented_list import ListEvents
27
27
  from toprompt import AnyPromptType
28
28
 
@@ -31,7 +31,6 @@ if TYPE_CHECKING:
31
31
  ModelType,
32
32
  ProcessorCallback,
33
33
  PromptCompatible,
34
- SupportsStructuredOutput,
35
34
  ToolType,
36
35
  )
37
36
  from agentpool.delegation.teamrun import ExtendedTeamTalk, TeamRun
@@ -66,9 +65,6 @@ class BaseTeam[TDeps, TResult](MessageNode[TDeps, TResult]):
66
65
  display_name: str | None = None,
67
66
  shared_prompt: str | None = None,
68
67
  mcp_servers: list[str | MCPServerConfig] | None = None,
69
- picker: SupportsStructuredOutput | None = None,
70
- num_picks: int | None = None,
71
- pick_prompt: str | None = None,
72
68
  event_configs: Sequence[EventConfig] | None = None,
73
69
  agent_pool: AgentPool | None = None,
74
70
  ) -> None:
@@ -97,28 +93,6 @@ class BaseTeam[TDeps, TResult](MessageNode[TDeps, TResult]):
97
93
  self.shared_prompt = shared_prompt
98
94
  self._main_task: asyncio.Task[ChatMessage[Any] | None] | None = None
99
95
  self._infinite = False
100
- self.picker = picker
101
- self.num_picks = num_picks
102
- self.pick_prompt = pick_prompt
103
-
104
- async def pick_agents(self, task: str) -> Sequence[MessageNode[Any, Any]]:
105
- """Pick agents to run."""
106
- from agentpool.agents.interactions import Interactions
107
-
108
- if self.picker:
109
- interactions = Interactions(self.picker)
110
- if self.num_picks == 1:
111
- result = await interactions.pick(self, task, self.pick_prompt)
112
- return [result.selection]
113
- multi_result = await interactions.pick_multiple(
114
- self,
115
- task,
116
- min_picks=self.num_picks or 1,
117
- max_picks=self.num_picks,
118
- prompt=self.pick_prompt,
119
- )
120
- return multi_result.selections
121
- return list(self.nodes)
122
96
 
123
97
  def _on_node_changed(
124
98
  self, index: int, old: MessageNode[Any, Any], new: MessageNode[Any, Any]
@@ -382,7 +356,7 @@ class BaseTeam[TDeps, TResult](MessageNode[TDeps, TResult]):
382
356
  team_config = shared_pool.manifest.teams.get(self.name)
383
357
  if not team_config:
384
358
  mode = "parallel" if isinstance(self, Team) else "sequential"
385
- team_config = TeamConfig(name=self.name, mode=mode, members=[])
359
+ team_config = TeamConfig(name=self.name, mode=mode, members=[]) # type: ignore[call-arg]
386
360
  if not pool_ids:
387
361
  logger.debug("No pool found for team.", team=self.name)
388
362
  return TeamContext(
@@ -495,8 +469,8 @@ if __name__ == "__main__":
495
469
  async def main() -> None:
496
470
  from agentpool import Agent, Team
497
471
 
498
- agent = Agent("My Agent")
499
- agent_2 = Agent("My Agent")
472
+ agent = Agent("My Agent", model="openai:gpt-5-nano")
473
+ agent_2 = Agent("My Agent", model="openai:gpt-5-nano")
500
474
  team = Team([agent, agent_2], mcp_servers=["uvx mcp-server-git"])
501
475
  async with team:
502
476
  print(await agent.tools.get_tools())