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
@@ -0,0 +1,386 @@
1
+ # OpenCode Storage Architecture
2
+
3
+ This document explains how OpenCode persists conversation data to the filesystem.
4
+
5
+ ## Directory Structure
6
+
7
+ ```
8
+ ~/.local/share/opencode/
9
+ ├── storage/
10
+ │ ├── message/ # Message metadata by session
11
+ │ │ └── {sessionID}/
12
+ │ │ └── {messageID}.json # Message metadata
13
+ │ ├── part/ # Message content parts
14
+ │ │ └── {messageID}/
15
+ │ │ └── {partID}.json # Content blocks (text, tool_use, etc.)
16
+ │ ├── session/ # Session metadata by project
17
+ │ │ ├── global/ # Sessions not tied to a project
18
+ │ │ │ └── {sessionID}.json # Session metadata
19
+ │ │ └── {projectID}/ # Project-specific sessions
20
+ │ │ └── {sessionID}.json # Session metadata
21
+ │ ├── session_diff/ # Session diffs (unused?)
22
+ │ ├── session_share/ # Shared sessions (unused?)
23
+ │ └── project/ # Project metadata
24
+ ├── snapshot/ # Project snapshots
25
+ ├── log/ # Application logs
26
+ └── auth.json # Authentication credentials
27
+ ```
28
+
29
+ ## Core Concepts
30
+
31
+ ### 1. Storage Model: Normalized Database on Filesystem
32
+
33
+ OpenCode uses a **normalized relational model** stored as JSON files:
34
+ - **Sessions** → Metadata about conversations
35
+ - **Messages** → Message-level metadata (role, time, agent)
36
+ - **Parts** → Content blocks within messages (text, tool_use, tool_result)
37
+
38
+ This is fundamentally different from Claude Code's append-only JSONL approach.
39
+
40
+ ### 2. ID Format
41
+
42
+ All IDs use a custom format with prefixes:
43
+ - **Session**: `ses_{random}` (e.g., `ses_4afbda00cffeVl5YERm4op7JEG`)
44
+ - **Message**: `msg_{random}` (e.g., `msg_b50425ff7001vgFiMUNFzLtCda`)
45
+ - **Part**: `prt_{random}` (e.g., `prt_b50425ff7002egcP330jM5wQcU`)
46
+ - **Project**: SHA1 hash of directory path (e.g., `486ce75a8fddd4372018ab816ac62d8004dc52fd`)
47
+
48
+ The random portion appears to be a timestamp-based identifier.
49
+
50
+ ### 3. Projects
51
+
52
+ **Project ID**: SHA1 hash of the absolute directory path
53
+
54
+ Example:
55
+ ```bash
56
+ echo -n "/home/phil65/dev/oss/agentpool" | sha1sum
57
+ # → 486ce75a8fddd4372018ab816ac62d8004dc52fd
58
+ ```
59
+
60
+ **Special Project**: `global`
61
+ - Used for sessions not tied to a specific directory
62
+ - Sessions created from home directory or no working directory
63
+
64
+ **Storage**:
65
+ - Session files: `storage/session/{projectID}/{sessionID}.json`
66
+ - Global sessions: `storage/session/global/{sessionID}.json`
67
+
68
+ ### 4. Sessions
69
+
70
+ **Session File**: `storage/session/{projectID}/{sessionID}.json`
71
+
72
+ ```json
73
+ {
74
+ "id": "ses_4afbda00cffeVl5YERm4op7JEG",
75
+ "version": "1.0.193",
76
+ "projectID": "486ce75a8fddd4372018ab816ac62d8004dc52fd",
77
+ "directory": "/home/phil65/dev/oss/agentpool",
78
+ "title": "Claude capabilities overview",
79
+ "time": {
80
+ "created": 1766578085876,
81
+ "updated": 1766578154946
82
+ },
83
+ "summary": {
84
+ "additions": 0,
85
+ "deletions": 0,
86
+ "files": 0
87
+ }
88
+ }
89
+ ```
90
+
91
+ **Fields**:
92
+ - `id`: Session identifier
93
+ - `version`: OpenCode version that created the session
94
+ - `projectID`: SHA1 hash of directory or "global"
95
+ - `directory`: Absolute path to working directory
96
+ - `title`: Auto-generated summary of session topic
97
+ - `time.created`: Unix timestamp (milliseconds)
98
+ - `time.updated`: Unix timestamp (milliseconds)
99
+ - `summary`: File change statistics
100
+
101
+ ### 5. Messages
102
+
103
+ **Message File**: `storage/message/{sessionID}/{messageID}.json`
104
+
105
+ ```json
106
+ {
107
+ "id": "msg_b50425ff7001vgFiMUNFzLtCda",
108
+ "sessionID": "ses_4afbda00cffeVl5YERm4op7JEG",
109
+ "role": "user",
110
+ "time": {
111
+ "created": 1766578085884
112
+ },
113
+ "summary": {
114
+ "title": "Exploring available tools",
115
+ "diffs": []
116
+ },
117
+ "agent": "build",
118
+ "model": {
119
+ "providerID": "anthropic",
120
+ "modelID": "claude-opus-4-5-20251101"
121
+ }
122
+ }
123
+ ```
124
+
125
+ **Fields**:
126
+ - `id`: Message identifier
127
+ - `sessionID`: Parent session
128
+ - `role`: "user" | "assistant"
129
+ - `time.created`: Unix timestamp (milliseconds)
130
+ - `summary.title`: Auto-generated message summary
131
+ - `summary.diffs`: File changes in this message
132
+ - `agent`: Agent name (e.g., "build", custom agent names)
133
+ - `model`: Model configuration (for assistant messages)
134
+ - `providerID`: "anthropic", "openai", etc.
135
+ - `modelID`: Full model identifier
136
+
137
+ ### 6. Parts (Message Content)
138
+
139
+ **Part File**: `storage/part/{messageID}/{partID}.json`
140
+
141
+ Parts represent the actual content blocks within a message.
142
+
143
+ #### Text Part
144
+ ```json
145
+ {
146
+ "id": "prt_b50425ff7002egcP330jM5wQcU",
147
+ "sessionID": "ses_4afbda00cffeVl5YERm4op7JEG",
148
+ "messageID": "msg_b50425ff7001vgFiMUNFzLtCda",
149
+ "type": "text",
150
+ "text": "what tools do you have?"
151
+ }
152
+ ```
153
+
154
+ #### Tool Use Part
155
+ ```json
156
+ {
157
+ "id": "prt_...",
158
+ "sessionID": "ses_...",
159
+ "messageID": "msg_...",
160
+ "type": "tool_use",
161
+ "name": "read_file",
162
+ "input": {
163
+ "path": "src/main.py"
164
+ }
165
+ }
166
+ ```
167
+
168
+ #### Tool Result Part
169
+ ```json
170
+ {
171
+ "id": "prt_...",
172
+ "sessionID": "ses_...",
173
+ "messageID": "msg_...",
174
+ "type": "tool_result",
175
+ "tool_use_id": "toolu_...",
176
+ "content": "file contents here..."
177
+ }
178
+ ```
179
+
180
+ **Part Types**:
181
+ - `text`: Text content
182
+ - `tool_use`: Tool invocation
183
+ - `tool_result`: Tool execution result
184
+ - `thinking`: Claude's thinking process (extended thinking)
185
+ - `image`: Image content (base64 or URL)
186
+
187
+ ### 7. Message Flow
188
+
189
+ Unlike Claude Code's linked list, OpenCode doesn't store parent-child relationships explicitly in the storage layer. The conversation flow is determined by:
190
+
191
+ 1. **Message order**: Files in `storage/message/{sessionID}/` directory
192
+ 2. **Timestamp**: `time.created` field determines chronological order
193
+ 3. **No parent references**: Must reconstruct flow from timestamps
194
+
195
+ To read a conversation:
196
+ ```python
197
+ # 1. List all message files in session directory
198
+ messages = list_files(f"storage/message/{session_id}/")
199
+
200
+ # 2. Read each message
201
+ for msg_file in messages:
202
+ msg = load_json(msg_file)
203
+ parts = load_parts(f"storage/part/{msg['id']}/")
204
+ # Combine message + parts
205
+ ```
206
+
207
+ ## Key Differences from Claude Code
208
+
209
+ | Aspect | OpenCode | Claude Code |
210
+ |--------|----------|-------------|
211
+ | **Format** | Normalized JSON files | Append-only JSONL |
212
+ | **Structure** | Relational (sessions → messages → parts) | Linear log with parent refs |
213
+ | **Message Flow** | Timestamp-based ordering | Explicit parent-child links |
214
+ | **Updates** | Files can be updated in place | Append-only, immutable |
215
+ | **Branches** | Not supported | Sidechains with `isSidechain` flag |
216
+ | **Projects** | SHA1 hash of directory | URL-encoded path |
217
+ | **IDs** | Custom prefixed format | UUIDs or short hex |
218
+ | **Content** | Separated into parts | Inline in message entry |
219
+
220
+ ## Storage Provider Implementation
221
+
222
+ ### Key Responsibilities
223
+
224
+ 1. **Path Management**
225
+ - Hash directory paths to project IDs
226
+ - Organize sessions by project
227
+ - Handle "global" project for unscoped sessions
228
+
229
+ 2. **Message Reconstruction**
230
+ - Load message metadata from `message/` directory
231
+ - Load content parts from `part/` directory
232
+ - Combine into unified message representation
233
+
234
+ 3. **Conversation Queries**
235
+ - List sessions for a project
236
+ - Get message count and statistics
237
+ - Retrieve messages in chronological order
238
+
239
+ 4. **Format Conversion**
240
+ - OpenCode format → `ChatMessage` (for agentpool)
241
+ - `ChatMessage` → OpenCode format (for persistence)
242
+
243
+ ### Challenges
244
+
245
+ 1. **No Parent Links**
246
+ - Cannot trace message ancestry efficiently
247
+ - Must rely on timestamps for ordering
248
+ - Forking/branching not supported
249
+
250
+ 2. **Scattered Data**
251
+ - Each message requires multiple file reads
252
+ - Parts are in separate directories
253
+ - No atomic transactions across files
254
+
255
+ 3. **No Versioning**
256
+ - Files can be updated in place
257
+ - No history of edits
258
+ - No way to detect concurrent modifications
259
+
260
+ ## Data Consistency
261
+
262
+ ### File Organization
263
+ - Messages grouped by session in directories
264
+ - Parts grouped by message in directories
265
+ - Sessions grouped by project in directories
266
+
267
+ ### Atomic Operations
268
+ - Individual JSON file writes are atomic
269
+ - No atomicity across multiple files
270
+ - No transaction support
271
+
272
+ ### Concurrent Access
273
+ - No locking mechanism
274
+ - Last write wins on conflicts
275
+ - Reading while writing may see partial state
276
+
277
+ ## Integration Points
278
+
279
+ ### With Agentpool
280
+ - Implements `StorageProvider` protocol
281
+ - Converts between OpenCode format and domain models
282
+ - Enables conversation persistence
283
+
284
+ ### Missing Features
285
+ - **Todos/Plans**: No built-in todo tracking
286
+ - **File History**: Separate from message storage
287
+ - **Branching**: No conversation forking support
288
+ - **Ancestry**: No parent-child relationships
289
+
290
+ ## Usage Patterns
291
+
292
+ ### Reading a Session
293
+ ```python
294
+ provider = OpenCodeStorageProvider()
295
+
296
+ # 1. Get session metadata
297
+ session_file = f"~/.local/share/opencode/storage/session/{project_id}/{session_id}.json"
298
+ session = load_json(session_file)
299
+
300
+ # 2. List messages in session
301
+ message_dir = f"~/.local/share/opencode/storage/message/{session_id}/"
302
+ message_files = list_files(message_dir)
303
+
304
+ # 3. Load each message + parts
305
+ messages = []
306
+ for msg_file in sorted(message_files): # Sort by timestamp in ID
307
+ msg = load_json(msg_file)
308
+
309
+ # Load parts
310
+ part_dir = f"~/.local/share/opencode/storage/part/{msg['id']}/"
311
+ parts = [load_json(p) for p in list_files(part_dir)]
312
+
313
+ messages.append(combine(msg, parts))
314
+ ```
315
+
316
+ ### Writing a Message
317
+ ```python
318
+ # 1. Create message metadata
319
+ message = {
320
+ "id": f"msg_{generate_id()}",
321
+ "sessionID": session_id,
322
+ "role": "user",
323
+ "time": {"created": time_ms()},
324
+ "summary": {"title": "...", "diffs": []},
325
+ "agent": "default",
326
+ }
327
+ write_json(f"storage/message/{session_id}/{message['id']}.json", message)
328
+
329
+ # 2. Create parts
330
+ for part in content_parts:
331
+ part_data = {
332
+ "id": f"prt_{generate_id()}",
333
+ "sessionID": session_id,
334
+ "messageID": message['id'],
335
+ "type": part['type'],
336
+ **part['data']
337
+ }
338
+ write_json(f"storage/part/{message['id']}/{part_data['id']}.json", part_data)
339
+ ```
340
+
341
+ ## Design Rationale
342
+
343
+ ### Why Normalized Structure?
344
+ - **Flexibility**: Can update individual components
345
+ - **Modularity**: Parts can be processed independently
346
+ - **Extensibility**: Easy to add new part types
347
+
348
+ ### Why Separate Parts?
349
+ - **Streaming**: Can load message metadata without content
350
+ - **Lazy Loading**: Only load parts when needed
351
+ - **Type Safety**: Each part type has specific schema
352
+
353
+ ### Why SHA1 for Project ID?
354
+ - **Deterministic**: Same path always gives same ID
355
+ - **Collision-resistant**: Very unlikely hash collisions
356
+ - **Path-independent**: ID doesn't reveal directory structure
357
+
358
+ ### Drawbacks
359
+ - **Performance**: Many small files, lots of I/O
360
+ - **Consistency**: No atomic multi-file operations
361
+ - **Complexity**: More complex than append-only log
362
+ - **No History**: Can't track conversation evolution
363
+
364
+ ## Future Considerations
365
+
366
+ ### Performance
367
+ - Consider SQLite for better query performance
368
+ - Index sessions by project and timestamp
369
+ - Cache frequently accessed metadata
370
+
371
+ ### Consistency
372
+ - Implement write-ahead logging
373
+ - Add transaction support
374
+ - Version individual files
375
+
376
+ ### Features
377
+ - Add parent-child message links
378
+ - Support conversation branching
379
+ - Track message edit history
380
+ - Implement proper locking
381
+
382
+ ---
383
+
384
+ **Related Files:**
385
+ - Implementation: [`provider.py`](./provider.py)
386
+ - Base Protocol: [`../base.py`](../base.py)
@@ -0,0 +1,16 @@
1
+ """OpenCode storage provider.
2
+
3
+ This package implements the storage backend compatible with OpenCode's
4
+ normalized JSON file format.
5
+
6
+ See ARCHITECTURE.md for detailed documentation of the storage format and
7
+ design decisions.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from agentpool_storage.opencode_provider.provider import OpenCodeStorageProvider
13
+
14
+ __all__ = [
15
+ "OpenCodeStorageProvider",
16
+ ]