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
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  from typing import TYPE_CHECKING, Literal
6
6
 
7
7
  from pydantic import ConfigDict, Field
8
- from tokonomics.model_discovery import ProviderType # noqa: TC002
9
8
 
10
9
  from agentpool.models.acp_agents.base import BaseACPAgentConfig
11
10
 
@@ -67,6 +66,12 @@ class CodexACPAgentConfig(BaseACPAgentConfig):
67
66
  )
68
67
  """Model override."""
69
68
 
69
+ auto_approve: bool = Field(
70
+ default=False,
71
+ title="Auto Approve",
72
+ )
73
+ """Automatically accept all actions (YOLO mode)."""
74
+
70
75
  sandbox_permissions: list[str] | None = Field(
71
76
  default=None,
72
77
  title="Sandbox Permissions",
@@ -85,11 +90,6 @@ class CodexACPAgentConfig(BaseACPAgentConfig):
85
90
  """Get the command to spawn the ACP server."""
86
91
  return "npx"
87
92
 
88
- @property
89
- def model_providers(self) -> list[ProviderType]:
90
- """Codex uses OpenAI models."""
91
- return ["openai"]
92
-
93
93
  async def get_args(self, prompt_manager: PromptManager | None = None) -> list[str]:
94
94
  """Build command arguments from settings."""
95
95
  args: list[str] = ["@zed-industries/codex-acp"]
@@ -105,6 +105,8 @@ class CodexACPAgentConfig(BaseACPAgentConfig):
105
105
  "-c",
106
106
  f"shell_environment_policy.inherit={self.shell_environment_policy_inherit}",
107
107
  ])
108
+ if self.auto_approve:
109
+ args.extend(["-c", "approval_mode=yolo"])
108
110
 
109
111
  return args
110
112
 
@@ -142,11 +144,6 @@ class OpenCodeACPAgentConfig(BaseACPAgentConfig):
142
144
  args.extend(["--cwd", self.cwd])
143
145
  return args
144
146
 
145
- @property
146
- def model_providers(self) -> list[ProviderType]:
147
- """OpenCode supports multiple providers."""
148
- return ["openai", "anthropic", "gemini", "openrouter"]
149
-
150
147
 
151
148
  class GooseACPAgentConfig(BaseACPAgentConfig):
152
149
  """Configuration for Goose via ACP.
@@ -176,11 +173,6 @@ class GooseACPAgentConfig(BaseACPAgentConfig):
176
173
  """Build command arguments from settings."""
177
174
  return ["acp"]
178
175
 
179
- @property
180
- def model_providers(self) -> list[ProviderType]:
181
- """Goose supports multiple providers."""
182
- return ["openai", "anthropic", "gemini", "openrouter"]
183
-
184
176
 
185
177
  class MistralACPAgentConfig(BaseACPAgentConfig):
186
178
  """Configuration for Mistral Agent via ACP.
@@ -208,11 +200,6 @@ class MistralACPAgentConfig(BaseACPAgentConfig):
208
200
  """Build command arguments from settings."""
209
201
  return []
210
202
 
211
- @property
212
- def model_providers(self) -> list[ProviderType]:
213
- """Goose supports multiple providers."""
214
- return ["mistral"]
215
-
216
203
 
217
204
  class OpenHandsACPAgentConfig(BaseACPAgentConfig):
218
205
  """Configuration for OpenHands via ACP.
@@ -242,11 +229,6 @@ class OpenHandsACPAgentConfig(BaseACPAgentConfig):
242
229
  """Build command arguments from settings."""
243
230
  return ["acp"]
244
231
 
245
- @property
246
- def model_providers(self) -> list[ProviderType]:
247
- """OpenHands supports multiple providers."""
248
- return ["openai", "anthropic", "gemini", "openrouter"]
249
-
250
232
 
251
233
  class AmpACPAgentConfig(BaseACPAgentConfig):
252
234
  """Configuration for Amp (AmpCode) via ACP.
@@ -292,11 +274,6 @@ class AmpACPAgentConfig(BaseACPAgentConfig):
292
274
  """Build command arguments for amp-acp bridge."""
293
275
  return ["-y", "amp-acp"]
294
276
 
295
- @property
296
- def model_providers(self) -> list[ProviderType]:
297
- """Amp supports multiple providers."""
298
- return ["openai", "anthropic", "gemini"]
299
-
300
277
 
