agentpool 2.1.9__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 (311) hide show
  1. acp/__init__.py +13 -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/bridge/README.md +15 -2
  7. acp/bridge/__init__.py +3 -2
  8. acp/bridge/__main__.py +60 -19
  9. acp/bridge/ws_server.py +173 -0
  10. acp/bridge/ws_server_cli.py +89 -0
  11. acp/client/connection.py +38 -29
  12. acp/client/implementations/default_client.py +3 -2
  13. acp/client/implementations/headless_client.py +2 -2
  14. acp/connection.py +2 -2
  15. acp/notifications.py +20 -50
  16. acp/schema/__init__.py +2 -0
  17. acp/schema/agent_responses.py +21 -0
  18. acp/schema/client_requests.py +3 -3
  19. acp/schema/session_state.py +63 -29
  20. acp/stdio.py +39 -9
  21. acp/task/supervisor.py +2 -2
  22. acp/transports.py +362 -2
  23. acp/utils.py +17 -4
  24. agentpool/__init__.py +6 -1
  25. agentpool/agents/__init__.py +2 -0
  26. agentpool/agents/acp_agent/acp_agent.py +407 -277
  27. agentpool/agents/acp_agent/acp_converters.py +196 -38
  28. agentpool/agents/acp_agent/client_handler.py +191 -26
  29. agentpool/agents/acp_agent/session_state.py +17 -6
  30. agentpool/agents/agent.py +607 -572
  31. agentpool/agents/agui_agent/__init__.py +0 -2
  32. agentpool/agents/agui_agent/agui_agent.py +176 -110
  33. agentpool/agents/agui_agent/agui_converters.py +0 -131
  34. agentpool/agents/agui_agent/helpers.py +3 -4
  35. agentpool/agents/base_agent.py +632 -17
  36. agentpool/agents/claude_code_agent/FORKING.md +191 -0
  37. agentpool/agents/claude_code_agent/__init__.py +13 -1
  38. agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
  39. agentpool/agents/claude_code_agent/converters.py +74 -143
  40. agentpool/agents/claude_code_agent/history.py +474 -0
  41. agentpool/agents/claude_code_agent/models.py +77 -0
  42. agentpool/agents/claude_code_agent/static_info.py +100 -0
  43. agentpool/agents/claude_code_agent/usage.py +242 -0
  44. agentpool/agents/context.py +40 -0
  45. agentpool/agents/events/__init__.py +24 -0
  46. agentpool/agents/events/builtin_handlers.py +67 -1
  47. agentpool/agents/events/event_emitter.py +32 -2
  48. agentpool/agents/events/events.py +104 -3
  49. agentpool/agents/events/infer_info.py +145 -0
  50. agentpool/agents/events/processors.py +254 -0
  51. agentpool/agents/interactions.py +41 -6
  52. agentpool/agents/modes.py +67 -0
  53. agentpool/agents/slashed_agent.py +5 -4
  54. agentpool/agents/tool_call_accumulator.py +213 -0
  55. agentpool/agents/tool_wrapping.py +18 -6
  56. agentpool/common_types.py +56 -21
  57. agentpool/config_resources/__init__.py +38 -1
  58. agentpool/config_resources/acp_assistant.yml +2 -2
  59. agentpool/config_resources/agents.yml +3 -0
  60. agentpool/config_resources/agents_template.yml +1 -0
  61. agentpool/config_resources/claude_code_agent.yml +10 -6
  62. agentpool/config_resources/external_acp_agents.yml +2 -1
  63. agentpool/delegation/base_team.py +4 -30
  64. agentpool/delegation/pool.py +136 -289
  65. agentpool/delegation/team.py +58 -57
  66. agentpool/delegation/teamrun.py +51 -55
  67. agentpool/diagnostics/__init__.py +53 -0
  68. agentpool/diagnostics/lsp_manager.py +1593 -0
  69. agentpool/diagnostics/lsp_proxy.py +41 -0
  70. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  71. agentpool/diagnostics/models.py +398 -0
  72. agentpool/functional/run.py +10 -4
  73. agentpool/mcp_server/__init__.py +0 -2
  74. agentpool/mcp_server/client.py +76 -32
  75. agentpool/mcp_server/conversions.py +54 -13
  76. agentpool/mcp_server/manager.py +34 -54
  77. agentpool/mcp_server/registries/official_registry_client.py +35 -1
  78. agentpool/mcp_server/tool_bridge.py +186 -139
  79. agentpool/messaging/__init__.py +0 -2
  80. agentpool/messaging/compaction.py +72 -197
  81. agentpool/messaging/connection_manager.py +11 -10
  82. agentpool/messaging/event_manager.py +5 -5
  83. agentpool/messaging/message_container.py +6 -30
  84. agentpool/messaging/message_history.py +99 -8
  85. agentpool/messaging/messagenode.py +52 -14
  86. agentpool/messaging/messages.py +54 -35
  87. agentpool/messaging/processing.py +12 -22
  88. agentpool/models/__init__.py +1 -1
  89. agentpool/models/acp_agents/base.py +6 -24
  90. agentpool/models/acp_agents/mcp_capable.py +126 -157
  91. agentpool/models/acp_agents/non_mcp.py +129 -95
  92. agentpool/models/agents.py +98 -76
  93. agentpool/models/agui_agents.py +1 -1
  94. agentpool/models/claude_code_agents.py +144 -19
  95. agentpool/models/file_parsing.py +0 -1
  96. agentpool/models/manifest.py +113 -50
  97. agentpool/prompts/conversion_manager.py +1 -1
  98. agentpool/prompts/prompts.py +5 -2
  99. agentpool/repomap.py +1 -1
  100. agentpool/resource_providers/__init__.py +11 -1
  101. agentpool/resource_providers/aggregating.py +56 -5
  102. agentpool/resource_providers/base.py +70 -4
  103. agentpool/resource_providers/codemode/code_executor.py +72 -5
  104. agentpool/resource_providers/codemode/helpers.py +2 -2
  105. agentpool/resource_providers/codemode/provider.py +64 -12
  106. agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
  107. agentpool/resource_providers/codemode/remote_provider.py +9 -12
  108. agentpool/resource_providers/filtering.py +3 -1
  109. agentpool/resource_providers/mcp_provider.py +89 -12
  110. agentpool/resource_providers/plan_provider.py +228 -46
  111. agentpool/resource_providers/pool.py +7 -3
  112. agentpool/resource_providers/resource_info.py +111 -0
  113. agentpool/resource_providers/static.py +4 -2
  114. agentpool/sessions/__init__.py +4 -1
  115. agentpool/sessions/manager.py +33 -5
  116. agentpool/sessions/models.py +59 -6
  117. agentpool/sessions/protocol.py +28 -0
  118. agentpool/sessions/session.py +11 -55
  119. agentpool/skills/registry.py +13 -8
  120. agentpool/storage/manager.py +572 -49
  121. agentpool/talk/registry.py +4 -4
  122. agentpool/talk/talk.py +9 -10
  123. agentpool/testing.py +538 -20
  124. agentpool/tool_impls/__init__.py +6 -0
  125. agentpool/tool_impls/agent_cli/__init__.py +42 -0
  126. agentpool/tool_impls/agent_cli/tool.py +95 -0
  127. agentpool/tool_impls/bash/__init__.py +64 -0
  128. agentpool/tool_impls/bash/helpers.py +35 -0
  129. agentpool/tool_impls/bash/tool.py +171 -0
  130. agentpool/tool_impls/delete_path/__init__.py +70 -0
  131. agentpool/tool_impls/delete_path/tool.py +142 -0
  132. agentpool/tool_impls/download_file/__init__.py +80 -0
  133. agentpool/tool_impls/download_file/tool.py +183 -0
  134. agentpool/tool_impls/execute_code/__init__.py +55 -0
  135. agentpool/tool_impls/execute_code/tool.py +163 -0
  136. agentpool/tool_impls/grep/__init__.py +80 -0
  137. agentpool/tool_impls/grep/tool.py +200 -0
  138. agentpool/tool_impls/list_directory/__init__.py +73 -0
  139. agentpool/tool_impls/list_directory/tool.py +197 -0
  140. agentpool/tool_impls/question/__init__.py +42 -0
  141. agentpool/tool_impls/question/tool.py +127 -0
  142. agentpool/tool_impls/read/__init__.py +104 -0
  143. agentpool/tool_impls/read/tool.py +305 -0
  144. agentpool/tools/__init__.py +2 -1
  145. agentpool/tools/base.py +114 -34
  146. agentpool/tools/manager.py +57 -1
  147. agentpool/ui/base.py +2 -2
  148. agentpool/ui/mock_provider.py +2 -2
  149. agentpool/ui/stdlib_provider.py +2 -2
  150. agentpool/utils/file_watcher.py +269 -0
  151. agentpool/utils/identifiers.py +121 -0
  152. agentpool/utils/pydantic_ai_helpers.py +46 -0
  153. agentpool/utils/streams.py +616 -2
  154. agentpool/utils/subprocess_utils.py +155 -0
  155. agentpool/utils/token_breakdown.py +461 -0
  156. agentpool/vfs_registry.py +7 -2
  157. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/METADATA +41 -27
  158. agentpool-2.5.0.dist-info/RECORD +579 -0
  159. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
  160. agentpool_cli/__main__.py +24 -0
  161. agentpool_cli/create.py +1 -1
  162. agentpool_cli/serve_acp.py +100 -21
  163. agentpool_cli/serve_agui.py +87 -0
  164. agentpool_cli/serve_opencode.py +119 -0
  165. agentpool_cli/ui.py +557 -0
  166. agentpool_commands/__init__.py +42 -5
  167. agentpool_commands/agents.py +75 -2
  168. agentpool_commands/history.py +62 -0
  169. agentpool_commands/mcp.py +176 -0
  170. agentpool_commands/models.py +56 -3
  171. agentpool_commands/pool.py +260 -0
  172. agentpool_commands/session.py +1 -1
  173. agentpool_commands/text_sharing/__init__.py +119 -0
  174. agentpool_commands/text_sharing/base.py +123 -0
  175. agentpool_commands/text_sharing/github_gist.py +80 -0
  176. agentpool_commands/text_sharing/opencode.py +462 -0
  177. agentpool_commands/text_sharing/paste_rs.py +59 -0
  178. agentpool_commands/text_sharing/pastebin.py +116 -0
  179. agentpool_commands/text_sharing/shittycodingagent.py +112 -0
  180. agentpool_commands/tools.py +57 -0
  181. agentpool_commands/utils.py +80 -30
  182. agentpool_config/__init__.py +30 -2
  183. agentpool_config/agentpool_tools.py +498 -0
  184. agentpool_config/builtin_tools.py +77 -22
  185. agentpool_config/commands.py +24 -1
  186. agentpool_config/compaction.py +258 -0
  187. agentpool_config/converters.py +1 -1
  188. agentpool_config/event_handlers.py +42 -0
  189. agentpool_config/events.py +1 -1
  190. agentpool_config/forward_targets.py +1 -4
  191. agentpool_config/jinja.py +3 -3
  192. agentpool_config/mcp_server.py +132 -6
  193. agentpool_config/nodes.py +1 -1
  194. agentpool_config/observability.py +44 -0
  195. agentpool_config/session.py +0 -3
  196. agentpool_config/storage.py +82 -38
  197. agentpool_config/task.py +3 -3
  198. agentpool_config/tools.py +11 -22
  199. agentpool_config/toolsets.py +109 -233
  200. agentpool_server/a2a_server/agent_worker.py +307 -0
  201. agentpool_server/a2a_server/server.py +23 -18
  202. agentpool_server/acp_server/acp_agent.py +234 -181
  203. agentpool_server/acp_server/commands/acp_commands.py +151 -156
  204. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
  205. agentpool_server/acp_server/event_converter.py +651 -0
  206. agentpool_server/acp_server/input_provider.py +53 -10
  207. agentpool_server/acp_server/server.py +24 -90
  208. agentpool_server/acp_server/session.py +173 -331
  209. agentpool_server/acp_server/session_manager.py +8 -34
  210. agentpool_server/agui_server/server.py +3 -1
  211. agentpool_server/mcp_server/server.py +5 -2
  212. agentpool_server/opencode_server/.rules +95 -0
  213. agentpool_server/opencode_server/ENDPOINTS.md +401 -0
  214. agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
  215. agentpool_server/opencode_server/__init__.py +19 -0
  216. agentpool_server/opencode_server/command_validation.py +172 -0
  217. agentpool_server/opencode_server/converters.py +975 -0
  218. agentpool_server/opencode_server/dependencies.py +24 -0
  219. agentpool_server/opencode_server/input_provider.py +421 -0
  220. agentpool_server/opencode_server/models/__init__.py +250 -0
  221. agentpool_server/opencode_server/models/agent.py +53 -0
  222. agentpool_server/opencode_server/models/app.py +72 -0
  223. agentpool_server/opencode_server/models/base.py +26 -0
  224. agentpool_server/opencode_server/models/common.py +23 -0
  225. agentpool_server/opencode_server/models/config.py +37 -0
  226. agentpool_server/opencode_server/models/events.py +821 -0
  227. agentpool_server/opencode_server/models/file.py +88 -0
  228. agentpool_server/opencode_server/models/mcp.py +44 -0
  229. agentpool_server/opencode_server/models/message.py +179 -0
  230. agentpool_server/opencode_server/models/parts.py +323 -0
  231. agentpool_server/opencode_server/models/provider.py +81 -0
  232. agentpool_server/opencode_server/models/pty.py +43 -0
  233. agentpool_server/opencode_server/models/question.py +56 -0
  234. agentpool_server/opencode_server/models/session.py +111 -0
  235. agentpool_server/opencode_server/routes/__init__.py +29 -0
  236. agentpool_server/opencode_server/routes/agent_routes.py +473 -0
  237. agentpool_server/opencode_server/routes/app_routes.py +202 -0
  238. agentpool_server/opencode_server/routes/config_routes.py +302 -0
  239. agentpool_server/opencode_server/routes/file_routes.py +571 -0
  240. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  241. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  242. agentpool_server/opencode_server/routes/message_routes.py +761 -0
  243. agentpool_server/opencode_server/routes/permission_routes.py +63 -0
  244. agentpool_server/opencode_server/routes/pty_routes.py +300 -0
  245. agentpool_server/opencode_server/routes/question_routes.py +128 -0
  246. agentpool_server/opencode_server/routes/session_routes.py +1276 -0
  247. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  248. agentpool_server/opencode_server/server.py +475 -0
  249. agentpool_server/opencode_server/state.py +151 -0
  250. agentpool_server/opencode_server/time_utils.py +8 -0
  251. agentpool_storage/__init__.py +12 -0
  252. agentpool_storage/base.py +184 -2
  253. agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
  254. agentpool_storage/claude_provider/__init__.py +42 -0
  255. agentpool_storage/claude_provider/provider.py +1089 -0
  256. agentpool_storage/file_provider.py +278 -15
  257. agentpool_storage/memory_provider.py +193 -12
  258. agentpool_storage/models.py +3 -0
  259. agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
  260. agentpool_storage/opencode_provider/__init__.py +16 -0
  261. agentpool_storage/opencode_provider/helpers.py +414 -0
  262. agentpool_storage/opencode_provider/provider.py +895 -0
  263. agentpool_storage/project_store.py +325 -0
  264. agentpool_storage/session_store.py +26 -6
  265. agentpool_storage/sql_provider/__init__.py +4 -2
  266. agentpool_storage/sql_provider/models.py +48 -0
  267. agentpool_storage/sql_provider/sql_provider.py +269 -3
  268. agentpool_storage/sql_provider/utils.py +12 -13
  269. agentpool_storage/zed_provider/__init__.py +16 -0
  270. agentpool_storage/zed_provider/helpers.py +281 -0
  271. agentpool_storage/zed_provider/models.py +130 -0
  272. agentpool_storage/zed_provider/provider.py +442 -0
  273. agentpool_storage/zed_provider.py +803 -0
  274. agentpool_toolsets/__init__.py +0 -2
  275. agentpool_toolsets/builtin/__init__.py +2 -12
  276. agentpool_toolsets/builtin/code.py +96 -57
  277. agentpool_toolsets/builtin/debug.py +118 -48
  278. agentpool_toolsets/builtin/execution_environment.py +115 -230
  279. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  280. agentpool_toolsets/builtin/skills.py +9 -4
  281. agentpool_toolsets/builtin/subagent_tools.py +64 -51
  282. agentpool_toolsets/builtin/workers.py +4 -2
  283. agentpool_toolsets/composio_toolset.py +2 -2
  284. agentpool_toolsets/entry_points.py +3 -1
  285. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  286. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  287. agentpool_toolsets/fsspec_toolset/grep.py +99 -7
  288. agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
  289. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  290. agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
  291. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  292. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  293. agentpool_toolsets/mcp_discovery/toolset.py +511 -0
  294. agentpool_toolsets/mcp_run_toolset.py +87 -12
  295. agentpool_toolsets/notifications.py +33 -33
  296. agentpool_toolsets/openapi.py +3 -1
  297. agentpool_toolsets/search_toolset.py +3 -1
  298. agentpool-2.1.9.dist-info/RECORD +0 -474
  299. agentpool_config/resources.py +0 -33
  300. agentpool_server/acp_server/acp_tools.py +0 -43
  301. agentpool_server/acp_server/commands/spawn.py +0 -210
  302. agentpool_storage/text_log_provider.py +0 -275
  303. agentpool_toolsets/builtin/agent_management.py +0 -239
  304. agentpool_toolsets/builtin/chain.py +0 -288
  305. agentpool_toolsets/builtin/history.py +0 -36
  306. agentpool_toolsets/builtin/integration.py +0 -85
  307. agentpool_toolsets/builtin/tool_management.py +0 -90
  308. agentpool_toolsets/builtin/user_interaction.py +0 -52
  309. agentpool_toolsets/semantic_memory_toolset.py +0 -536
  310. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
  311. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,151 @@
