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
@@ -17,6 +17,7 @@ from acp.schema import (
17
17
  PromptResponse,
18
18
  ResumeSessionResponse,
19
19
  SessionInfo,
20
+ SessionMode,
20
21
  SessionModelState,
21
22
  SessionModeState,
22
23
  SetSessionModelRequest,
@@ -24,20 +25,16 @@ from acp.schema import (
24
25
  SetSessionModeRequest,
25
26
  SetSessionModeResponse,
26
27
  )
27
- from agentpool import Agent
28
28
  from agentpool.log import get_logger
29
29
  from agentpool.utils.tasks import TaskManager
30
30
  from agentpool_server.acp_server.converters import (
31
31
  # agent_to_mode, # TODO: Re-enable when supporting agent switching via modes
32
- get_confirmation_modes,
33
32
  mode_id_to_confirmation_mode,
34
33
  )
35
34
  from agentpool_server.acp_server.session_manager import ACPSessionManager
36
35
 
37
36
 
38
37
  if TYPE_CHECKING:
39
- from collections.abc import Sequence
40
-
41
38
  from pydantic_ai import ModelRequest, ModelResponse
42
39
 
43
40
  from acp import AgentSideConnection, Client
@@ -63,59 +60,111 @@ if TYPE_CHECKING:
63
60
  logger = get_logger(__name__)
64
61
 
65
62
 
66
- # Claude Code model definitions - simple IDs that the SDK understands
67
- CLAUDE_CODE_MODELS: list[ACPModelInfo] = [
68
- ACPModelInfo(
69
- model_id="opus",
70
- name="Claude Opus",
71
- description="Claude Opus - most capable model",
72
- ),
73
- ACPModelInfo(
74
- model_id="sonnet",
75
- name="Claude Sonnet",
76
- description="Claude Sonnet - balanced performance and speed",
77
- ),
78
- ACPModelInfo(
79
- model_id="haiku",
80
- name="Claude Haiku",
81
- description="Claude Haiku - fast and cost-effective",
82
- ),
83
- ]
84
-
85
-
86
- def create_claude_code_model_state(current_model: str | None = None) -> SessionModelState:
87
- """Create SessionModelState for Claude Code agents.
63
+ async def get_session_model_state(
64
+ agent: Any, current_model: str | None = None
65
+ ) -> SessionModelState | None:
66
+ """Get SessionModelState from an agent using its get_available_models() method.
67
+
68
+ Converts tokonomics ModelInfo to ACP ModelInfo format.
88
69
 
89
70
  Args:
90
- current_model: Currently active model ID (e.g., "sonnet")
71
+ agent: Any agent with get_available_models() method
72
+ current_model: Currently active model ID (defaults to first available)
91
73
 
92
74
  Returns:
93
- SessionModelState with Claude Code models
75
+ SessionModelState with all available models, None if no models available
94
76
  """
95
- model_ids = [m.model_id for m in CLAUDE_CODE_MODELS]
96
- current = current_model if current_model in model_ids else "sonnet"
97
- return SessionModelState(available_models=CLAUDE_CODE_MODELS, current_model_id=current)
77
+ from agentpool.agents.base_agent import BaseAgent
98
78
 
79
+ if not isinstance(agent, BaseAgent):
80
+ return None
99
81
 
100
- def create_session_model_state(
101
- available_models: Sequence[ACPModelInfo], current_model: str | None = None
102
- ) -> SessionModelState | None:
103
- """Create a SessionModelState from available ACP models.
82
+ try:
83
+ toko_models = await agent.get_available_models()
84
+ except Exception:
85
+ logger.exception("Failed to get available models from agent")
86
+ return None
87
+
88
+ if not toko_models:
89
+ return None
90
+
91
+ # Convert tokonomics ModelInfo to ACP ModelInfo
92
+ acp_models: list[ACPModelInfo] = []
93
+ for toko in toko_models:
94
+ # Use id_override if set (e.g., "opus" for Claude Code), otherwise use id
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
+
104
+ if not acp_models:
105
+ return None
106
+
107
+ # Ensure current model is in the list
108
+ all_ids = [model.model_id for model in acp_models]
109
+ if current_model and current_model not in all_ids:
110
+ # 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
+ )
119
+ current_model_id = current_model
120
+ else:
121
+ current_model_id = current_model if current_model in all_ids else all_ids[0]
122
+
123
+ return SessionModelState(available_models=acp_models, current_model_id=current_model_id)
124
+
125
+
126
+ def get_session_mode_state(agent: Any) -> SessionModeState | None:
127
+ """Get SessionModeState from an agent using its get_modes() method.
128
+
129
+ Converts agentpool ModeCategory to ACP SessionModeState format.
130
+ Currently uses the first mode category (ACP only supports one dropdown for now).
104
131
 
105
132
  Args:
106
- available_models: List of ACP ModelInfo objects (already converted from tokonomics)
107
- current_model: The currently active model (defaults to first available)
133
+ agent: Any agent with get_modes() method
108
134
 
109
135
  Returns:
110
- SessionModelState with all available models, None if no models provided
136
+ SessionModeState from agent's modes, None if no modes available
111
137
  """
