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
@@ -25,9 +25,13 @@ from agentpool.tools.base import Tool
25
25
 
26
26
 
27
27
  if TYPE_CHECKING:
28
+ from collections.abc import Sequence
29
+
28
30
  from exxec import ExecutionEnvironment
31
+ from slashed import Command
29
32
 
30
33
  from acp.schema import (
34
+ AvailableCommand,
31
35
  CreateTerminalRequest,
32
36
  KillTerminalCommandRequest,
33
37
  ReadTextFileRequest,
@@ -98,11 +102,93 @@ class ACPClientHandler(Client):
98
102
  return self._agent.config.allow_terminal
99
103
 
100
104
  async def session_update(self, params: SessionNotification[Any]) -> None:
101
- """Handle session update notifications from the agent."""
105
+ """Handle session update notifications from the agent.
106
+
107
+ Some updates are state changes (mode, model, config) that should update
108
+ session state. Others are stream events (text chunks, tool calls) that
109
+ should be queued for the run_stream consumer.
110
+ """
111
+ from acp.schema import (
112
+ AvailableCommandsUpdate,
113
+ ConfigOptionUpdate,
114
+ CurrentModelUpdate,
115
+ CurrentModeUpdate,
116
+ )
102
117
  from agentpool.agents.acp_agent.acp_converters import acp_to_native_event
103
118
 
104
119
  update = params.update
105
- # Convert to native event and queue it
120
+
121
+ # Handle state updates - these modify session state, not stream events
122
+ match update:
123
+ case CurrentModeUpdate(current_mode_id=mode_id):
124
+ if self.state.modes:
125
+ self.state.modes.current_mode_id = mode_id
126
+ # Find ModeInfo and emit signal
127
+ for acp_mode in self.state.modes.available_modes:
128
+ if acp_mode.id == mode_id:
129
+ from agentpool.agents.modes import ModeInfo
130
+
131
+ mode_info = ModeInfo(
132
+ id=acp_mode.id,
133
+ name=acp_mode.name,
134
+ description=acp_mode.description or "",
135
+ category_id="remote",
136
+ )
137
+ await self._agent.state_updated.emit(mode_info)
138
+ break
139
+ self.state.current_mode_id = mode_id
140
+ logger.debug("Mode updated", mode_id=mode_id)
141
+ self._update_event.set()
142
+ return
143
+
144
+ case CurrentModelUpdate(current_model_id=model_id):
145
+ self.state.current_model_id = model_id
146
+ if self.state.models:
147
+ self.state.models.current_model_id = model_id
148
+ # Find ModelInfo and emit signal
149
+ for acp_model in self.state.models.available_models:
150
+ if acp_model.model_id == model_id:
151
+ from tokonomics.model_discovery.model_info import (
152
+ ModelInfo as TokoModelInfo,
153
+ )
154
+
155
+ model_info = TokoModelInfo(
156
+ id=acp_model.model_id,
157
+ name=acp_model.name,
158
+ description=acp_model.description,
159
+ )
160
+ await self._agent.state_updated.emit(model_info)
161
+ break
162
+ logger.debug("Model updated", model_id=model_id)
163
+ self._update_event.set()
164
+ return
165
+
166
+ case ConfigOptionUpdate():
167
+ await self._agent.state_updated.emit(update)
168
+ logger.debug("Config option updated", update=update)
169
+ self._update_event.set()
170
+ return
171
+
172
+ case AvailableCommandsUpdate():
173
+ self.state.available_commands = update
174
+ # Populate command store with remote commands
175
+ self._populate_command_store(update.available_commands)
176
+ # Emit to parent session - remote commands will be merged with local ones.
177
+ # The "way back" works because session.split_commands() only extracts
178
+ # LOCAL commands; remote commands pass through to the agent prompt.
179
+ await self._agent.state_updated.emit(update)
180
+ logger.debug("Available commands updated", count=len(update.available_commands))
181
+ self._update_event.set()
182
+ return
183
+
184
+ # TODO: AgentPlanUpdate handling is complex and needs design work.
185
+ # Options:
186
+ # 1. Update pool.todos - requires merging with existing todos
187
+ # 2. Pass through to UI - but then todos aren't centrally managed
188
+ # 3. Switch to agent-owned todos instead of pool-owned
189
+ # For now, AgentPlanUpdate falls through to stream events.
190
+
191
+ # All other updates are stream events - convert and queue
106
192
  if native_event := acp_to_native_event(update):
107
193
  self.state.events.append(native_event)
108
194
  self._update_event.set()
@@ -138,6 +224,8 @@ class ACPClientHandler(Client):
138
224
 
139
225
  if self._input_provider:
140
226
  ctx = self._agent.get_context() # Use the agent's NodeContext
227
+ # Attach tool_call_id for permission event matching in TUI
228
+ ctx.tool_call_id = params.tool_call.tool_call_id
141
229
  # Create a dummy tool representation from ACP params
142
230
  tool = Tool(callable=lambda: None, name=params.tool_call.tool_call_id, description=name)
143
231
  # Extract arguments - ACP doesn't expose them in ToolCall
@@ -301,13 +389,79 @@ class ACPClientHandler(Client):
301
389
  """Handle extension notifications."""
302
390
  logger.debug("Extension notification", method=method)
303
391
 
392
+ def _populate_command_store(self, commands: Sequence[AvailableCommand]) -> None:
393
+ """Populate the agent's command store with remote ACP commands.
394
+
395
+ Args:
396
+ commands: List of AvailableCommand objects from the remote agent
397
+ """
398
+ store = self._agent.command_store
399
+
400
+ for cmd in commands:
401
+ command = self._create_acp_command(cmd)
402
+ # Unregister if already exists (in case of update)
403
+ if store.get_command(cmd.name):
404
+ store.unregister_command(cmd.name)
405
+ store.register_command(command)
406
+
407
+ logger.debug("Populated command store", command_count=len(store.list_commands()))
408
+
409
+ def _create_acp_command(self, cmd: AvailableCommand) -> Command:
410
+ """Create a slashed Command from an ACP AvailableCommand.
411
+
412
+ The command, when executed, sends a prompt with the slash command
413
+ to the remote ACP agent.
414
+
415
+ Args:
416
+ cmd: AvailableCommand from remote agent
417
+
418
+ Returns:
419
+ A slashed Command that sends the command to the remote agent
420
+ """
421
+ from pydantic_ai import PartDeltaEvent, TextPartDelta
422
+ from slashed import Command
423
+
424
+ name = cmd.name
425
+ description = cmd.description
426
+ input_hint = cmd.input.root.hint if cmd.input else None
427
+
428
+ async def execute_command(
429
+ ctx: Any,
430
+ args: list[str],
431
+ kwargs: dict[str, str],
432
+ ) -> None:
433
+ """Execute the remote ACP slash command."""
434
+ # Build command string
435
+ args_str = " ".join(args) if args else ""
436
+ if kwargs:
437
+ kwargs_str = " ".join(f"{k}={v}" for k, v in kwargs.items())
438
+ args_str = f"{args_str} {kwargs_str}".strip()
439
+
440
+ full_command = f"/{name} {args_str}".strip()
441
+
442
+ # Execute via agent run_stream - the slash command goes as a prompt
443
+ async for event in self._agent.run_stream(full_command):
444
+ # Extract text from PartDeltaEvent with TextPartDelta
445
+ if isinstance(event, PartDeltaEvent):
446
+ delta = event.delta
447
+ if isinstance(delta, TextPartDelta):
448
+ await ctx.print(delta.content_delta)
449
+
450
+ return Command.from_raw(
451
+ execute_command,
452
+ name=name,
453
+ description=description,
454
+ category="remote",
455
+ usage=input_hint,
456
+ )
457
+
304
458
 
305
459
  if __name__ == "__main__":
306
460
  from agentpool.agents.acp_agent import ACPAgent
307
461
 
308
462
  async def main() -> None:
309
463
  """Demo: Basic call to an ACP agent."""
310
- args = ["run", "agentpool", "serve-acp", "--model-provider", "openai"]
464
+ args = ["run", "agentpool", "serve-acp"]
311
465
  cwd = str(Path.cwd())
312
466
  async with ACPAgent(command="uv", args=args, cwd=cwd, event_handlers=["detailed"]) as agent:
313
467
  print("Response (streaming): ", end="", flush=True)
@@ -9,7 +9,7 @@ from agentpool.log import get_logger
9
9
 
10
10
 
11
11
  if TYPE_CHECKING:
12
- from acp.schema import SessionModelState, SessionModeState
12
+ from acp.schema import AvailableCommandsUpdate, SessionModelState, SessionModeState
13
13
  from agentpool.agents.events import RichAgentStreamEvent
14
14
 
15
15
  logger = get_logger(__name__)
@@ -39,6 +39,9 @@ class ACPSessionState:
39
39
  current_mode_id: str | None = None
40
40
  """Current mode ID."""
41
41
 
42
+ available_commands: AvailableCommandsUpdate | None = None
43
+ """Available commands from the agent."""
44
+
42
45
  def clear(self) -> None:
43
46
  self.events.clear()
44
47
  # Note: Don't clear current_model_id or models - those persist across prompts