301
278
  class CagentACPAgentConfig(BaseACPAgentConfig):
302
279
  """Configuration for Docker cagent via ACP.
@@ -393,11 +370,6 @@ class CagentACPAgentConfig(BaseACPAgentConfig):
393
370
 
394
371
  return args
395
372
 
396
- @property
397
- def model_providers(self) -> list[ProviderType]:
398
- """Cagent supports multiple providers via MCP."""
399
- return ["openai", "anthropic", "gemini"]
400
-
401
373
 
402
374
  class StakpakACPAgentConfig(BaseACPAgentConfig):
403
375
  """Configuration for Stakpak Agent via ACP.
@@ -538,11 +510,6 @@ class StakpakACPAgentConfig(BaseACPAgentConfig):
538
510
 
539
511
  return args
540
512
 
541
- @property
542
- def model_providers(self) -> list[ProviderType]:
543
- """Stakpak supports multiple providers."""
544
- return ["openai", "anthropic", "gemini"]
545
-
546
513
 
547
514
  class VTCodeACPAgentConfig(BaseACPAgentConfig):
548
515
  """Configuration for VT Code via ACP.
@@ -644,7 +611,7 @@ class VTCodeACPAgentConfig(BaseACPAgentConfig):
644
611
  )
645
612
  """Configuration file path."""
646
613
 
647
- skip_confirmations: bool = Field(default=False, title="Skip Confirmations")
614
+ auto_approve: bool = Field(default=False, title="Auto Approve")
648
615
  """Skip safety confirmations."""
649
616
 
650
617
  full_auto: bool = Field(default=False, title="Full Auto")
@@ -684,18 +651,13 @@ class VTCodeACPAgentConfig(BaseACPAgentConfig):
684
651
  args.extend(["--max-tool-calls", str(self.max_tool_calls)])
685
652
  if self.config:
686
653
  args.extend(["--config", self.config])
687
- if self.skip_confirmations:
654
+ if self.auto_approve:
688
655
  args.append("--skip-confirmations")
689
656
  if self.full_auto:
690
657
  args.append("--full-auto")
691
658
 
692
659
  return args
693
660
 
694
- @property
695
- def model_providers(self) -> list[ProviderType]:
696
- """VT Code supports multiple providers."""
697
- return ["openai", "anthropic", "gemini"]
698
-
699
661
 
700
662
  class CursorACPAgentConfig(BaseACPAgentConfig):
701
663
  """Configuration for Cursor via ACP.
@@ -748,25 +710,13 @@ class CursorACPAgentConfig(BaseACPAgentConfig):
748
710
  )
749
711
  """Session storage directory (default: ~/.cursor-sessions)."""
750
712
 
751
- timeout: int | None = Field(
752
- default=None,
753
- title="Timeout",
754
- examples=[30000, 60000],
755
- )
713
+ timeout: int | None = Field(default=None, title="Timeout", examples=[30000, 60000])
756
714
  """Cursor-agent timeout in milliseconds (default: 30000)."""
757
715
 
758
- retries: int | None = Field(
759
- default=None,
760
- title="Retries",
761
- examples=[3, 5],
762
- )
716
+ retries: int | None = Field(default=None, title="Retries", examples=[3, 5])
763
717
  """Number of retries for cursor-agent commands (default: 3)."""
764
718
 
765
- max_sessions: int | None = Field(
766
- default=None,
767
- title="Max Sessions",
768
- examples=[100, 200],
769
- )
719
+ max_sessions: int | None = Field(default=None, title="Max Sessions", examples=[100, 200])
770
720
  """Maximum number of concurrent sessions (default: 100)."""
771
721
 