1
+ """Server state management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable, Coroutine
6
+ from dataclasses import dataclass, field
7
+ import time
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ import asyncio
13
+
14
+ from agentpool import AgentPool
15
+ from agentpool.agents.base_agent import BaseAgent
16
+ from agentpool.diagnostics.lsp_manager import LSPManager
17
+ from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider
18
+ from agentpool_server.opencode_server.models import (
19
+ Config,
20
+ Event,
21
+ MessageWithParts,
22
+ QuestionInfo,
23
+ Session,
24
+ SessionStatus,
25
+ Todo,
26
+ )
27
+
28
+ # Type alias for async callback
29
+ OnFirstSubscriberCallback = Callable[[], Coroutine[Any, Any, None]]
30
+
31
+
32
+ @dataclass
33
+ class PendingQuestion:
34
+ """Pending question awaiting user response."""
35
+
36
+ session_id: str
37
+ """Session that owns this question."""
38
+
39
+ questions: list[QuestionInfo]
40
+ """Questions to ask."""
41
+
42
+ future: asyncio.Future[list[list[str]]]
43
+ """Future that resolves when user answers."""
44
+
45
+ tool: dict[str, str] | None = None
46
+ """Optional tool context: {message_id, call_id}."""
47
+
48
+
49
+ @dataclass
50
+ class ServerState:
51
+ """Shared state for the OpenCode server.
52
+
53
+ Uses AgentPool for session persistence and storage.
54
+ In-memory state tracks active sessions and runtime data.
55
+ """
56
+
57
+ working_dir: str
58
+ pool: AgentPool[Any]
59
+ agent: BaseAgent[Any, Any]
60
+ start_time: float = field(default_factory=time.time)
61
+
62
+ # Configuration (mutable runtime config)
63
+ # Initialized after state creation
64
+ config: Config | None = None
65
+
66
+ # Active sessions cache (session_id -> OpenCode Session model)
67
+ # This is a cache of sessions loaded from pool.sessions
68
+ sessions: dict[str, Session] = field(default_factory=dict)
69
+ session_status: dict[str, SessionStatus] = field(default_factory=dict)
70
+
71
+ # Message storage (session_id -> messages)
72
+ # Runtime cache - messages are also persisted via pool.storage
73
+ messages: dict[str, list[MessageWithParts]] = field(default_factory=dict)
74
+
75
+ # Reverted messages storage (session_id -> removed messages)
76
+ # Stores messages removed during revert for unrevert operation
77
+ reverted_messages: dict[str, list[MessageWithParts]] = field(default_factory=dict)
78
+
79
+ # Todo storage (session_id -> todos)
80
+ # Uses pool.todos for persistence
81
+ todos: dict[str, list[Todo]] = field(default_factory=dict)
82
+
83
+ # Input providers for permission handling (session_id -> provider)
84
+ input_providers: dict[str, OpenCodeInputProvider] = field(default_factory=dict)
85
+
86
+ # Question storage (question_id -> pending question info)
87
+ pending_questions: dict[str, PendingQuestion] = field(default_factory=dict)
88
+
89
+ # SSE event subscribers
90
+ event_subscribers: list[asyncio.Queue[Event]] = field(default_factory=list)
91
+
92
+ # Callback for first subscriber connection (e.g., for update check)
93
+ on_first_subscriber: OnFirstSubscriberCallback | None = None
94
+ _first_subscriber_triggered: bool = field(default=False, repr=False)
95
+
96
+ # Background tasks (for cleanup on shutdown)
97
+ background_tasks: set[asyncio.Task[Any]] = field(default_factory=set)
98
+
99
+ # LSP manager for language server integration (initialized lazily)
100
+ lsp_manager: LSPManager | None = None
101
+
102
+ def create_background_task(self, coro: Any, *, name: str | None = None) -> asyncio.Task[Any]:
103
+ """Create and track a background task."""
104
+ import asyncio
105
+
106
+ task = asyncio.create_task(coro, name=name)
107
+ self.background_tasks.add(task)
108
+ task.add_done_callback(self.background_tasks.discard)
109
+ return task
110
+
111
+ async def cleanup_tasks(self) -> None:
112
+ """Cancel and wait for all background tasks."""
113
+ for task in self.background_tasks:
114
+ task.cancel()
115
+ if self.background_tasks:
116
+ import asyncio
117
+
118
+ await asyncio.gather(*self.background_tasks, return_exceptions=True)
119
+ self.background_tasks.clear()
120
+
121
+ async def broadcast_event(self, event: Event) -> None:
122
+ """Broadcast an event to all SSE subscribers."""
123
+ print(f"Broadcasting event: {event.type} to {len(self.event_subscribers)} subscribers")
124
+ for queue in self.event_subscribers:
125
+ await queue.put(event)
126
+
127
+ def get_or_create_lsp_manager(self) -> LSPManager:
128
+ """Get or create the LSP manager.
129
+
130
+ Creates the LSP manager lazily using the agent's execution environment.
131
+
132
+ Returns:
133
+ The LSP manager instance.
134
+
135
+ Raises:
136
+ RuntimeError: If the agent doesn't have an execution environment.
137
+ """
138
+ if self.lsp_manager is not None:
139
+ return self.lsp_manager
140
+
141
+ from agentpool.diagnostics.lsp_manager import LSPManager
142
+
143
+ # Get the execution environment from the agent
144
+ env = getattr(self.agent, "env", None)
145
+ if env is None:
146
+ msg = "Agent does not have an execution environment for LSP"
147
+ raise RuntimeError(msg)
148
+
149
+ self.lsp_manager = LSPManager(env=env)
150
+ self.lsp_manager.register_defaults()
151
+ return self.lsp_manager
@@ -0,0 +1,8 @@
1
+ """Time utilities for OpenCode compatibility."""
2
+
3
+ import time
4
+
5
+
6
+ def now_ms() -> int:
7
+ """Return current time in milliseconds as integer (OpenCode format)."""
8
+ return int(time.time() * 1000)
@@ -1,9 +1,21 @@
1
1
  """Storage provider package."""
2
2
 
3
3
  from agentpool_storage.base import StorageProvider
4
+ from agentpool_storage.project_store import (
5
+ ProjectStore,
6
+ detect_project_root,
7
+ discover_config_path,
8
+ generate_project_id,
9
+ resolve_config,
10
+ )
4
11
  from agentpool_storage.session_store import SQLSessionStore
5
12
 
6
13
  __all__ = [
14
+ "ProjectStore",
7
15
  "SQLSessionStore",
8
16
  "StorageProvider",
17
+ "detect_project_root",
18
+ "discover_config_path",
19
+ "generate_project_id",
20
+ "resolve_config",
9
21
  ]
agentpool_storage/base.py CHANGED
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
19
19
 
20
20
  from agentpool.common_types import JsonValue
21
21
  from agentpool.messaging import ChatMessage, TokenCost
22
+ from agentpool.sessions.models import ProjectData
22
23
  from agentpool_config.session import SessionQuery
23
24
  from agentpool_config.storage import BaseStorageProviderConfig
24
25
  from agentpool_storage.models import ConversationData, QueryFilters, StatsFilters
@@ -37,7 +38,6 @@ class StoredMessage:
37
38
  token_usage: dict[str, int] | None = None
38
39
  cost: float | None = None
39
40
  response_time: float | None = None
40
- forwarded_from: list[str] | None = None
41
41
 
42
42
 
43
43
  class StoredConversation:
@@ -98,10 +98,10 @@ class StorageProvider:
98
98
  content: str,
99
99
  role: str,
100
100
  name: str | None = None,
101
+ parent_id: str | None = None,
101
102
  cost_info: TokenCost | None = None,
102
103
  model: str | None = None,
103
104
  response_time: float | None = None,
104
- forwarded_from: list[str] | None = None,
105
105
  provider_name: str | None = None,
106
106
  provider_response_id: str | None = None,
107
107
  messages: str | None = None,
@@ -144,6 +144,87 @@ class StorageProvider:
144
144
  """
