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
@@ -6,6 +6,7 @@ from typing import Any, Literal, Self
6
6
 
7
7
  from pydantic import Field
8
8
 
9
+ from agentpool_server.opencode_server.models.app import Project # noqa: TC001
9
10
  from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
10
11
  from agentpool_server.opencode_server.models.message import ( # noqa: TC001
11
12
  AssistantMessage,
@@ -203,26 +204,68 @@ class PartUpdatedEvent(OpenCodeBaseModel):
203
204
  return cls(properties=PartUpdatedEventProperties(part=part, delta=delta))
204
205
 
205
206
 
206
- class PermissionTimeInfo(OpenCodeBaseModel):
207
- """Time information for permission event."""
207
+ class MessageRemovedProperties(OpenCodeBaseModel):
208
+ """Properties for message removed event."""
208
209
 
209
- created: int
210
+ session_id: str = Field(alias="sessionID")
211
+ message_id: str = Field(alias="messageID")
210
212
 
211
213
 
212
- class PermissionUpdatedProperties(OpenCodeBaseModel):
213
- """Properties for permission updated event.
214
+ class MessageRemovedEvent(OpenCodeBaseModel):
215
+ """Message removed event - emitted during revert."""
214
216
 
215
- Matches OpenCode's Permission.Info schema.
216
- """
217
+ type: Literal["message.removed"] = "message.removed"
218
+ properties: MessageRemovedProperties
217
219
 
218
- id: str
219
- """Permission ID."""
220
+ @classmethod
221
+ def create(cls, session_id: str, message_id: str) -> Self:
222
+ """Create message removed event.
220
223
 
221
- type: str
222
- """Tool type/name."""
224
+ Args:
225
+ session_id: Session identifier
226
+ message_id: Message identifier
223
227
 
224
- session_id: str
225
- """Session ID."""
228
+ Returns:
229
+ MessageRemovedEvent instance
230
+ """
231
+ return cls(properties=MessageRemovedProperties(sessionID=session_id, messageID=message_id))
232
+
233
+
234
+ class PartRemovedProperties(OpenCodeBaseModel):
235
+ """Properties for part removed event."""
236
+
237
+ session_id: str = Field(alias="sessionID")
238
+ message_id: str = Field(alias="messageID")
239
+ part_id: str = Field(alias="partID")
240
+
241
+
242
+ class PartRemovedEvent(OpenCodeBaseModel):
243
+ """Part removed event - emitted during revert."""
244
+
245
+ type: Literal["message.part.removed"] = "message.part.removed"
246
+ properties: PartRemovedProperties
247
+
248
+ @classmethod
249
+ def create(cls, session_id: str, message_id: str, part_id: str) -> Self:
250
+ """Create part removed event.
251
+
252
+ Args:
253
+ session_id: Session identifier
254
+ message_id: Message identifier
255
+ part_id: Part identifier
256
+
257
+ Returns:
258
+ PartRemovedEvent instance
259
+ """
260
+ return cls(
261
+ properties=PartRemovedProperties(
262
+ sessionID=session_id, messageID=message_id, partID=part_id
263
+ )
264
+ )
265
+
266
+
267
+ class PermissionToolInfo(OpenCodeBaseModel):
268
+ """Tool information for permission event."""
226
269
 
227
270
  message_id: str
228
271
  """Message ID."""
@@ -230,27 +273,43 @@ class PermissionUpdatedProperties(OpenCodeBaseModel):
230
273
  call_id: str | None = None
231
274
  """Optional tool call ID."""
232
275
 
233
- title: str
234
- """Human-readable title for the permission request."""
276
+
277
+ class PermissionAskedProperties(OpenCodeBaseModel):
278
+ """Properties for permission.asked event.
279
+
280
+ Matches OpenCode's PermissionNext.Event.Asked schema.
281
+ """
282
+
283
+ id: str
284
+ """Permission request ID."""
285
+
286
+ session_id: str
287
+ """Session ID."""
288
+
289
+ permission: str
290
+ """Tool/permission type name."""
291
+
292
+ patterns: list[str]
293
+ """Patterns for matching (e.g., file paths, commands)."""
235
294
 
236
295
  metadata: dict[str, Any]
237
296
  """Arbitrary metadata about the tool call."""
238
297
 
239
- time: PermissionTimeInfo
240
- """Timestamp information."""
298
+ always: list[str]
299
+ """Patterns that would be approved for future requests if user selects 'always'."""
241
300
 
242
- pattern: str | list[str] | None = None
243
- """Optional pattern for matching."""
301
+ tool: PermissionToolInfo
302
+ """Tool call information."""
244
303
 
245
304
 
246
305
  class PermissionRequestEvent(OpenCodeBaseModel):
247
306
  """Permission request event - sent when a tool needs user confirmation.
248
307
 
249
- Uses 'permission.updated' event type for OpenCode TUI compatibility.
308
+ Uses 'permission.asked' event type for OpenCode TUI compatibility.
250
309
  """
251
310
 
252
- type: Literal["permission.updated"] = "permission.updated"
253
- properties: PermissionUpdatedProperties
311
+ type: Literal["permission.asked"] = "permission.asked"
312
+ properties: PermissionAskedProperties
254
313
 
255
314
  @classmethod
256
315
  def create(
@@ -263,17 +322,17 @@ class PermissionRequestEvent(OpenCodeBaseModel):
263
322
  message_id: str = "",
264
323
  call_id: str | None = None,
265
324
  ) -> Self:
266
- import time
325
+ # Create pattern from tool name and args
326
+ pattern = f"{tool_name}: {args_preview}" if args_preview else tool_name
267
327
 
268
- props = PermissionUpdatedProperties(
328
+ props = PermissionAskedProperties(
269
329
  id=permission_id,
270
- type=tool_name,
271
330
  session_id=session_id,
272
- message_id=message_id,
273
- call_id=call_id,
274
- title=message,
331
+ permission=tool_name,
332
+ patterns=[pattern],
275
333
  metadata={"args_preview": args_preview},
276
- time=PermissionTimeInfo(created=int(time.time() * 1000)),
334
+ always=[pattern], # Same pattern for "always" approval
335
+ tool=PermissionToolInfo(message_id=message_id, call_id=call_id),
277
336
  )
278
337
  return cls(properties=props)
279
338
 
@@ -601,6 +660,25 @@ class LspClientDiagnosticsEvent(OpenCodeBaseModel):
601
660
  # =============================================================================
602
661
 
603
662
 
663
+ class ProjectUpdatedEvent(OpenCodeBaseModel):
664
+ """Project metadata updated event."""
665
+
666
+ type: Literal["project.updated"] = "project.updated"
667
+ properties: Project
668
+
669
+ @classmethod
670
+ def create(cls, project: Project) -> Self:
671
+ """Create project updated event.
672
+
673
+ Args:
674
+ project: The updated project data
675
+
676
+ Returns:
677
+ ProjectUpdatedEvent instance
678
+ """
679
+ return cls(properties=project)
680
+
681
+
604
682
  class VcsBranchUpdatedProperties(OpenCodeBaseModel):
605
683
  """Properties for VCS branch updated event."""
606
684
 
@@ -619,6 +697,96 @@ class VcsBranchUpdatedEvent(OpenCodeBaseModel):
619
697
  return cls(properties=VcsBranchUpdatedProperties(branch=branch))
620
698
 
621
699
 
700
+ class QuestionAskedProperties(OpenCodeBaseModel):
701
+ """Properties for question asked event."""
702
+
703
+ id: str
704
+ session_id: str
705
+ questions: list[dict[str, Any]]
706
+ tool: dict[str, str] | None = None
707
+
708
+
709
+ class QuestionAskedEvent(OpenCodeBaseModel):
710
+ """Question asked event - sent when agent asks a question."""
711
+
712
+ type: Literal["question.asked"] = "question.asked"
713
+ properties: QuestionAskedProperties
714
+
715
+ @classmethod
716
+ def create(
717
+ cls,
718
+ request_id: str,
719
+ session_id: str,
720
+ questions: list[dict[str, Any]],
721
+ tool: dict[str, str] | None = None,
722
+ ) -> Self:
723
+ return cls(
724
+ properties=QuestionAskedProperties(
725
+ id=request_id,
726
+ sessionID=session_id,
727
+ questions=questions,
728
+ tool=tool,
729
+ )
730
+ )
731
+
732
+
733
+ class QuestionRepliedProperties(OpenCodeBaseModel):
734
+ """Properties for question replied event."""
735
+
736
+ session_id: str
737
+ request_id: str
738
+ answers: list[list[str]]
739
+
740
+
741
+ class QuestionRepliedEvent(OpenCodeBaseModel):
742
+ """Question replied event - sent when user answers a question."""
743
+
744
+ type: Literal["question.replied"] = "question.replied"
745
+ properties: QuestionRepliedProperties
746
+
747
+ @classmethod
748
+ def create(
749
+ cls,
750
+ session_id: str,
751
+ request_id: str,
752
+ answers: list[list[str]],
753
+ ) -> Self:
754
+ return cls(
755
+ properties=QuestionRepliedProperties(
756
+ sessionID=session_id,
757
+ requestID=request_id,
758
+ answers=answers,
759
+ )
760
+ )
761
+
762
+
763
+ class QuestionRejectedProperties(OpenCodeBaseModel):
764
+ """Properties for question rejected event."""
765
+
766
+ session_id: str
767
+ request_id: str
768
+
769
+
770
+ class QuestionRejectedEvent(OpenCodeBaseModel):
771
+ """Question rejected event - sent when user dismisses a question."""
772
+
773
+ type: Literal["question.rejected"] = "question.rejected"
774
+ properties: QuestionRejectedProperties
775
+
776
+ @classmethod
777
+ def create(
778
+ cls,
779
+ session_id: str,
780
+ request_id: str,
781
+ ) -> Self:
782
+ return cls(
783
+ properties=QuestionRejectedProperties(
784
+ sessionID=session_id,
785
+ requestID=request_id,
786
+ )
787
+ )
788
+
789
+
622
790
  Event = (
623
791
  ServerConnectedEvent
624
792
  | SessionCreatedEvent
@@ -628,9 +796,14 @@ Event = (
628
796
  | SessionErrorEvent
629
797
  | SessionIdleEvent
630
798
  | MessageUpdatedEvent
799
+ | MessageRemovedEvent
631
800
  | PartUpdatedEvent
801
+ | PartRemovedEvent
632
802
  | PermissionRequestEvent
633
803
  | PermissionResolvedEvent
804
+ | QuestionAskedEvent
805
+ | QuestionRepliedEvent
806
+ | QuestionRejectedEvent
634
807
  | TodoUpdatedEvent
635
808
  | FileWatcherUpdatedEvent
636
809
  | SessionCompactedEvent
@@ -640,6 +813,7 @@ Event = (
640
813
  | PtyDeletedEvent
641
814
  | LspUpdatedEvent
642
815
  | LspClientDiagnosticsEvent
816
+ | ProjectUpdatedEvent
643
817
  | VcsBranchUpdatedEvent
644
818
  | TuiPromptAppendEvent
645
819
  | TuiCommandExecuteEvent
@@ -23,3 +23,22 @@ class MCPStatus(OpenCodeBaseModel):
23
23
  status: Literal["connected", "disconnected", "error"]
24
24
  tools: list[str] = Field(default_factory=list)
25
25
  error: str | None = None
26
+
27
+
28
+ class McpResource(OpenCodeBaseModel):
29
+ """MCP resource info matching OpenCode SDK McpResource type."""
30
+
31
+ name: str
32
+ """Name of the resource."""
33
+
34
+ uri: str
35
+ """URI identifying the resource location."""
36
+
37
+ description: str | None = None
38
+ """Optional description of the resource."""
39
+
40
+ mime_type: str | None = None
41
+ """MIME type of the resource content."""
42
+
43
+ client: str
44
+ """Name of the MCP client/server providing this resource."""
@@ -11,6 +11,14 @@ from agentpool_server.opencode_server.models.common import TimeCreated # noqa:
11
11
  from agentpool_server.opencode_server.models.parts import Part # noqa: TC001
12
12
 
13
13
 
14
+ class MessageSummary(OpenCodeBaseModel):
15
+ """Summary information for a message."""
16
+
17
+ title: str | None = None
18
+ body: str | None = None
19
+ diffs: list[Any] = Field(default_factory=list)
20
+
21
+
14
22
  class MessagePath(OpenCodeBaseModel):
15
23
  """Path context for a message."""
16
24
 
@@ -56,7 +64,11 @@ class UserMessage(OpenCodeBaseModel):
56
64
  session_id: str
57
65
  time: TimeCreated
58
66
  agent: str = "default"
59
- model: UserMessageModel
67
+ model: UserMessageModel | None = None
68
+ summary: MessageSummary | None = None
69
+ system: str | None = None
70
+ tools: dict[str, bool] | None = None
71
+ variant: str | None = None
60
72
 
61
73
 
62
74
  class AssistantMessage(OpenCodeBaseModel):
@@ -160,3 +172,8 @@ class CommandRequest(OpenCodeBaseModel):
160
172
  agent: str | None = None
161
173
  model: str | None = None # Format: "providerID/modelID"
162
174
  message_id: str | None = None
175
+
176
+
177
+ # Type unions
178
+
179
+ MessageInfo = UserMessage | AssistantMessage
@@ -7,6 +7,7 @@ from typing import Any, Literal
7
7
  from pydantic import Field
8
8
 
9
9
  from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
10
+ from agentpool_server.opencode_server.models.common import TimeCreated # noqa: TC001
10
11
 
11
12
 
12
13
  class TimeStart(OpenCodeBaseModel):
@@ -148,6 +149,125 @@ class FilePart(OpenCodeBaseModel):
148
149
  source: FileSource | None = None
149
150
 
150
151
 
152
+ class AgentPartSource(OpenCodeBaseModel):
153
+ """Agent part source location in original text."""
154
+
155
+ value: str
156
+ start: int
157
+ end: int
158
+
159
+
160
+ class AgentPart(OpenCodeBaseModel):
161
+ """Agent mention part - references a sub-agent to delegate to.
162
+
163
+ When a user types @agent-name in the prompt, this part is created.
164
+ The server should inject a synthetic instruction to call the task tool
165
+ with the specified agent.
166
+ """
167
+
168
+ id: str
169
+ type: Literal["agent"] = "agent"
170
+ message_id: str
171
+ session_id: str
172
+ name: str
173
+ """Name of the agent to delegate to."""
174
+ source: AgentPartSource | None = None
175
+ """Source location in the original prompt text."""
176
+
177
+
178
+ class SnapshotPart(OpenCodeBaseModel):
179
+ """File system snapshot reference."""
180
+
181
+ id: str
182
+ type: Literal["snapshot"] = "snapshot"
183
+ message_id: str
184
+ session_id: str
185
+ snapshot: str
186
+ """Snapshot identifier."""
187
+
188
+
189
+ class PatchPart(OpenCodeBaseModel):
190
+ """Diff/patch content part."""
191
+
192
+ id: str
193
+ type: Literal["patch"] = "patch"
194
+ message_id: str
195
+ session_id: str
196
+ hash: str
197
+ """Hash of the patch."""
198
+ files: list[str] = Field(default_factory=list)
199
+ """List of files affected by this patch."""
200
+
201
+
202
+ class ReasoningPart(OpenCodeBaseModel):
203
+ """Extended thinking/reasoning content part.
204
+
205
+ Used for models that support extended thinking (e.g., Claude with thinking tokens).
206
+ """
207
+
208
+ id: str
209
+ type: Literal["reasoning"] = "reasoning"
210
+ message_id: str
211
+ session_id: str
212
+ text: str
213
+ """The reasoning/thinking content."""
214
+ metadata: dict[str, Any] | None = None
215
+ time: TimeStartEndOptional | None = None
216
+
217
+
218
+ class CompactionPart(OpenCodeBaseModel):
219
+ """Marks where conversation was compacted/summarized."""
220
+
221
+ id: str
222
+ type: Literal["compaction"] = "compaction"
223
+ message_id: str
224
+ session_id: str
225
+ auto: bool = False
226
+ """Whether this was an automatic compaction."""
227
+
228
+
229
+ class SubtaskPart(OpenCodeBaseModel):
230
+ """References a spawned subtask."""
231
+
232
+ id: str
233
+ type: Literal["subtask"] = "subtask"
234
+ message_id: str
235
+ session_id: str
236
+ prompt: str
237
+ """The prompt for the subtask."""
238
+ description: str
239
+ """Description of what the subtask does."""
240
+ agent: str
241
+ """The agent handling this subtask."""
242
+ command: str | None = None
243
+ """Optional command associated with the subtask."""
244
+
245
+
246
+ class APIErrorInfo(OpenCodeBaseModel):
247
+ """API error information for retry parts."""
248
+
249
+ message: str
250
+ status_code: int | None = None
251
+ is_retryable: bool = False
252
+ response_headers: dict[str, str] | None = None
253
+ response_body: str | None = None
254
+ metadata: dict[str, str] | None = None
255
+
256
+
257
+ class RetryPart(OpenCodeBaseModel):
258
+ """Marks a retry of a failed operation."""
259
+
260
+ id: str
261
+ type: Literal["retry"] = "retry"
262
+ message_id: str
263
+ session_id: str
264
+ attempt: int
265
+ """Which retry attempt this is."""
266
+ error: APIErrorInfo
267
+ """Error information from the failed attempt."""
268
+ time: TimeCreated
269
+
270
+
151
271
  class StepStartPart(OpenCodeBaseModel):
152
272
  """Step start marker."""
153
273
 
@@ -187,4 +307,17 @@ class StepFinishPart(OpenCodeBaseModel):
187
307
  tokens: StepFinishTokens = Field(default_factory=StepFinishTokens)
188
308
 
189
309
 
190
- Part = TextPart | ToolPart | FilePart | StepStartPart | StepFinishPart
310
+ Part = (
311
+ TextPart
312
+ | ToolPart
313
+ | FilePart
314
+ | AgentPart
315
+ | SnapshotPart
316
+ | PatchPart
317
+ | ReasoningPart
318
+ | CompactionPart
319
+ | SubtaskPart
320
+ | RetryPart
321
+ | StepStartPart
322
+ | StepFinishPart
323
+ )
@@ -0,0 +1,56 @@
1
+ """Question models for OpenCode compatibility."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic import Field
6
+
7
+ from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
8
+
9
+
10
+ class QuestionOption(OpenCodeBaseModel):
11
+ """A single option for a question."""
12
+
13
+ label: str
14
+ """Display text (1-5 words, concise)."""
15
+
16
+ description: str
17
+ """Explanation of choice."""
18
+
19
+
20
+ class QuestionInfo(OpenCodeBaseModel):
21
+ """Information about a single question."""
22
+
23
+ question: str
24
+ """Complete question."""
25
+
26
+ header: str = Field(max_length=12)
27
+ """Very short label (max 12 chars)."""
28
+
29
+ options: list[QuestionOption]
30
+ """Available choices."""
31
+
32
+ multiple: bool | None = None
33
+ """Allow selecting multiple choices."""
34
+
35
+
36
+ class QuestionRequest(OpenCodeBaseModel):
37
+ """A pending question request."""
38
+
39
+ id: str
40
+ """Unique question identifier."""
41
+
42
+ session_id: str
43
+ """Session identifier."""
44
+
45
+ questions: list[QuestionInfo]
46
+ """List of questions to ask."""
47
+
48
+ tool: dict[str, str] | None = None
49
+ """Optional tool context: {message_id, call_id}."""
50
+
51
+
52
+ class QuestionReply(OpenCodeBaseModel):
53
+ """Reply to a question request."""
54
+
55
+ answers: list[list[str]]
56
+ """User answers in order of questions (each answer is an array of selected labels)."""
@@ -2,12 +2,23 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Literal
5
+ from typing import Any, Literal
6
+
7
+ from pydantic import Field
6
8
 
7
9
  from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
8
10
  from agentpool_server.opencode_server.models.common import TimeCreatedUpdated # noqa: TC001
9
11
 
10
12
 
13
+ class SessionSummary(OpenCodeBaseModel):
14
+ """Summary information for a session."""
15
+
16
+ additions: int | None = None
17
+ deletions: int | None = None
18
+ files: int | None = None
19
+ diffs: list[Any] = Field(default_factory=list)
20
+
21
+
11
22
  class SessionRevert(OpenCodeBaseModel):
12
23
  """Revert information for a session."""
13
24
 
@@ -33,6 +44,7 @@ class Session(OpenCodeBaseModel):
33
44
  version: str = "1"
34
45
  time: TimeCreatedUpdated
35
46
  parent_id: str | None = None
47
+ summary: SessionSummary | None = None
36
48
  revert: SessionRevert | None = None
37
49
  share: SessionShare | None = None
38
50
 
@@ -7,6 +7,8 @@ from agentpool_server.opencode_server.routes.session_routes import router as ses
7
7
  from agentpool_server.opencode_server.routes.message_routes import router as message_router
8
8
  from agentpool_server.opencode_server.routes.file_routes import router as file_router
9
9
  from agentpool_server.opencode_server.routes.agent_routes import router as agent_router
10
+ from agentpool_server.opencode_server.routes.permission_routes import router as permission_router
11
+ from agentpool_server.opencode_server.routes.question_routes import router as question_router
10
12
  from agentpool_server.opencode_server.routes.pty_routes import router as pty_router
11
13
  from agentpool_server.opencode_server.routes.tui_routes import router as tui_router
12
14
  from agentpool_server.opencode_server.routes.lsp_routes import router as lsp_router
@@ -19,7 +21,9 @@ __all__ = [
19
21
  "global_router",
20
22
  "lsp_router",
21
23
  "message_router",
24
+ "permission_router",
22
25
  "pty_router",
26
+ "question_router",
23
27
  "session_router",
24
28
  "tui_router",
25
29
  ]
@@ -21,11 +21,12 @@ from agentpool_config.mcp_server import (
21
21
  StdioMCPServerConfig,
22
22
  StreamableHTTPMCPServerConfig,
23
23
  )
24
- from agentpool_server.opencode_server.dependencies import StateDep # noqa: TC001
25
- from agentpool_server.opencode_server.models import ( # noqa: TC001
24
+ from agentpool_server.opencode_server.dependencies import StateDep
25
+ from agentpool_server.opencode_server.models import (
26
26
  Agent,
27
27
  Command,
28
28
  LogRequest,
29
+ McpResource,
29
30
  MCPStatus,
30
31
  )
31
32
 
@@ -178,6 +179,36 @@ async def log(request: LogRequest, state: StateDep) -> bool:
178
179
  return True
179
180
 
180
181
 
182
+ @router.get("/experimental/resource")
183
+ async def list_mcp_resources(state: StateDep) -> dict[str, McpResource]:
184
+ """Get all available MCP resources from connected servers.
185
+
186
+ Returns a dictionary mapping resource keys to McpResource objects.
187
+ Keys are formatted as "{client}:{resource_name}" for uniqueness.
188
+ """
189
+ try:
190
+ resources = await state.agent.tools.list_resources()
191
+ result: dict[str, McpResource] = {}
192
+
193
+ for resource in resources:
194
+ # Create unique key: sanitize client and resource names
195
+ client_name = (resource.client or "unknown").replace("/", "_")
196
+ resource_name = resource.name.replace("/", "_")
197
+ key = f"{client_name}:{resource_name}"
198
+
199
+ result[key] = McpResource(
200
+ name=resource.name,
201
+ uri=resource.uri,
202
+ description=resource.description,
203
+ mime_type=resource.mime_type,
204
+ client=resource.client or "unknown",
205
+ )
206
+ except Exception: # noqa: BLE001
207
+ return {}
208
+ else:
209
+ return result
210
+
211
+
181
212
  @router.get("/experimental/tool/ids")
182
213
  async def list_tool_ids(state: StateDep) -> list[str]:
183
214
  """List all available tool IDs.