772
722
  session_timeout: int | None = Field(
@@ -782,11 +732,7 @@ class CursorACPAgentConfig(BaseACPAgentConfig):
782
732
  no_terminal: bool = Field(default=False, title="No Terminal")
783
733
  """Disable terminal tools."""
784
734
 
785
- max_processes: int | None = Field(
786
- default=None,
787
- title="Max Processes",
788
- examples=[5, 10],
789
- )
735
+ max_processes: int | None = Field(default=None, title="Max Processes", examples=[5, 10])
790
736
  """Maximum number of terminal processes (default: 5)."""
791
737
 
792
738
  def get_command(self) -> str:
@@ -821,10 +767,120 @@ class CursorACPAgentConfig(BaseACPAgentConfig):
821
767
  args.extend(["--max-processes", str(self.max_processes)])
822
768
  return args
823
769
 
824
- @property
825
- def model_providers(self) -> list[ProviderType]:
826
- """Cursor supports multiple providers."""
827
- return ["openai", "anthropic", "gemini", "openrouter"]
770
+
771
+ class GeminiACPAgentConfig(BaseACPAgentConfig):
772
+ """Configuration for Gemini CLI via ACP.
773
+
774
+ Provides typed settings for the gemini CLI with ACP support.
775
+
776
+ Note:
777
+ Gemini CLI does not support runtime MCP server injection via config.
778
+ MCP servers must be pre-configured using `gemini mcp add` command.
779
+
780
+ Example:
781
+ ```yaml
782
+ agents:
783
+ coder:
784
+ type: acp
785
+ provider: gemini
786
+ cwd: /path/to/project
787
+ model: gemini-2.5-pro
788
+ approval_mode: auto_edit
789
+ allowed_tools:
790
+ - read_file
791
+ - write_file
792
+ - terminal
793
+ ```
794
+ """
795
+
796
+ model_config = ConfigDict(json_schema_extra={"title": "Gemini ACP Agent Configuration"})
797
+
798
+ provider: Literal["gemini"] = Field("gemini", init=False)
799
+ """Discriminator for Gemini ACP agent."""
800
+
801
+ model: str | None = Field(
802
+ default=None,
803
+ title="Model",
804
+ examples=["gemini-2.5-pro", "gemini-2.5-flash"],
805
+ )
806
+ """Model override."""
807
+
808
+ approval_mode: Literal["default", "auto_edit", "yolo"] | None = Field(
809
+ default=None,
810
+ title="Approval Mode",
811
+ examples=["auto_edit", "yolo"],
812
+ )
813
+ """Approval mode for tool execution."""
814
+
815
+ sandbox: bool = Field(default=False, title="Sandbox")
816
+ """Run in sandbox mode."""
817
+
818
+ auto_approve: bool = Field(default=False, title="Auto Approve")
819
+ """Automatically accept all actions."""
820
+
821
+ allowed_tools: list[str] | None = Field(
822
+ default=None,
823
+ title="Allowed Tools",
824
+ examples=[["read_file", "write_file", "terminal"], ["search"]],
825
+ )
826
+ """Tools allowed to run without confirmation."""
827
+
828
+ allowed_mcp_server_names: list[str] | None = Field(
829
+ default=None,
830
+ title="Allowed MCP Server Names",
831
+ examples=[["filesystem", "github"], ["slack"]],
832
+ )
833
+ """Allowed MCP server names."""
834
+
835
+ extensions: list[str] | None = Field(
836
+ default=None,
837
+ title="Extensions",
838
+ examples=[["python", "typescript"], ["rust", "go"]],
839
+ )
840
+ """List of extensions to use. If not provided, all are used."""
841
+
842
+ include_directories: list[str] | None = Field(
843
+ default=None,
844
+ title="Include Directories",
845
+ examples=[["/path/to/lib", "/path/to/shared"], ["./vendor"]],
846
+ )
847
+ """Additional directories to include in the workspace."""
848
+
849
+ output_format: Literal["text", "json", "stream-json"] | None = Field(
850
+ default=None,
851
+ title="Output Format",
852
+ examples=["json", "stream-json"],
853
+ )
854
+ """Output format."""
855
+
856
+ def get_command(self) -> str:
857
+ """Get the command to spawn the ACP server."""
858
+ return "gemini"
859
+
860
+ async def get_args(self, prompt_manager: PromptManager | None = None) -> list[str]:
861
+ """Build command arguments from settings."""
862
+ args: list[str] = ["--experimental-acp"]
863
+
864
+ if self.model:
865
+ args.extend(["--model", self.model])
866
+ if self.approval_mode:
867
+ args.extend(["--approval-mode", self.approval_mode])
868
+ if self.sandbox:
869
+ args.append("--sandbox")
870
+ if self.auto_approve:
871
+ args.append("--yolo")
872
+ if self.allowed_tools:
873
+ args.extend(["--allowed-tools", *self.allowed_tools])
874
+ if self.allowed_mcp_server_names:
875
+ args.extend(["--allowed-mcp-server-names", *self.allowed_mcp_server_names])
876
+ if self.extensions:
877
+ args.extend(["--extensions", *self.extensions])
878
+ if self.include_directories:
879
+ args.extend(["--include-directories", *self.include_directories])
880
+ if self.output_format:
881
+ args.extend(["--output-format", self.output_format])
882
+
883
+ return args
828
884
 
829
885
 
830
886
  # Union of all ACP agent config types
@@ -839,4 +895,5 @@ RegularACPAgentConfigTypes = (
839
895
  | MistralACPAgentConfig
840
896
  | VTCodeACPAgentConfig
841
897
  | CursorACPAgentConfig
898
+ | GeminiACPAgentConfig
842
899
  )
@@ -12,6 +12,7 @@ from llmling_models.configs import AnyModelConfig # noqa: TC002
12
12
  from pydantic import ConfigDict, Field, model_validator
13
13
  from pydantic_ai import UsageLimits # noqa: TC002
14
14
  from schemez import InlineSchemaDef
15
+ from tokonomics.model_discovery import ProviderType # noqa: TC002
15
16
  from tokonomics.model_names import ModelId # noqa: TC002
16
17
  from toprompt import render_prompt
17
18
 
@@ -19,6 +20,7 @@ from agentpool import log
19
20
  from agentpool.common_types import EndStrategy # noqa: TC001
20
21
  from agentpool.prompts.prompts import PromptMessage, StaticPrompt
21
22
  from agentpool.resource_providers import StaticResourceProvider
23
+ from agentpool_config.builtin_tools import BaseBuiltinToolConfig
22
24
  from agentpool_config.hooks import HooksConfig # noqa: TC001
23
25
  from agentpool_config.knowledge import Knowledge # noqa: TC001
24
26
  from agentpool_config.nodes import BaseAgentConfig
@@ -37,7 +39,6 @@ if TYPE_CHECKING:
37
39
 
38
40
 
39
41
  ToolMode = Literal["codemode"]
40
- AutoCache = Literal["off", "5m", "1h"]
41
42
 
42
43
  logger = log.get_logger(__name__)
43
44
 
@@ -230,6 +231,18 @@ class NativeAgentConfig(BaseAgentConfig):
230
231
  usage_limits: UsageLimits | None = Field(default=None, title="Usage limits")
231
232
  """Usage limits for this agent."""
232
233
 
234
+ model_providers: list[ProviderType] | None = Field(
235
+ default=None,
236
+ examples=[["models.dev"], ["anthropic", "openai"]],
237
+ title="Model providers",
238
+ )
239
+ """List of model providers to use for model discovery.
240
+
241
+ When set, the agent's get_available_models() will return models from these
242
+ providers. Common values: "openai", "anthropic", "gemini", "mistral", etc.
243
+ If not set, defaults to ["models.dev"].
244
+ """
245
+
233
246
  tool_mode: ToolMode | None = Field(
234
247
  default=None,
235
248
  examples=["codemode"],
@@ -240,17 +253,6 @@ class NativeAgentConfig(BaseAgentConfig):
240
253
  - "codemode": Tools are wrapped in a Python execution environment
241
254
  """
242
255
 
243
- auto_cache: AutoCache = Field(
244
- default="off",
245
- examples=["off", "5m", "1h"],
246
- title="Automatic caching",
247
- )
248
- """Automatic prompt caching configuration:
249
- - "off": No automatic caching
250
- - "5m": Add cache point with 5 minute TTL
251
- - "1h": Add cache point with 1 hour TTL
252
- """
253
-
254
256
  hooks: HooksConfig | None = Field(
255
257
  default=None,
256
258
  title="Lifecycle hooks",
@@ -312,7 +314,7 @@ class NativeAgentConfig(BaseAgentConfig):
312
314
  return providers
313
315
 
314
316
  def get_tool_provider(self) -> ResourceProvider | None:
315
- """Get tool provider for this agent."""
317
+ """Get tool provider for this agent (excludes builtin tools)."""
316
318
  from agentpool.tools.base import Tool
317
319
 
318
320
  # Create provider for static tools
@@ -320,6 +322,9 @@ class NativeAgentConfig(BaseAgentConfig):
320
322
  return None
321
323
  static_tools: list[Tool] = []
322
324
  for tool_config in self.tools:
325
+ # Skip builtin tools - they're handled via get_builtin_tools()
326
+ if isinstance(tool_config, BaseBuiltinToolConfig):
327
+ continue
323
328
  try:
324
329
  match tool_config:
325
330
  case str():
@@ -331,8 +336,25 @@ class NativeAgentConfig(BaseAgentConfig):
331
336
  logger.exception("Failed to load tool", config=tool_config)
332
337
  continue
333
338
 
339
+ if not static_tools:
340
+ return None
334
341
  return StaticResourceProvider(name="builtin", tools=static_tools)
335
342
 
343
+ def get_builtin_tools(self) -> list[Any]:
344
+ """Get pydantic-ai builtin tools from config.
345
+
346
+ Returns:
347
+ List of AbstractBuiltinTool instances (WebSearchTool, etc.)
348
+ """
349
+ builtin_tools: list[Any] = []
350
+ for tool_config in self.tools:
351
+ if isinstance(tool_config, BaseBuiltinToolConfig):
352
+ try:
353
+ builtin_tools.append(tool_config.get_builtin_tool())
354
+ except Exception:
355
+ logger.exception("Failed to load builtin tool", config=tool_config)
356
+ return builtin_tools
357
+
336
358
  def get_session_config(self) -> MemoryConfig:
337
359
  """Get resolved memory configuration."""
338
360
  match self.session:
@@ -18,6 +18,26 @@ if TYPE_CHECKING:
18
18
 
19
19
 
20
20
  PermissionMode = Literal["default", "acceptEdits", "plan", "bypassPermissions"]
21
+ ToolName = Literal[
22
+ "Task",
23
+ "TaskOutput",
24
+ "Bash",
25
+ "Glob",
26
+ "Grep",
27
+ "ExitPlanMode",
28
+ "Read",
29
+ "Edit",
30
+ "Write",
31
+ "NotebookEdit",
32
+ "WebFetch",
33
+ "TodoWrite",
34
+ "WebSearch",
35
+ "KillShell",
36
+ "AskUserQuestion",
37
+ "Skill",
38
+ "EnterPlanMode",
39
+ "LSP",
40
+ ]
21
41
 
22
42
 
23
43
  class ClaudeCodeAgentConfig(BaseAgentConfig):
@@ -74,7 +94,7 @@ class ClaudeCodeAgentConfig(BaseAgentConfig):
74
94
  )
75
95
  """Model to use for this agent. Defaults to Claude's default model."""
76
96
 
77
- allowed_tools: list[str] | None = Field(
97
+ allowed_tools: list[ToolName | str] | None = Field(
78
98
  default=None,
79
99
  title="Allowed Tools",
80
100
  examples=[["Read", "Write", "Bash"], ["Read", "Grep", "Glob"]],
@@ -85,7 +105,7 @@ class ClaudeCodeAgentConfig(BaseAgentConfig):
85
105
  Common tools: Read, Write, Edit, Bash, Glob, Grep, Task, WebFetch, etc.
86
106
  """
87
107
 
88
- disallowed_tools: list[str] | None = Field(
108
+ disallowed_tools: list[ToolName | str] | None = Field(
89
109
  default=None,
90
110
  title="Disallowed Tools",
91
111
  examples=[["Bash", "Write"], ["Task"]],
@@ -132,6 +152,17 @@ class ClaudeCodeAgentConfig(BaseAgentConfig):
132
152
  )
133
153
  """Maximum number of conversation turns before stopping."""
134
154
 
155
+ max_budget_usd: float | None = Field(
156
+ default=None,
157
+ title="Max Budget (USD)",
158
+ ge=0.0,
159
+ examples=[1.0, 5.0, 10.0],
160
+ )
161
+ """Maximum budget in USD before stopping.
162
+
163
+ When set, the agent will stop once the estimated cost exceeds this limit.
164
+ """
165
+
135
166
  max_thinking_tokens: int | None = Field(
136
167
  default=None,
137
168
  title="Max Thinking Tokens",
@@ -17,6 +17,7 @@ from agentpool.models.agui_agents import AGUIAgentConfig
17
17
  from agentpool.models.claude_code_agents import ClaudeCodeAgentConfig
18
18
  from agentpool.models.file_agents import FileAgentConfig
19
19
  from agentpool_config.commands import CommandConfig, StaticCommandConfig
20
+ from agentpool_config.compaction import CompactionConfig
20
21
  from agentpool_config.converters import ConversionConfig
21
22
  from agentpool_config.mcp_server import BaseMCPServerConfig, MCPServerConfig
22
23
  from agentpool_config.observability import ObservabilityConfig
@@ -38,6 +39,7 @@ from agentpool_config.workers import (
38
39
  if TYPE_CHECKING:
39
40
  from upathtools import JoinablePathLike
40
41
 
42
+ from agentpool.messaging.compaction import CompactionPipeline
41
43
  from agentpool.models.acp_agents import BaseACPAgentConfig
42
44
  from agentpool.prompts.manager import PromptManager
43
45
  from agentpool.vfs_registry import VFSRegistry
@@ -77,6 +79,12 @@ class AgentsManifest(Schema):
77
79
  INHERIT: str | list[str] | None = None
78
80
  """Inheritance references."""
79
81
 
82
+ name: str | None = None
83
+ """Optional name for this manifest.
84
+
85
+ Useful for identification when working with multiple configurations.
86
+ """
87
+
80
88
  resources: dict[str, ResourceConfig] = Field(
81
89
  default_factory=dict,
82
90
  examples=[
@@ -272,6 +280,30 @@ class AgentsManifest(Schema):
272
280
  path: "./prompts/analysis.md"
273
281
  """
274
282
 
283
+ compaction: CompactionConfig | None = None
284
+ """Compaction configuration for message history management.
285
+
286
+ Controls how conversation history is compacted/summarized to manage context size.
287
+ Can use a preset or define custom steps:
288
+ compaction:
289
+ preset: balanced # or: minimal, summarizing
290
+
291
+ Or custom steps:
292
+ compaction:
293
+ steps:
294
+ - type: filter_thinking
295
+ - type: summarize
296
+ model: openai:gpt-4o-mini
297
+ threshold: 15
298
+ """
299
+
300
+ config_file_path: str | None = Field(default=None, exclude=True)
301
+ """Path to the configuration file this manifest was loaded from.
302
+
303
+ Set automatically by `from_file()`. Used for resolving relative paths.
304
+ Excluded from serialization.
305
+ """
306
+
275
307
  model_config = ConfigDict(
276
308
  json_schema_extra={
277
309
  "x-icon": "octicon:file-code-16",
@@ -583,6 +615,16 @@ class AgentsManifest(Schema):
583
615
 
584
616
  return PromptManager(self.prompts)
585
617
 
618
+ def get_compaction_pipeline(self) -> CompactionPipeline | None:
619
+ """Get the configured compaction pipeline, if any.
620
+
621
+ Returns:
622
+ CompactionPipeline instance or None if not configured
623
+ """
624
+ if self.compaction is None:
625
+ return None
626
+ return self.compaction.build()
627
+
586
628
  # @model_validator(mode="after")
587
629
  # def validate_response_types(self) -> AgentsManifest:
588
630
  # """Ensure all agent output_types exist in responses or are inline."""
@@ -623,6 +665,7 @@ class AgentsManifest(Schema):
623
665
 
624
666
  return agent_def.model_copy(
625
667
  update={
668
+ "config_file_path": path_str,
626
669
  "agents": update_with_path(agent_def.agents),
627
670
  "teams": update_with_path(agent_def.teams),
628
671
  }
agentpool/repomap.py CHANGED
@@ -1109,7 +1109,7 @@ def get_file_map_from_content( # noqa: PLR0915
1109
1109
 
1110
1110
  header = (
1111
1111
  f"# File: {filename} ({lines} lines)\n"
1112
- f"# Structure map (~{tokens_approx} tokens). Use read_file with line/limit for details.\n\n"
1112
+ f"# Structure map (~{tokens_approx} tokens). Use read with line/limit for details.\n\n"
1113
1113
  )
1114
1114
 
1115
1115
  result = header + f"{filename}:\n" + tree_output
@@ -1,6 +1,11 @@
1
1
  """Resource provider implementations."""
2
2
 
3
- from agentpool.resource_providers.base import ResourceProvider
3
+ from agentpool.resource_providers.base import (
4
+ ProviderKind,
5
+ ResourceChangeEvent,
6
+ ResourceProvider,
7
+ ResourceType,
8
+ )
4
9
  from agentpool.resource_providers.static import StaticResourceProvider
5
10
  from agentpool.resource_providers.filtering import FilteringResourceProvider
6
11
  from agentpool.resource_providers.aggregating import AggregatingResourceProvider
@@ -12,6 +17,9 @@ __all__ = [
12
17
  "FilteringResourceProvider",
13
18
  "MCPResourceProvider",
14
19
  "PlanProvider",
20
+ "ProviderKind",
21
+ "ResourceChangeEvent",
15
22
  "ResourceProvider",
23
+ "ResourceType",
16
24
  "StaticResourceProvider",
17
25
  ]
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from agentpool.resource_providers import ResourceProvider
7
+ from agentpool.resource_providers.base import ResourceChangeEvent, ResourceProvider
8
8
 
9
9
 
10
10
  if TYPE_CHECKING:
@@ -14,9 +14,17 @@ if TYPE_CHECKING:
14
14
  from agentpool.tools.base import Tool
15
15
  from agentpool_config.resources import ResourceInfo
16
16
 
17
+ _ = ResourceChangeEvent # Used at runtime in method signatures
18
+
17
19
 
18
20
  class AggregatingResourceProvider(ResourceProvider):
19
- """Provider that combines resources from multiple providers."""
21
+ """Provider that combines resources from multiple providers.
22
+
23
+ Automatically forwards change signals from child providers.
24
+ When a child emits tools_changed, this provider re-emits it.
25
+ """
26
+
27
+ kind = "aggregating"
20
28
 
21
29
  def __init__(self, providers: list[ResourceProvider], name: str = "aggregating") -> None:
22
30
  """Initialize provider with list of providers to aggregate.
@@ -26,9 +34,50 @@ class AggregatingResourceProvider(ResourceProvider):
26
34
  name: Name for this provider
27
35
  """
28
36
  super().__init__(name=name)
29
- # Store reference to the providers list for dynamic updates
37
+ self._providers: list[ResourceProvider] = []
38
+ # Use property setter to set up signal forwarding
30
39
  self.providers = providers
31
40
 
41
+ @property
42
+ def providers(self) -> list[ResourceProvider]:
43
+ """Get the list of child providers."""
44
+ return self._providers
45
+
46
+ @providers.setter
47
+ def providers(self, value: list[ResourceProvider]) -> None:
48
+ """Set the list of child providers and set up signal forwarding."""
49
+ # Disconnect from old providers
50
+ for provider in self._providers:
51
+ provider.tools_changed.disconnect(self._forward_tools_changed)
52
+ provider.prompts_changed.disconnect(self._forward_prompts_changed)
53
+ provider.resources_changed.disconnect(self._forward_resources_changed)
54
+ provider.skills_changed.disconnect(self._forward_skills_changed)
55
+
56
+ self._providers = value
57
+
58
+ # Connect to new providers
59
+ for provider in self._providers:
60
+ provider.tools_changed.connect(self._forward_tools_changed)
61
+ provider.prompts_changed.connect(self._forward_prompts_changed)
62
+ provider.resources_changed.connect(self._forward_resources_changed)
63
+ provider.skills_changed.connect(self._forward_skills_changed)
64
+
65
+ async def _forward_tools_changed(self, event: ResourceChangeEvent) -> None:
66
+ """Forward tools_changed signal from child provider."""
67
+ await self.tools_changed.emit(event)
68
+
69
+ async def _forward_prompts_changed(self, event: ResourceChangeEvent) -> None:
70
+ """Forward prompts_changed signal from child provider."""
71
+ await self.prompts_changed.emit(event)
72
+
73
+ async def _forward_resources_changed(self, event: ResourceChangeEvent) -> None:
74
+ """Forward resources_changed signal from child provider."""
75
+ await self.resources_changed.emit(event)
76
+
77
+ async def _forward_skills_changed(self, event: ResourceChangeEvent) -> None:
78
+ """Forward skills_changed signal from child provider."""
79
+ await self.skills_changed.emit(event)
80
+
32
81
  async def get_tools(self) -> list[Tool]:
33
82
  """Get tools from all providers."""
34
83
  return [t for provider in self.providers for t in await provider.get_tools()]