145
145
  return None
146
146
 
147
+ async def get_conversation_messages(
148
+ self,
149
+ conversation_id: str,
150
+ *,
151
+ include_ancestors: bool = False,
152
+ ) -> list[ChatMessage[str]]:
153
+ """Get all messages for a conversation.
154
+
155
+ Args:
156
+ conversation_id: ID of the conversation
157
+ include_ancestors: If True, also include messages from ancestor
158
+ conversations (following parent_id chain). Useful for forked
159
+ conversations where you want the full history.
160
+
161
+ Returns:
162
+ List of messages ordered by timestamp.
163
+ """
164
+ msg = f"{self.__class__.__name__} does not support getting conversation messages"
165
+ raise NotImplementedError(msg)
166
+
167
+ async def get_message(
168
+ self,
169
+ message_id: str,
170
+ ) -> ChatMessage[str] | None:
171
+ """Get a single message by ID.
172
+
173
+ Args:
174
+ message_id: ID of the message
175
+
176
+ Returns:
177
+ The message if found, None otherwise.
178
+ """
179
+ return None
180
+
181
+ async def get_message_ancestry(
182
+ self,
183
+ message_id: str,
184
+ ) -> list[ChatMessage[str]]:
185
+ """Get the ancestry chain of a message.
186
+
187
+ Traverses the parent_id chain to build full history leading to this message.
188
+ Useful for forked conversations where you need context from the fork point.
189
+
190
+ Args:
191
+ message_id: ID of the message to get ancestry for
192
+
193
+ Returns:
194
+ List of messages from oldest ancestor to the specified message.
195
+ """
196
+ msg = f"{self.__class__.__name__} does not support message ancestry"
197
+ raise NotImplementedError(msg)
198
+
199
+ async def fork_conversation(
200
+ self,
201
+ *,
202
+ source_conversation_id: str,
203
+ new_conversation_id: str,
204
+ fork_from_message_id: str | None = None,
205
+ new_agent_name: str | None = None,
206
+ ) -> str | None:
207
+ """Fork a conversation at a specific point.
208
+
209
+ Creates a new conversation that branches from the source conversation.
210
+ The new conversation's first message will have parent_id pointing to
211
+ the fork point, allowing history traversal.
212
+
213
+ Args:
214
+ source_conversation_id: ID of the conversation to fork from
215
+ new_conversation_id: ID for the new forked conversation
216
+ fork_from_message_id: Message ID to fork from. If None, forks from
217
+ the last message in the source conversation.
218
+ new_agent_name: Agent name for the new conversation. If None,
219
+ inherits from source.
220
+
221
+ Returns:
222
+ The message_id of the fork point (the parent for new messages),
223
+ or None if the source conversation is empty.
224
+ """
225
+ msg = f"{self.__class__.__name__} does not support forking conversations"
226
+ raise NotImplementedError(msg)
227
+
147
228
  async def log_command(
148
229
  self,
149
230
  *,
@@ -308,3 +389,104 @@ class StorageProvider:
308
389
  Tuple of (conversation count, message count)
309
390
  """
310
391
  raise NotImplementedError
392
+
393
+ async def delete_conversation_messages(
394
+ self,
395
+ conversation_id: str,
396
+ ) -> int:
397
+ """Delete all messages for a conversation.
398
+
399
+ Used for compaction - removes existing messages so they can be
400
+ replaced with compacted versions.
401
+
402
+ Args:
403
+ conversation_id: ID of the conversation to clear
404
+
405
+ Returns:
406
+ Number of messages deleted
407
+ """
408
+ msg = f"{self.__class__.__name__} does not support deleting messages"
409
+ raise NotImplementedError(msg)
410
+
411
+ # Project methods
412
+
413
+ async def save_project(self, project: ProjectData) -> None:
414
+ """Save or update a project.
415
+
416
+ Args:
417
+ project: Project data to persist
418
+ """
419
+ msg = f"{self.__class__.__name__} does not support project storage"
420
+ raise NotImplementedError(msg)
421
+
422
+ async def get_project(self, project_id: str) -> ProjectData | None:
423
+ """Get a project by ID.
424
+
425
+ Args:
426
+ project_id: Project identifier
427
+
428
+ Returns:
429
+ Project data if found, None otherwise
430
+ """
431
+ msg = f"{self.__class__.__name__} does not support project storage"
432
+ raise NotImplementedError(msg)
433
+
434
+ async def get_project_by_worktree(self, worktree: str) -> ProjectData | None:
435
+ """Get a project by worktree path.
436
+
437
+ Args:
438
+ worktree: Absolute path to the project worktree
439
+
440
+ Returns:
441
+ Project data if found, None otherwise
442
+ """
443
+ msg = f"{self.__class__.__name__} does not support project storage"
444
+ raise NotImplementedError(msg)
445
+
446
+ async def get_project_by_name(self, name: str) -> ProjectData | None:
447
+ """Get a project by friendly name.
448
+
449
+ Args:
450
+ name: Project name
451
+
452
+ Returns:
453
+ Project data if found, None otherwise
454
+ """
455
+ msg = f"{self.__class__.__name__} does not support project storage"
456
+ raise NotImplementedError(msg)
457
+
458
+ async def list_projects(
459
+ self,
460
+ limit: int | None = None,
461
+ ) -> list[ProjectData]:
462
+ """List all projects, ordered by last_active descending.
463
+
464
+ Args:
465
+ limit: Maximum number of projects to return
466
+
467
+ Returns:
468
+ List of project data objects
469
+ """
470
+ msg = f"{self.__class__.__name__} does not support project storage"
471
+ raise NotImplementedError(msg)
472
+
473
+ async def delete_project(self, project_id: str) -> bool:
474
+ """Delete a project.
475
+
476
+ Args:
477
+ project_id: Project identifier
478
+
479
+ Returns:
480
+ True if project was deleted, False if not found
481
+ """
482
+ msg = f"{self.__class__.__name__} does not support project storage"
483
+ raise NotImplementedError(msg)
484
+
485
+ async def touch_project(self, project_id: str) -> None:
486
+ """Update project's last_active timestamp.
487
+
488
+ Args:
489
+ project_id: Project identifier
490
+ """
491
+ msg = f"{self.__class__.__name__} does not support project storage"
492
+ raise NotImplementedError(msg)