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
@@ -16,10 +16,13 @@ from acp.schema import (
16
16
  NewSessionResponse,
17
17
  PromptResponse,
18
18
  ResumeSessionResponse,
19
+ SessionConfigOption,
20
+ SessionConfigSelectOption,
19
21
  SessionInfo,
20
22
  SessionMode,
21
23
  SessionModelState,
22
24
  SessionModeState,
25
+ SetSessionConfigOptionResponse,
23
26
  SetSessionModelRequest,
24
27
  SetSessionModelResponse,
25
28
  SetSessionModeRequest,
@@ -27,17 +30,13 @@ from acp.schema import (
27
30
  )
28
31
  from agentpool.log import get_logger
29
32
  from agentpool.utils.tasks import TaskManager
30
- from agentpool_server.acp_server.converters import (
31
- # agent_to_mode, # TODO: Re-enable when supporting agent switching via modes
32
- mode_id_to_confirmation_mode,
33
- )
34
33
  from agentpool_server.acp_server.session_manager import ACPSessionManager
35
34
 
36
35
 
37
36
  if TYPE_CHECKING:
38
37
  from pydantic_ai import ModelRequest, ModelResponse
39
38
 
40
- from acp import AgentSideConnection, Client
39
+ from acp import Client
41
40
  from acp.schema import (
42
41
  AuthenticateRequest,
43
42
  CancelNotification,
@@ -50,6 +49,7 @@ if TYPE_CHECKING:
50
49
  NewSessionRequest,
51
50
  PromptRequest,
52
51
  ResumeSessionRequest,
52
+ SetSessionConfigOptionRequest,
53
53
  SetSessionModelRequest,
54
54
  SetSessionModeRequest,
55
55
  )
@@ -93,14 +93,8 @@ async def get_session_model_state(
93
93
  for toko in toko_models:
94
94
  # Use id_override if set (e.g., "opus" for Claude Code), otherwise use id
95
95
  model_id = toko.id_override if toko.id_override else toko.id
96
- acp_models.append(
97
- ACPModelInfo(
98
- model_id=model_id,
99
- name=toko.name,
100
- description=toko.description or "",
101
- )
102
- )
103
-
96
+ info = ACPModelInfo(model_id=model_id, name=toko.name, description=toko.description or "")
97
+ acp_models.append(info)
104
98
  if not acp_models:
105
99
  return None
106
100
 
@@ -108,14 +102,9 @@ async def get_session_model_state(
108
102
  all_ids = [model.model_id for model in acp_models]
109
103
  if current_model and current_model not in all_ids:
110
104
  # Add current model to the list so the UI shows it
111
- acp_models.insert(
112
- 0,
113
- ACPModelInfo(
114
- model_id=current_model,
115
- name=current_model,
116
- description="Currently configured model",
117
- ),
118
- )
105
+ desc = "Currently configured model"
106
+ model_info = ACPModelInfo(model_id=current_model, name=current_model, description=desc)
107
+ acp_models.insert(0, model_info)
119
108
  current_model_id = current_model
120
109
  else:
121
110
  current_model_id = current_model if current_model in all_ids else all_ids[0]
@@ -123,11 +112,11 @@ async def get_session_model_state(
123
112
  return SessionModelState(available_models=acp_models, current_model_id=current_model_id)
124
113
 
125
114
 
126
- def get_session_mode_state(agent: Any) -> SessionModeState | None:
115
+ async def get_session_mode_state(agent: Any) -> SessionModeState | None:
127
116
  """Get SessionModeState from an agent using its get_modes() method.
128
117
 
129
118
  Converts agentpool ModeCategory to ACP SessionModeState format.
130
- Currently uses the first mode category (ACP only supports one dropdown for now).
119
+ Uses the first category that looks like permissions (not model).
131
120
 
132
121
  Args:
133
122
  agent: Any agent with get_modes() method
@@ -141,7 +130,7 @@ def get_session_mode_state(agent: Any) -> SessionModeState | None:
141
130
  return None
142
131
 
143
132
  try:
144
- mode_categories = agent.get_modes()
133
+ mode_categories = await agent.get_modes()
145
134
  except Exception:
146
135
  logger.exception("Failed to get modes from agent")
147
136
  return None
@@ -149,8 +138,10 @@ def get_session_mode_state(agent: Any) -> SessionModeState | None:
149
138
  if not mode_categories:
150
139
  return None
151
140
 
152
- # Use first category for now (ACP currently only supports single mode dropdown)
153
- category = mode_categories[0]
141
+ # Find the permissions category (not model)
142
+ category = next((c for c in mode_categories if c.id != "model"), None)
143
+ if not category:
144
+ return None
154
145
 
155
146
  # Convert ModeInfo to ACP SessionMode
156
147
  acp_modes = [
@@ -168,6 +159,56 @@ def get_session_mode_state(agent: Any) -> SessionModeState | None:
168
159
  )
169
160
 
170
161
 
162
+ async def get_session_config_options(agent: Any) -> list[SessionConfigOption]:
163
+ """Get SessionConfigOptions from an agent using its get_modes() method.
164
+
165
+ Converts all agentpool ModeCategories to ACP SessionConfigOption format.
166
+
167
+ Args:
168
+ agent: Any agent with get_modes() method
169
+
170
+ Returns:
171
+ List of SessionConfigOption from agent's mode categories
172
+ """
173
+ from agentpool.agents.base_agent import BaseAgent
174
+
175
+ if not isinstance(agent, BaseAgent):
176
+ return []
177
+
178
+ try:
179
+ mode_categories = await agent.get_modes()
180
+ except Exception:
181
+ logger.exception("Failed to get modes from agent")
182
+ return []
183
+
184
+ if not mode_categories:
185
+ return []
186
+
187
+ # Convert each ModeCategory to a SessionConfigOption
188
+ config_options: list[SessionConfigOption] = []
189
+ for category in mode_categories:
190
+ options = [
191
+ SessionConfigSelectOption(
192
+ value=mode.id,
193
+ name=mode.name,
194
+ description=mode.description,
195
+ )
196
+ for mode in category.available_modes
197
+ ]
198
+ config_options.append(
199
+ SessionConfigOption(
200
+ id=category.id,
201
+ name=category.name,
202
+ description=None,
203
+ category=category.category,
204
+ current_value=category.current_mode_id,
205
+ options=options,
206
+ )
207
+ )
208
+
209
+ return config_options
210
+
211
+
171
212
  @dataclass
172
213
  class AgentPoolACPAgent(ACPAgent):
173
214
  """Implementation of ACP Agent protocol interface for AgentPool.
@@ -178,7 +219,7 @@ class AgentPoolACPAgent(ACPAgent):
178
219
 
179
220
  PROTOCOL_VERSION: ClassVar = 1
180
221
 
181
- connection: AgentSideConnection
222
+ client: Client
182
223
  """ACP connection for client communication."""
183
224
 
184
225
  agent_pool: AgentPool[Any]
@@ -206,7 +247,6 @@ class AgentPoolACPAgent(ACPAgent):
206
247
 
207
248
  def __post_init__(self) -> None:
208
249
  """Initialize derived attributes and setup after field assignment."""
209
- self.client: Client = self.connection
210
250
  self.client_capabilities: ClientCapabilities | None = None
211
251
  self.client_info: Implementation | None = None
212
252
  self.session_manager = ACPSessionManager(pool=self.agent_pool)
@@ -274,6 +314,7 @@ class AgentPoolACPAgent(ACPAgent):
274
314
 
275
315
  state: SessionModeState | None = None
276
316
  models: SessionModelState | None = None
317
+ config_options: list[SessionConfigOption] = []
277
318
 
278
319
  if session := self.session_manager.get_session(session_id):
279
320
  if isinstance(session.agent, ACPAgentClient):
@@ -281,10 +322,13 @@ class AgentPoolACPAgent(ACPAgent):
281
322
  if session.agent._state:
282
323
  models = session.agent._state.models
283
324
  state = session.agent._state.modes
325
+ # Also get config_options from nested agent
326
+ config_options = await get_session_config_options(session.agent)
284
327
  else:
285
328
  # Use unified helpers for all other agents
286
329
  models = await get_session_model_state(session.agent, session.agent.model_name)
287
- state = get_session_mode_state(session.agent)
330
+ state = await get_session_mode_state(session.agent)
331
+ config_options = await get_session_config_options(session.agent)
288
332
  except Exception:
289
333
  logger.exception("Failed to create new session")
290
334
  raise
@@ -299,7 +343,13 @@ class AgentPoolACPAgent(ACPAgent):
299
343
  coro_4 = session.init_client_skills()
300
344
  self.tasks.create_task(coro_4, name=f"init_client_skills_{session_id}")
301
345
  logger.info("Created session", session_id=session_id)
302
- return NewSessionResponse(session_id=session_id, modes=state, models=models)
346
+
347
+ return NewSessionResponse(
348
+ session_id=session_id,
349
+ modes=state,
350
+ models=models,
351
+ config_options=config_options if config_options else None,
352
+ )
303
353
 
304
354
  async def load_session(self, params: LoadSessionRequest) -> LoadSessionResponse:
305
355
  """Load an existing session from storage.
@@ -311,13 +361,14 @@ class AgentPoolACPAgent(ACPAgent):
311
361
  4. Replay conversation history via ACP notifications
312
362
  5. Return session state (modes, models)
313
363
  """
364
+ from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
365
+
314
366
  if not self._initialized:
315
367
  raise RuntimeError("Agent not initialized")
316
368
 
317
369
  try:
318
370
  # First check if session is already active
319
371
  session = self.session_manager.get_session(params.session_id)
320
-
321
372
  if not session:
322
373
  # Try to resume from storage
323
374
  msg = "Attempting to resume session from storage"
@@ -344,21 +395,21 @@ class AgentPoolACPAgent(ACPAgent):
344
395
  session.mcp_servers = params.mcp_servers
345
396
  await session.initialize_mcp_servers()
346
397
 
347
- # Build response with current session state
348
- from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
349
-
350
398
  mode_state: SessionModeState | None = None
351
399
  models: SessionModelState | None = None
400
+ config_opts: list[SessionConfigOption] = []
352
401
 
353
402
  if isinstance(session.agent, ACPAgentClient):
354
403
  # Nested ACP agent - pass through its state directly
355
404
  if session.agent._state:
356
405
  mode_state = session.agent._state.modes
357
406
  models = session.agent._state.models
407
+ config_opts = await get_session_config_options(session.agent)
358
408
  elif session.agent:
359
409
  # Use unified helpers for all other agents
360
- mode_state = get_session_mode_state(session.agent)
410
+ mode_state = await get_session_mode_state(session.agent)
361
411
  models = await get_session_model_state(session.agent, session.agent.model_name)
412
+ config_opts = await get_session_config_options(session.agent)
362
413
  else:
363
414
  models = None
364
415
  # Schedule post-load initialization tasks
@@ -367,7 +418,7 @@ class AgentPoolACPAgent(ACPAgent):
367
418
  # Replay conversation history via ACP notifications
368
419
  self.tasks.create_task(self._replay_conversation_history(session))
369
420
  logger.info("Session loaded successfully", agent=session.current_agent_name)
370
- return LoadSessionResponse(models=models, modes=mode_state)
421
+ return LoadSessionResponse(models=models, modes=mode_state, config_options=config_opts)
371
422
 
372
423
  except Exception:
373
424
  logger.exception("Failed to load session", session_id=params.session_id)
@@ -626,14 +677,8 @@ class AgentPoolACPAgent(ACPAgent):
626
677
  ) -> SetSessionModeResponse | None:
627
678
  """Set the session mode (change tool confirmation level).
628
679
 
629
- Maps ACP mode IDs to ToolConfirmationMode and calls set_tool_confirmation_mode
630
- on the session's agent. Each agent type handles the mode change appropriately:
631
- - Agent/AGUIAgent: Updates local confirmation mode
632
- - ACPAgent: Updates local mode AND forwards to remote ACP server
633
-
634
- Mode mappings:
635
- - "default": per_tool (confirm tools marked as requiring it)
636
- - "acceptEdits": never (auto-approve all tool calls)
680
+ Calls set_mode directly on the agent with the mode_id, allowing the agent
681
+ to handle mode-specific logic (e.g., acceptEdits auto-allowing edit tools).
637
682
  """
638
683
  from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
639
684
 
@@ -643,15 +688,8 @@ class AgentPoolACPAgent(ACPAgent):
643
688
  logger.warning("Session not found for mode switch", session_id=params.session_id)
644
689
  return None
645
690
 
646
- # Map mode_id to confirmation mode
647
- confirmation_mode = mode_id_to_confirmation_mode(params.mode_id)
648
- if not confirmation_mode:
649
- logger.error("Invalid mode_id", mode_id=params.mode_id)
650
- return None
651
-
652
- # All agent types support set_tool_confirmation_mode
653
- # ACPAgent handles forwarding to remote server internally
654
- await session.agent.set_tool_confirmation_mode(confirmation_mode)
691
+ # Call set_mode directly - agent handles mode-specific logic
692
+ await session.agent.set_mode(params.mode_id)
655
693
 
656
694
  # Update stored mode state for ACPAgent
657
695
  if (
@@ -662,9 +700,8 @@ class AgentPoolACPAgent(ACPAgent):
662
700
  session.agent._state.modes.current_mode_id = params.mode_id
663
701
 
664
702
  logger.info(
665
- "Set tool confirmation mode",
703
+ "Set mode",
666
704
  mode_id=params.mode_id,
667
- confirmation_mode=confirmation_mode,
668
705
  session_id=params.session_id,
669
706
  agent_type=type(session.agent).__name__,
670
707
  )
@@ -726,13 +763,45 @@ class AgentPoolACPAgent(ACPAgent):
726
763
 
727
764
  # Set the model on the agent (all agents now have async set_model)
728
765
  await session.agent.set_model(params.model_id)
729
-
730
766
  logger.info("Set model", model_id=params.model_id, session_id=params.session_id)
731
767
  return SetSessionModelResponse()
732
768
  except Exception:
733
769
  logger.exception("Failed to set session model", session_id=params.session_id)
734
770
  return None
735
771
 
772
+ async def set_session_config_option(
773
+ self, params: SetSessionConfigOptionRequest
774
+ ) -> SetSessionConfigOptionResponse | None:
775
+ """Set a session config option.
776
+
777
+ Forwards the config option change to the agent's set_mode method
778
+ and returns the updated config options.
779
+ """
780
+ try:
781
+ session = self.session_manager.get_session(params.session_id)
782
+ if not session or not session.agent:
783
+ msg = "Session not found for config option change"
784
+ logger.warning(msg, session_id=params.session_id)
785
+ return None
786
+
787
+ logger.info(
788
+ "Set config option",
789
+ config_id=params.config_id,
790
+ value=params.value,
791
+ session_id=params.session_id,
792
+ )
793
+
794
+ # Forward to agent's set_mode method
795
+ # config_id maps to category_id, value maps to mode_id
796
+ await session.agent.set_mode(params.value, category_id=params.config_id)
797
+
798
+ # Return updated config options
799
+ config_options = await get_session_config_options(session.agent)
800
+ return SetSessionConfigOptionResponse(config_options=config_options)
801
+ except Exception:
802
+ logger.exception("Failed to set session config option", session_id=params.session_id)
803
+ return None
804
+
736
805
  async def swap_pool(self, config_path: str, agent: str | None = None) -> list[str]:
737
806
  """Swap the agent pool with a new one from configuration.
738
807