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
@@ -9,7 +9,7 @@ This is the reverse of the conversion done in acp_server/session.py handle_event
9
9
  from __future__ import annotations
10
10
 
11
11
  import base64
12
- from typing import TYPE_CHECKING, Any, overload
12
+ from typing import TYPE_CHECKING, Any, assert_never, overload
13
13
 
14
14
  from pydantic_ai import (
15
15
  AudioUrl,
@@ -53,12 +53,24 @@ from agentpool.agents.events import (
53
53
  if TYPE_CHECKING:
54
54
  from collections.abc import Sequence
55
55
 
56
- from pydantic_ai import UserContent
57
-
58
- from acp.schema import ContentBlock, SessionUpdate
59
- from acp.schema.mcp import HttpMcpServer, McpServer, SseMcpServer, StdioMcpServer
60
- from acp.schema.tool_call import ToolCallContent, ToolCallLocation
56
+ from pydantic_ai import FinishReason, UserContent
57
+
58
+ from acp.schema import (
59
+ ContentBlock,
60
+ HttpMcpServer,
61
+ McpServer,
62
+ SessionConfigOption,
63
+ SessionModelState,
64
+ SessionModeState,
65
+ SessionUpdate,
66
+ SseMcpServer,
67
+ StdioMcpServer,
68
+ StopReason,
69
+ ToolCallContent,
70
+ ToolCallLocation,
71
+ )
61
72
  from agentpool.agents.events import RichAgentStreamEvent, ToolCallContentItem
73
+ from agentpool.agents.modes import ModeCategory, ModeInfo
62
74
  from agentpool_config.mcp_server import (
63
75
  MCPServerConfig,
64
76
  SSEMCPServerConfig,
@@ -66,15 +78,122 @@ if TYPE_CHECKING:
66
78
  StreamableHTTPMCPServerConfig,
67
79
  )
68
80
 
81
+ STOP_REASON_MAP: dict[StopReason, FinishReason] = {
82
+ "end_turn": "stop",
83
+ "max_tokens": "length",
84
+ "max_turn_requests": "length",
85
+ "refusal": "content_filter",
86
+ "cancelled": "error",
87
+ }
88
+
89
+
90
+ def get_modes(
91
+ config_options: list[SessionConfigOption],
92
+ available_modes: SessionModeState | None,
93
+ available_models: SessionModelState | None,
94
+ ) -> list[ModeCategory]:
95
+ from acp.schema import SessionConfigSelectGroup
96
+ from agentpool.agents.modes import ModeCategory, ModeInfo
97
+
98
+ categories: list[ModeCategory] = []
99
+
100
+ if config_options:
101
+ for config_opt in config_options:
102
+ # Extract options from the config (ungrouped or grouped)
103
+ mode_infos: list[ModeInfo] = []
104
+ if isinstance(config_opt.options, list):
105
+ for opt_item in config_opt.options:
106
+ if isinstance(opt_item, SessionConfigSelectGroup):
107
+ mode_infos.extend(
108
+ ModeInfo(
109
+ id=sub_opt.value,
110
+ name=sub_opt.name,
111
+ description=sub_opt.description or "",
112
+ category_id=config_opt.id,
113
+ )
114
+ for sub_opt in opt_item.options
115
+ )
116
+ else:
117
+ # Ungrouped options
118
+ mode_infos.append(
119
+ ModeInfo(
120
+ id=opt_item.value,
121
+ name=opt_item.name,
122
+ description=opt_item.description or "",
123
+ category_id=config_opt.id,
124
+ )
125
+ )
126
+
127
+ categories.append(
128
+ ModeCategory(
129
+ id=config_opt.id,
130
+ name=config_opt.name,
131
+ available_modes=mode_infos,
132
+ current_mode_id=config_opt.current_value,
133
+ category=config_opt.category or "other",
134
+ )
135
+ )
136
+ return categories
137
+
138
+ # Legacy: Convert ACP SessionModeState to ModeCategory
139
+ if available_modes:
140
+ acp_modes = available_modes
141
+ modes = [
142
+ ModeInfo(
143
+ id=m.id,
144
+ name=m.name,
145
+ description=m.description or "",
146
+ category_id="permissions",
147
+ )
148
+ for m in acp_modes.available_modes
149
+ ]
150
+ categories.append(
151
+ ModeCategory(
152
+ id="permissions",
153
+ name="Mode",
154
+ available_modes=modes,
155
+ current_mode_id=acp_modes.current_mode_id,
156
+ category="mode",
157
+ )
158
+ )
159
+
160
+ # Legacy: Convert ACP SessionModelState to ModeCategory
161
+ if available_models:
162
+ acp_models = available_models
163
+ models = [
164
+ ModeInfo(
165
+ id=m.model_id,
166
+ name=m.name,
167
+ description=m.description or "",
168
+ category_id="model",
169
+ )
170
+ for m in acp_models.available_models
171
+ ]
172
+ categories.append(
173
+ ModeCategory(
174
+ id="model",
175
+ name="Model",
176
+ available_modes=models,
177
+ current_mode_id=acp_models.current_model_id,
178
+ category="model",
179
+ )
180
+ )
181
+
182
+ return categories
183
+
184
+
185
+ def to_finish_reason(stop_reason: StopReason) -> FinishReason:
186
+ return STOP_REASON_MAP.get(stop_reason, "stop")
187
+
69
188
 
70
189
  def convert_acp_locations(
71
- locations: list[ToolCallLocation] | None,
190
+ locations: Sequence[ToolCallLocation] | None,
72
191
  ) -> list[LocationContentItem]:
73
192
  """Convert ACP ToolCallLocation list to native LocationContentItem list."""
74
193
  return [LocationContentItem(path=loc.path, line=loc.line) for loc in locations or []]
75
194
 
76
195
 
77
- def convert_acp_content(content: list[ToolCallContent] | None) -> list[ToolCallContentItem]:
196
+ def convert_acp_content(content: Sequence[ToolCallContent] | None) -> list[ToolCallContentItem]:
78
197
  """Convert ACP ToolCallContent list to native ToolCallContentItem list."""
79
198
  if not content:
80
199
  return []
@@ -217,7 +336,7 @@ def acp_to_native_event(update: SessionUpdate) -> RichAgentStreamEvent[Any] | No
217
336
  raw_input=raw_input or {},
218
337
  )
219
338
 
220
- # Tool call progress -> ToolCallProgressEvent
339
+ # Tool call progress -> ToolCallProgressEvent or ToolCallCompleteEvent
221
340
  case ToolCallProgress(
222
341
  tool_call_id=tool_call_id,
223
342
  status=status,
@@ -225,6 +344,20 @@ def acp_to_native_event(update: SessionUpdate) -> RichAgentStreamEvent[Any] | No
225
344
  content=content,
226
345
  raw_output=raw_output,
227
346
  ):
347
+ # If completed, return ToolCallCompleteEvent for metadata injection
348
+ if status == "completed":
349
+ from agentpool.agents.events import ToolCallCompleteEvent
350
+
351
+ return ToolCallCompleteEvent(
352
+ tool_call_id=tool_call_id,
353
+ tool_name=title or "unknown",
354
+ tool_input={}, # ACP doesn't provide input in progress updates
355
+ tool_result=str(raw_output) if raw_output else "",
356
+ agent_name="", # Will be set by agent
357
+ message_id="",
358
+ metadata=None, # Will be injected by agent from metadata accumulator
359
+ )
360
+ # Otherwise return progress event
228
361
  return ToolCallProgressEvent(
229
362
  tool_call_id=tool_call_id,
230
363
  status=status or "in_progress",
@@ -247,22 +380,22 @@ def acp_to_native_event(update: SessionUpdate) -> RichAgentStreamEvent[Any] | No
247
380
 
248
381
 
249
382
  @overload
250
- def mcp_config_to_acp(config: StdioMCPServerConfig) -> StdioMcpServer | None: ...
383
+ def mcp_config_to_acp(config: StdioMCPServerConfig) -> StdioMcpServer: ...
251
384
 
252
385
 
253
386
  @overload
254
- def mcp_config_to_acp(config: SSEMCPServerConfig) -> SseMcpServer | None: ...
387
+ def mcp_config_to_acp(config: SSEMCPServerConfig) -> SseMcpServer: ...
255
388
 
256
389
 
257
390
  @overload
258
- def mcp_config_to_acp(config: StreamableHTTPMCPServerConfig) -> HttpMcpServer | None: ...
391
+ def mcp_config_to_acp(config: StreamableHTTPMCPServerConfig) -> HttpMcpServer: ...
259
392
 
260
393
 
261
394
  @overload
262
- def mcp_config_to_acp(config: MCPServerConfig) -> McpServer | None: ...
395
+ def mcp_config_to_acp(config: MCPServerConfig) -> McpServer: ...
263
396
 
264
397
 
265
- def mcp_config_to_acp(config: MCPServerConfig) -> McpServer | None:
398
+ def mcp_config_to_acp(config: MCPServerConfig) -> McpServer:
266
399
  """Convert native MCPServerConfig to ACP McpServer format.
267
400
 
268
401
  If the config has tool filtering (enabled_tools or disabled_tools),
@@ -303,8 +436,8 @@ def mcp_config_to_acp(config: MCPServerConfig) -> McpServer | None:
303
436
  case StreamableHTTPMCPServerConfig(url=url):
304
437
  return HttpMcpServer(name=config.name or str(url), url=url, headers=[])
305
438
 
306
- case _:
307
- return None
439
+ case _ as unreachable:
440
+ assert_never(unreachable)
308
441
 
309
442
 
310
443
  def mcp_configs_to_acp(configs: Sequence[MCPServerConfig]) -> list[McpServer]:
@@ -316,4 +449,4 @@ def mcp_configs_to_acp(configs: Sequence[MCPServerConfig]) -> list[McpServer]:
316
449
  Returns:
317
450
  List of ACP-compatible McpServer instances (skips unconvertible configs)
318
451
  """
319
- return [converted for config in configs if (converted := mcp_config_to_acp(config)) is not None]
452
+ return [mcp_config_to_acp(config) for config in configs]
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  from pathlib import Path
7
7
  from typing import TYPE_CHECKING, Any
8
- import uuid
9
8
 
10
9
  import anyio
11
10
 
@@ -21,7 +20,6 @@ from acp.schema import (
21
20
  WriteTextFileResponse,
22
21
  )
23
22
  from agentpool.log import get_logger
24
- from agentpool.tools.base import Tool
25
23
 
26
24
 
27
25
  if TYPE_CHECKING:
@@ -79,7 +77,7 @@ class ACPClientHandler(Client):
79
77
  self.state = state
80
78
  self._input_provider = input_provider
81
79
  self._update_event = asyncio.Event()
82
- # Map ACP terminal IDs to process manager IDs
80
+ # Map ACP terminal IDs to process manager IDs (for local execution only)
83
81
  self._terminal_to_process: dict[str, str] = {}
84
82
  # Copy tool confirmation mode from agent (can be updated via set_tool_confirmation_mode)
85
83
  self.tool_confirmation_mode: ToolConfirmationMode = agent.tool_confirmation_mode
@@ -224,13 +222,21 @@ class ACPClientHandler(Client):
224
222
 
225
223
  if self._input_provider:
226
224
  ctx = self._agent.get_context() # Use the agent's NodeContext
227
- # Attach tool_call_id for permission event matching in TUI
225
+ # Attach tool call metadata for permission event matching
228
226
  ctx.tool_call_id = params.tool_call.tool_call_id
227
+ ctx.tool_name = params.tool_call.title
228
+ args = (
229
+ params.tool_call.raw_input if isinstance(params.tool_call.raw_input, dict) else {}
230
+ )
231
+ ctx.tool_input = args
229
232
  # Create a dummy tool representation from ACP params
230
- tool = Tool(callable=lambda: None, name=params.tool_call.tool_call_id, description=name)
231
- # Extract arguments - ACP doesn't expose them in ToolCall
233
+ from agentpool.tools import FunctionTool
234
+
235
+ tool = FunctionTool(
236
+ callable=lambda: None, name=params.tool_call.tool_call_id, description=name
237
+ )
232
238
  try:
233
- result = await self._input_provider.get_tool_confirmation(ctx, tool=tool, args={})
239
+ result = await self._input_provider.get_tool_confirmation(ctx, tool=tool, args=args)
234
240
  # Map confirmation result to ACP response
235
241
  if result == "allow":
236
242
  option_id = params.options[0].option_id if params.options else "allow"
@@ -265,9 +271,10 @@ class ACPClientHandler(Client):
265
271
  logger.debug("Read file", path=params.path, num_chars=len(content))
266
272
  return ReadTextFileResponse(content=content)
267
273
 
268
- except FileNotFoundError:
269
- logger.exception("File not found", path=params.path)
270
- raise
274
+ except (FileNotFoundError, KeyError):
275
+ # Match Zed behavior: return empty string for non-existent files
276
+ logger.debug("File not found, returning empty string", path=params.path)
277
+ return ReadTextFileResponse(content="")
271
278
  except Exception:
272
279
  logger.exception("Failed to read file", path=params.path)
273
280
  raise
@@ -290,11 +297,18 @@ class ACPClientHandler(Client):
290
297
  raise
291
298
 
292
299
  async def create_terminal(self, params: CreateTerminalRequest) -> CreateTerminalResponse:
293
- """Create a new terminal session via ProcessManager."""
300
+ """Create a new terminal session via the configured ExecutionEnvironment.
301
+
302
+ The ProcessManager implementation determines where the terminal runs
303
+ (local, Docker, E2B, SSH, or forwarded to parent ACP client like Zed).
304
+
305
+ The terminal_id returned by process_manager.start_process() is used directly.
306
+ """
294
307
  if not self.allow_terminal:
295
308
  raise RuntimeError("Terminal operations not allowed")
309
+
296
310
  try:
297
- process_id = await self.env.process_manager.start_process(
311
+ terminal_id = await self.env.process_manager.start_process(
298
312
  command=params.command,
299
313
  args=list(params.args) if params.args else None,
300
314
  cwd=params.cwd,
@@ -304,10 +318,10 @@ class ACPClientHandler(Client):
304
318
  logger.exception("Failed to create terminal", command=params.command)
305
319
  raise
306
320
  else:
307
- terminal_id = f"term_{uuid.uuid4().hex[:8]}"
308
- self._terminal_to_process[terminal_id] = process_id
309
- msg = "Created terminal"
310
- logger.info(msg, terminal_id=terminal_id, process_id=process_id, cmd=params.command)
321
+ # Use the ID from process_manager directly - for ACPProcessManager this
322
+ # is already the parent's terminal ID (e.g., Zed's)
323
+ self._terminal_to_process[terminal_id] = terminal_id
324
+ logger.info("Created terminal", terminal_id=terminal_id, command=params.command)
311
325
  return CreateTerminalResponse(terminal_id=terminal_id)
312
326
 
313
327
  async def terminal_output(self, params: TerminalOutputRequest) -> TerminalOutputResponse:
@@ -319,8 +333,7 @@ class ACPClientHandler(Client):
319
333
  if terminal_id not in self._terminal_to_process:
320
334
  raise ValueError(f"Terminal {terminal_id} not found")
321
335
 
322
- process_id = self._terminal_to_process[terminal_id]
323
- proc_output = await self.env.process_manager.get_output(process_id)
336
+ proc_output = await self.env.process_manager.get_output(terminal_id)
324
337
  output = proc_output.combined or proc_output.stdout or ""
325
338
  return TerminalOutputResponse(output=output, truncated=proc_output.truncated)
326
339
 
@@ -335,8 +348,7 @@ class ACPClientHandler(Client):
335
348
  if terminal_id not in self._terminal_to_process:
336
349
  raise ValueError(f"Terminal {terminal_id} not found")
337
350
 
338
- process_id = self._terminal_to_process[terminal_id]
339
- exit_code = await self.env.process_manager.wait_for_exit(process_id)
351
+ exit_code = await self.env.process_manager.wait_for_exit(terminal_id)
340
352
  logger.debug("Terminal exited", terminal_id=terminal_id, exit_code=exit_code)
341
353
  return WaitForTerminalExitResponse(exit_code=exit_code)
342
354
 
@@ -351,8 +363,7 @@ class ACPClientHandler(Client):
351
363
  if terminal_id not in self._terminal_to_process:
352
364
  raise ValueError(f"Terminal {terminal_id} not found")
353
365
 
354
- process_id = self._terminal_to_process[terminal_id]
355
- await self.env.process_manager.kill_process(process_id)
366
+ await self.env.process_manager.kill_process(terminal_id)
356
367
  logger.info("Killed terminal", terminal_id=terminal_id)
357
368
  return KillTerminalCommandResponse()
358
369
 
@@ -364,8 +375,8 @@ class ACPClientHandler(Client):
364
375
  terminal_id = params.terminal_id
365
376
  if terminal_id not in self._terminal_to_process:
366
377
  raise ValueError(f"Terminal {terminal_id} not found")
367
- process_id = self._terminal_to_process[terminal_id]
368
- await self.env.process_manager.release_process(process_id)
378
+
379
+ await self.env.process_manager.release_process(terminal_id)
369
380
  del self._terminal_to_process[terminal_id]
370
381
  logger.info("Released terminal", terminal_id=terminal_id)
371
382
  return ReleaseTerminalResponse()
@@ -9,7 +9,12 @@ from agentpool.log import get_logger
9
9
 
10
10
 
11
11
  if TYPE_CHECKING:
12
- from acp.schema import AvailableCommandsUpdate, SessionModelState, SessionModeState
12
+ from acp.schema import (
13
+ AvailableCommandsUpdate,
14
+ SessionConfigOption,
15
+ SessionModelState,
16
+ SessionModeState,
17
+ )
13
18
  from agentpool.agents.events import RichAgentStreamEvent
14
19
 
15
20
  logger = get_logger(__name__)
@@ -28,20 +33,23 @@ class ACPSessionState:
28
33
  """Queue of native events converted from ACP updates."""
29
34
 
30
35
  current_model_id: str | None = None
31
- """Current model ID from session state."""
36
+ """Current model ID from session state (legacy)."""
32
37
 
33
38
  models: SessionModelState | None = None
34
- """Full model state including available models from nested ACP agent."""
39
+ """Full model state including available models (legacy)."""
35
40
 
36
41
  modes: SessionModeState | None = None
37
- """Full mode state including available modes from nested ACP agent."""
42
+ """Full mode state including available modes (legacy)."""
38
43
 
39
44
  current_mode_id: str | None = None
40
- """Current mode ID."""
45
+ """Current mode ID (legacy)."""
46
+
47
+ config_options: list[SessionConfigOption] = dataclass_field(default_factory=list)
48
+ """Unified session config options (replaces modes/models in newer ACP versions)."""
41
49
 
42
50
  available_commands: AvailableCommandsUpdate | None = None
43
51
  """Available commands from the agent."""
44
52
 
45
53
  def clear(self) -> None:
46
54
  self.events.clear()
47
- # Note: Don't clear current_model_id or models - those persist across prompts
55
+ # Note: Don't clear current_model_id, models, config_options - those persist