112
- if not available_models:
138
+ from agentpool.agents.base_agent import BaseAgent
139
+
140
+ if not isinstance(agent, BaseAgent):
141
+ return None
142
+
143
+ try:
144
+ mode_categories = agent.get_modes()
145
+ except Exception:
146
+ logger.exception("Failed to get modes from agent")
147
+ return None
148
+
149
+ if not mode_categories:
113
150
  return None
114
- # Use first model as current if not specified or not found
115
- all_ids = [model.model_id for model in available_models]
116
- current_model_id = current_model if current_model in all_ids else all_ids[0]
117
- return SessionModelState(
118
- available_models=list(available_models), current_model_id=current_model_id
151
+
152
+ # Use first category for now (ACP currently only supports single mode dropdown)
153
+ category = mode_categories[0]
154
+
155
+ # Convert ModeInfo to ACP SessionMode
156
+ acp_modes = [
157
+ SessionMode(
158
+ id=mode.id,
159
+ name=mode.name,
160
+ description=mode.description,
161
+ )
162
+ for mode in category.available_modes
163
+ ]
164
+
165
+ return SessionModeState(
166
+ available_modes=acp_modes,
167
+ current_mode_id=category.current_mode_id,
119
168
  )
120
169
 
121
170
 
@@ -137,9 +186,6 @@ class AgentPoolACPAgent(ACPAgent):
137
186
 
138
187
  _: KW_ONLY
139
188
 
140
- available_models: Sequence[ACPModelInfo] = field(default_factory=list)
141
- """List of available ACP ModelInfo objects (converted from tokonomics at startup)."""
142
-
143
189
  file_access: bool = True
144
190
  """Whether agent can access filesystem."""
145
191
 
@@ -176,17 +222,12 @@ class AgentPoolACPAgent(ACPAgent):
176
222
 
177
223
  async def initialize(self, params: InitializeRequest) -> InitializeResponse:
178
224
  """Initialize the agent and negotiate capabilities."""
179
- logger.info("Initializing ACP agent implementation")
180
225
  version = min(params.protocol_version, self.PROTOCOL_VERSION)
181
226
  self.client_capabilities = params.client_capabilities
182
227
  self.client_info = params.client_info
183
- logger.info(
184
- "Client capabilities",
185
- capabilities=self.client_capabilities,
186
- client_info=self.client_info,
187
- )
228
+ logger.info("Client info", request=params.model_dump_json())
188
229
  self._initialized = True
189
- response = InitializeResponse.create(
230
+ return InitializeResponse.create(
190
231
  protocol_version=version,
191
232
  name="agentpool",
192
233
  title="AgentPool",
@@ -199,10 +240,8 @@ class AgentPoolACPAgent(ACPAgent):
199
240
  embedded_context_prompts=True,
200
241
  image_prompts=True,
201
242
  )
202
- logger.info("ACP agent initialized successfully", response=response)
203
- return response
204
243
 
205
- async def new_session(self, params: NewSessionRequest) -> NewSessionResponse: # noqa: PLR0915
244
+ async def new_session(self, params: NewSessionRequest) -> NewSessionResponse:
206
245
  """Create a new session."""
207
246
  if not self._initialized:
208
247
  raise RuntimeError("Agent not initialized")
@@ -230,48 +269,22 @@ class AgentPoolACPAgent(ACPAgent):
230
269
  client_info=self.client_info,
231
270
  )
232
271
 
233
- # Get mode information - pass through from ACPAgent or use our confirmation modes
272
+ # Get mode and model information from the agent
234
273
  from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
235
274
 
236
- if session := self.session_manager.get_session(session_id):
237
- if isinstance(session.agent, ACPAgentClient):
238
- # Pass through nested agent's modes (e.g., Claude Code's modes)
239
- if session.agent._state and session.agent._state.modes:
240
- state = session.agent._state.modes
241
- modes = state.available_modes
242
- else:
243
- # Fallback to our confirmation modes if nested agent has none
244
- modes = get_confirmation_modes()
245
- state = SessionModeState(current_mode_id="default", available_modes=modes)
246
- else:
247
- # Native Agent - use our tool confirmation modes
248
- modes = get_confirmation_modes()
249
- state = SessionModeState(current_mode_id="default", available_modes=modes)
250
- else:
251
- modes = get_confirmation_modes()
252
- state = SessionModeState(current_mode_id="default", available_modes=modes)
253
- # TODO: Re-enable agent switching via modes when needed:
254
- # modes = [agent_to_mode(agent) for agent in self.agent_pool.all_agents.values()]
255
- # state = SessionModeState(current_mode_id=default_name, available_modes=modes)
275
+ state: SessionModeState | None = None
276
+ models: SessionModelState | None = None
256
277
 
257
- # Get model information from the default agent
258
278
  if session := self.session_manager.get_session(session_id):
259
- from agentpool.agents.claude_code_agent import ClaudeCodeAgent
260
-
261
- if isinstance(session.agent, ClaudeCodeAgent):
262
- # Claude Code uses simple model IDs (sonnet, opus, haiku)
263
- models = create_claude_code_model_state(session.agent.model_name)
264
- elif isinstance(session.agent, ACPAgentClient):
265
- # Nested ACP agent - pass through its model state directly
266
- if session.agent._state and session.agent._state.models:
279
+ if isinstance(session.agent, ACPAgentClient):
280
+ # Nested ACP agent - pass through its state directly
281
+ if session.agent._state:
267
282
  models = session.agent._state.models
268
- else:
269
- models = None
283
+ state = session.agent._state.modes
270
284
  else:
271
- current_model = session.agent.model_name
272
- models = create_session_model_state(self.available_models, current_model)
273
- else:
274
- models = None
285
+ # Use unified helpers for all other agents
286
+ models = await get_session_model_state(session.agent, session.agent.model_name)
287
+ state = get_session_mode_state(session.agent)
275
288
  except Exception:
276
289
  logger.exception("Failed to create new session")
277
290
  raise
@@ -285,7 +298,7 @@ class AgentPoolACPAgent(ACPAgent):
285
298
  if self.load_skills:
286
299
  coro_4 = session.init_client_skills()
287
300
  self.tasks.create_task(coro_4, name=f"init_client_skills_{session_id}")
288
- logger.info("Created session", session_id=session_id, agent_count=len(modes))
301
+ logger.info("Created session", session_id=session_id)
289
302
  return NewSessionResponse(session_id=session_id, modes=state, models=models)
290
303
 
291
304
  async def load_session(self, params: LoadSessionRequest) -> LoadSessionResponse:
@@ -332,38 +345,22 @@ class AgentPoolACPAgent(ACPAgent):
332
345
  await session.initialize_mcp_servers()
333
346
 
334
347
  # Build response with current session state
335
- # Get mode information - pass through from ACPAgent or use our confirmation modes
336
348
  from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
337
349
 
350
+ mode_state: SessionModeState | None = None
351
+ models: SessionModelState | None = None
352
+
338
353
  if isinstance(session.agent, ACPAgentClient):
339
- # Pass through nested agent's modes (e.g., Claude Code's modes)
340
- if session.agent._state and session.agent._state.modes:
354
+ # Nested ACP agent - pass through its state directly
355
+ if session.agent._state:
341
356
  mode_state = session.agent._state.modes
342
- else:
343
- # Fallback to our confirmation modes if nested agent has none
344
- modes = get_confirmation_modes()
345
- mode_state = SessionModeState(current_mode_id="default", available_modes=modes)
357
+ models = session.agent._state.models
358
+ elif session.agent:
359
+ # Use unified helpers for all other agents
360
+ mode_state = get_session_mode_state(session.agent)
361
+ models = await get_session_model_state(session.agent, session.agent.model_name)
346
362
  else:
347
- # Native Agent - use our tool confirmation modes
348
- modes = get_confirmation_modes()
349
- mode_state = SessionModeState(current_mode_id="default", available_modes=modes)
350
- # TODO: Re-enable agent switching via modes when needed:
351
- # modes = [agent_to_mode(agent) for agent in self.agent_pool.all_agents.values()]
352
- # mode_state = SessionModeState(
353
- # current_mode_id=session.current_agent_name,
354
- # available_modes=modes,
355
- # )
356
-
357
- # Get model information based on agent type
358
- from agentpool.agents.claude_code_agent import ClaudeCodeAgent
359
-
360
- models: SessionModelState | None
361
- if session.agent and isinstance(session.agent, ClaudeCodeAgent):
362
- # Claude Code uses simple model IDs (sonnet, opus, haiku)
363
- models = create_claude_code_model_state(session.agent.model_name)
364
- else:
365
- current_model = session.agent.model_name if session.agent else None
366
- models = create_session_model_state(self.available_models, current_model)
363
+ models = None
367
364
  # Schedule post-load initialization tasks
368
365
  self.tasks.create_task(session.send_available_commands_update())
369
366
  self.tasks.create_task(session.init_project_context())
@@ -677,19 +674,15 @@ class AgentPoolACPAgent(ACPAgent):
677
674
  logger.exception("Failed to set session mode", session_id=params.session_id)
678
675
  return None
679
676
 
680
- async def set_session_model( # noqa: PLR0911
677
+ async def set_session_model(
681
678
  self, params: SetSessionModelRequest
682
679
  ) -> SetSessionModelResponse | None:
683
680
  """Set the session model.
684
681
 
685
682
  Changes the model for the active agent in the session.
686
- Validates that the requested model is in the available models list:
687
- - For ClaudeCodeAgent: validates against Claude Code models (sonnet, opus, haiku)
688
- - For native Agent: validates against server's available_models
689
- - For ACPAgent: validates against the nested agent's model list
683
+ Validates that the requested model is available via agent.get_available_models().
690
684
  """
691
685
  from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
692
- from agentpool.agents.claude_code_agent import ClaudeCodeAgent
693
686
 
694
687
  try:
695
688
  session = self.session_manager.get_session(params.session_id)
@@ -698,23 +691,8 @@ class AgentPoolACPAgent(ACPAgent):
698
691
  logger.warning(msg, session_id=params.session_id)
699
692
  return None
700
693
 
701
- # Validate model based on agent type
702
- if isinstance(session.agent, ClaudeCodeAgent):
703
- # For ClaudeCodeAgent, validate against our hardcoded models
704
- available_ids = [m.model_id for m in CLAUDE_CODE_MODELS]
705
- if params.model_id not in available_ids:
706
- logger.warning(
707
- "Model not in Claude Code available models",
708
- model_id=params.model_id,
709
- available=available_ids,
710
- )
711
- return None
712
- await session.agent.set_model(params.model_id)
713
- logger.info("Set model", model_id=params.model_id, session_id=params.session_id)
714
- return SetSessionModelResponse()
715
-
694
+ # Handle ACPAgent specially - pass through to nested agent's model state
716
695
  if isinstance(session.agent, ACPAgentClient):
717
- # For ACPAgent, validate against nested agent's model list
718
696
  if session.agent._state and session.agent._state.models:
719
697
  available_ids = [
720
698
  m.model_id for m in session.agent._state.models.available_models
@@ -727,21 +705,27 @@ class AgentPoolACPAgent(ACPAgent):
727
705
  )
728
706
  return None
729
707
  # TODO: Use ACP protocol once set_session_model stabilizes
730
- # For now, we can't actually change the model on ACPAgent
731
708
  logger.warning(
732
709
  "Model change for ACPAgent not yet supported (ACP protocol UNSTABLE)",
733
710
  model_id=params.model_id,
734
711
  )
735
712
  return None
736
713
 
737
- if isinstance(session.agent, Agent):
738
- # For native Agent, validate against server's available models
739
- available_ids = [m.model_id for m in self.available_models]
714
+ # Get available models from agent and validate
715
+ toko_models = await session.agent.get_available_models()
716
+ if toko_models:
717
+ # Build list of valid model IDs (using id_override if set)
718
+ available_ids = [m.id_override if m.id_override else m.id for m in toko_models]
740
719
  if params.model_id not in available_ids:
741
- msg = "Model not in available models"
742
- logger.warning(msg, model_id=params.model_id, available=available_ids)
720
+ logger.warning(
721
+ "Model not in available models",
722
+ model_id=params.model_id,
723
+ available=available_ids,
724
+ )
743
725
  return None
744
- session.agent.set_model(params.model_id)
726
+
727
+ # Set the model on the agent (all agents now have async set_model)
728
+ await session.agent.set_model(params.model_id)
745
729
 
746
730
  logger.info("Set model", model_id=params.model_id, session_id=params.session_id)
747
731
  return SetSessionModelResponse()