@scemoon/cdh 1.0.4 → 1.0.6

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 (269) hide show
  1. package/ai-dlc-skill/SKILL.md +4 -2
  2. package/ai-dlc-skill/agents/master.md +6 -0
  3. package/ai-dlc-skill/agents/plan-agent.md +2 -2
  4. package/ai-dlc-skill/agents/understand-agent.md +4 -4
  5. package/ai-dlc-skill/agents/verify-agent.md +1 -1
  6. package/ai-dlc-skill/architecture/project-structure.md +10 -10
  7. package/ai-dlc-skill/brownfield/README.md +2 -2
  8. package/ai-dlc-skill/brownfield/generate-context.sh +3 -3
  9. package/ai-dlc-skill/brownfield/scripts/extract-api.sh +5 -5
  10. package/ai-dlc-skill/components/backend.md +1 -1
  11. package/ai-dlc-skill/components/web.md +2 -2
  12. package/ai-dlc-skill/contracts/README.md +3 -3
  13. package/ai-dlc-skill/cross-tool/cline/export.sh +1 -1
  14. package/ai-dlc-skill/cross-tool/copilot/export.sh +1 -1
  15. package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +7 -3
  16. package/ai-dlc-skill/cross-tool/onecode/export.sh +14 -8
  17. package/ai-dlc-skill/phases/deliver/lifecycle.md +6 -6
  18. package/ai-dlc-skill/phases/deliver/prompt.md +2 -2
  19. package/ai-dlc-skill/phases/deliver/rules.md +1 -1
  20. package/ai-dlc-skill/phases/plan/lifecycle.md +11 -11
  21. package/ai-dlc-skill/phases/plan/prompt.md +6 -6
  22. package/ai-dlc-skill/phases/plan/rules.md +1 -1
  23. package/ai-dlc-skill/phases/understand/entry.md +1 -1
  24. package/ai-dlc-skill/phases/understand/lifecycle.md +7 -7
  25. package/ai-dlc-skill/phases/understand/prompt.md +8 -8
  26. package/ai-dlc-skill/phases/understand/rules.md +5 -5
  27. package/ai-dlc-skill/phases/verify/lifecycle.md +24 -24
  28. package/ai-dlc-skill/phases/verify/prompt.md +8 -8
  29. package/ai-dlc-skill/phases/verify/rules.md +9 -9
  30. package/ai-dlc-skill/providers/README.md +2 -2
  31. package/ai-dlc-skill/providers/aliyun/preview.yaml +2 -2
  32. package/ai-dlc-skill/providers/tcb/preview.yaml +2 -2
  33. package/ai-dlc-skill/requirements.md +7 -7
  34. package/ai-dlc-skill/skill.yaml +4 -4
  35. package/ai-dlc-skill/templates/integration/CHANGELOG.md +2 -2
  36. package/ai-dlc-skill/templates/integration/contract-diff.md +7 -7
  37. package/ai-dlc-skill/templates/integration/contract-spec.md +7 -7
  38. package/ai-dlc-skill/templates/integration/runtime-contract.yaml +1 -1
  39. package/ai-dlc-skill/templates/project/README.md +18 -19
  40. package/ai-dlc-skill/templates/project/template.md +21 -21
  41. package/ai-dlc-skill/walkthrough/collect.sh +1 -1
  42. package/ai-dlc-skill/walkthrough/template.md +2 -2
  43. package/ai-dlc-skill/workflows/ai-dlc.yaml +12 -12
  44. package/ai-dlc-skill/workflows/deployment.yaml +4 -4
  45. package/cdh/__pycache__/cdh_mcp_injector.cpython-314.pyc +0 -0
  46. package/cdh/__pycache__/cdh_mcp_loader.cpython-314.pyc +0 -0
  47. package/cdh/__pycache__/cdh_mcp_manager.cpython-314.pyc +0 -0
  48. package/cdh/__pycache__/cdh_session_aggregator.cpython-314.pyc +0 -0
  49. package/cdh/__pycache__/cdh_skill_loader.cpython-314.pyc +0 -0
  50. package/cdh/__pycache__/cdh_skill_manager.cpython-314.pyc +0 -0
  51. package/cdh/__pycache__/cli.cpython-313.pyc +0 -0
  52. package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
  53. package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
  54. package/cdh/cdh_mcp_injector.py +115 -0
  55. package/cdh/cdh_mcp_loader.py +67 -0
  56. package/cdh/cdh_mcp_manager.py +73 -0
  57. package/cdh/cdh_session_aggregator.py +258 -0
  58. package/cdh/cdh_skill_manager.py +105 -0
  59. package/cdh/cli.py +64 -4
  60. package/cdh/scaffold.py +192 -41
  61. package/onecode/__init__.py +1 -1
  62. package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
  63. package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
  64. package/onecode/__pycache__/config.cpython-314.pyc +0 -0
  65. package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
  66. package/onecode/__pycache__/migrate.cpython-314.pyc +0 -0
  67. package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
  68. package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
  69. package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
  70. package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
  71. package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
  72. package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
  73. package/onecode/agent/__pycache__/project_doc.cpython-314.pyc +0 -0
  74. package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
  75. package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
  76. package/onecode/agent/agents/types.py +47 -164
  77. package/onecode/agent/attachments.py +0 -1
  78. package/onecode/agent/cdh_loader.py +17 -25
  79. package/onecode/agent/context.py +8 -3
  80. package/onecode/agent/engine.py +835 -220
  81. package/onecode/agent/hooks.py +1 -1
  82. package/onecode/agent/onecode_agent_acp.py +460 -47
  83. package/onecode/agent/onecode_agent_acp.py.bak +2447 -0
  84. package/onecode/agent/permissions.py +0 -1
  85. package/onecode/agent/project_doc.py +19 -0
  86. package/onecode/agent/session.py +17 -3
  87. package/onecode/agent/snapshot.py +3 -4
  88. package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
  89. package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
  90. package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
  91. package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
  92. package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
  93. package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
  94. package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
  95. package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
  96. package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
  97. package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
  98. package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
  99. package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
  100. package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
  101. package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
  102. package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
  103. package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
  104. package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
  105. package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
  106. package/onecode/agent/tools/agent_tools.py +4 -6
  107. package/onecode/agent/tools/apply_patch_tool.py +2 -8
  108. package/onecode/agent/tools/bash_tool.py +32 -12
  109. package/onecode/agent/tools/communication_tools.py +4 -5
  110. package/onecode/agent/tools/config_tool.py +3 -3
  111. package/onecode/agent/tools/cron_tools.py +4 -5
  112. package/onecode/agent/tools/file_ops.py +37 -3
  113. package/onecode/agent/tools/file_tools.py +10 -11
  114. package/onecode/agent/tools/git_tools.py +2 -2
  115. package/onecode/agent/tools/lsp_tools.py +2 -2
  116. package/onecode/agent/tools/mcp_tools.py +3 -5
  117. package/onecode/agent/tools/permission_handler.py +1 -1
  118. package/onecode/agent/tools/protocol.py +1 -1
  119. package/onecode/agent/tools/registry.py +69 -2
  120. package/onecode/agent/tools/sandbox.py +110 -16
  121. package/onecode/agent/tools/skill_tools.py +2 -2
  122. package/onecode/agent/tools/todo_tools.py +38 -19
  123. package/onecode/agent/tools/web_tools.py +6 -7
  124. package/onecode/builtin_skills/agent-browser/SKILL.md +212 -0
  125. package/onecode/builtin_skills/agent-browser/skill.yaml +8 -0
  126. package/onecode/builtin_skills/skill-creator/SKILL.md +106 -0
  127. package/onecode/builtin_skills/skill-creator/skill.yaml +8 -0
  128. package/onecode/cli.py +186 -6
  129. package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
  130. package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
  131. package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
  132. package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
  133. package/onecode/codebase/chunker.py +0 -5
  134. package/onecode/codebase/indexer.py +14 -1
  135. package/onecode/codebase/retriever.py +13 -2
  136. package/onecode/codebase/storage.py +5 -4
  137. package/onecode/commands.py +81 -6
  138. package/onecode/config.py +41 -12
  139. package/onecode/config_screen.py +7 -3
  140. package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
  141. package/onecode/mcp/manager.py +2 -3
  142. package/onecode/memory/__init__.py +7 -53
  143. package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
  144. package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
  145. package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
  146. package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
  147. package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
  148. package/onecode/memory/backend.py +13 -39
  149. package/onecode/memory/pyramid.py +2 -71
  150. package/onecode/memory/recall.py +38 -3
  151. package/onecode/migrate.py +122 -0
  152. package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
  153. package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
  154. package/onecode/models/messages.py +58 -0
  155. package/onecode/models/provider.py +20 -2
  156. package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
  157. package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
  158. package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
  159. package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
  160. package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
  161. package/onecode/models/providers/anthropic_provider.py +59 -12
  162. package/onecode/models/providers/glm_provider.py +40 -0
  163. package/onecode/models/providers/minimax_provider.py +3 -0
  164. package/onecode/models/providers/minimaxi_provider.py +3 -0
  165. package/onecode/models/providers/ollama_provider.py +3 -0
  166. package/onecode/server/app.py +1 -1
  167. package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
  168. package/onecode/skills/__pycache__/bootstrap.cpython-314.pyc +0 -0
  169. package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
  170. package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
  171. package/onecode/skills/argument_substitution.py +0 -1
  172. package/onecode/skills/bootstrap.py +124 -0
  173. package/onecode/skills/loader.py +35 -53
  174. package/onecode/skills/manager.py +2 -2
  175. package/onecode/storage/activity.py +3 -4
  176. package/onecode/storage/session.py +3 -4
  177. package/onecode/tasks/manager.py +23 -10
  178. package/onecode/trace/tracer.py +2 -3
  179. package/package.json +1 -1
  180. package/pyproject.toml +5 -1
  181. package/run.js +68 -14
  182. package/tui/__pycache__/about.cpython-314.pyc +0 -0
  183. package/tui/__pycache__/app.cpython-313.pyc +0 -0
  184. package/tui/__pycache__/app.cpython-314.pyc +0 -0
  185. package/tui/__pycache__/danger.cpython-314.pyc +0 -0
  186. package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
  187. package/tui/__pycache__/messages.cpython-314.pyc +0 -0
  188. package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
  189. package/tui/__pycache__/paths.cpython-314.pyc +0 -0
  190. package/tui/__pycache__/session_tracker.cpython-314.pyc +0 -0
  191. package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
  192. package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
  193. package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
  194. package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
  195. package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
  196. package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
  197. package/tui/acp/agent.py +125 -58
  198. package/tui/acp/api.py +12 -0
  199. package/tui/acp/messages.py +3 -1
  200. package/tui/acp/prompt.py +0 -57
  201. package/tui/acp/protocol.py +6 -0
  202. package/tui/app.py +11 -33
  203. package/tui/messages.py +2 -0
  204. package/tui/path_complete.py +1 -1
  205. package/tui/paths.py +16 -0
  206. package/tui/screens/__pycache__/agent_modal.cpython-314.pyc +0 -0
  207. package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
  208. package/tui/screens/__pycache__/diff_screen.cpython-314.pyc +0 -0
  209. package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
  210. package/tui/screens/__pycache__/main.cpython-313.pyc +0 -0
  211. package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
  212. package/tui/screens/__pycache__/permissions.cpython-314.pyc +0 -0
  213. package/tui/screens/__pycache__/projects_app.cpython-314.pyc +0 -0
  214. package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
  215. package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
  216. package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
  217. package/tui/screens/__pycache__/tool_content_screen.cpython-314.pyc +0 -0
  218. package/tui/screens/component_picker.py +35 -15
  219. package/tui/screens/diff_screen.py +84 -0
  220. package/tui/screens/log.py +25 -25
  221. package/tui/screens/main.py +48 -26
  222. package/tui/screens/projects.tcss +5 -1
  223. package/tui/screens/projects_app.py +6 -6
  224. package/tui/screens/projects_screen.py +19 -39
  225. package/tui/screens/sessions.py +3 -5
  226. package/tui/session_tracker.py +3 -0
  227. package/tui/settings_schema.py +1 -1
  228. package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
  229. package/tui/visuals/columns.py +1 -2
  230. package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
  231. package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
  232. package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
  233. package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
  234. package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
  235. package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
  236. package/tui/widgets/__pycache__/future_text.cpython-314.pyc +0 -0
  237. package/tui/widgets/__pycache__/grid_select.cpython-314.pyc +0 -0
  238. package/tui/widgets/__pycache__/load_more.cpython-314.pyc +0 -0
  239. package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
  240. package/tui/widgets/__pycache__/markdown_note.cpython-314.pyc +0 -0
  241. package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
  242. package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
  243. package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
  244. package/tui/widgets/__pycache__/project_summary.cpython-314.pyc +0 -0
  245. package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
  246. package/tui/widgets/__pycache__/session_summary.cpython-314.pyc +0 -0
  247. package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
  248. package/tui/widgets/__pycache__/side_bar.cpython-314.pyc +0 -0
  249. package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
  250. package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
  251. package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
  252. package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
  253. package/tui/widgets/agent_response.py +0 -1
  254. package/tui/widgets/agent_thought.py +0 -1
  255. package/tui/widgets/conversation.py +157 -14
  256. package/tui/widgets/diff_view.py +10 -0
  257. package/tui/widgets/load_more.py +24 -0
  258. package/tui/widgets/modified_files.py +86 -12
  259. package/tui/widgets/plan.py +1 -1
  260. package/tui/widgets/project_grid_select.py +9 -2
  261. package/tui/widgets/session_grid_select.py +6 -0
  262. package/tui/widgets/side_bar.py +10 -0
  263. package/tui/widgets/subagent.py +92 -58
  264. package/tui/widgets/subagent_screen.py +638 -115
  265. package/tui/widgets/terminal.py +1 -2
  266. package/tui/widgets/terminal_tool.py +11 -2
  267. package/tui/widgets/tool_call.py +35 -2
  268. package/onecode/memory/offload.py +0 -114
  269. package/onecode/memory/symbolic.py +0 -164
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
 
3
3
  from time import monotonic
4
- from typing import Awaitable, Callable, Iterable
4
+ from typing import Awaitable, Callable
5
5
 
6
6
  from textual.cache import LRUCache
7
7
 
@@ -18,7 +18,6 @@ from textual.strip import Strip
18
18
  from textual.timer import Timer
19
19
 
20
20
  from tui import ansi
21
- from tui.menus import MenuItem
22
21
 
23
22
 
24
23
  # Time required to double tab escape
@@ -11,14 +11,13 @@ from collections import deque
11
11
  from dataclasses import dataclass
12
12
  import struct
13
13
  import termios
14
- from typing import Iterable, Mapping
14
+ from typing import Mapping
15
15
 
16
16
  from textual.content import Content
17
17
  from textual.reactive import var
18
18
 
19
19
  from tui.shell_read import shell_read
20
20
  from tui.widgets.terminal import Terminal
21
- from tui.menus import MenuItem
22
21
 
23
22
 
24
23
  @dataclass
@@ -249,6 +248,16 @@ class TerminalTool(Terminal):
249
248
  break
250
249
  finally:
251
250
  transport.close()
251
+ try:
252
+ write_transport.close()
253
+ except Exception:
254
+ pass
255
+ if self._shell_fd is not None:
256
+ try:
257
+ os.close(self._shell_fd)
258
+ except OSError:
259
+ pass
260
+ self._shell_fd = None
252
261
 
253
262
  self.finalize()
254
263
  return_code = self._return_code = await process.wait()
@@ -136,7 +136,26 @@ class ToolCall(containers.VerticalGroup):
136
136
  super().__init__(id=id, classes=classes)
137
137
 
138
138
  async def update_tool_call(self, tool_call: protocol.ToolCall) -> None:
139
+ old_call = self.tool_call
140
+ old_status = old_call.get("status") if old_call else None
141
+ old_content_len = len(old_call.get("content", [])) if old_call else 0
142
+ new_status = tool_call.get("status")
143
+ new_content = tool_call.get("content", [])
144
+
139
145
  self.tool_call = tool_call
146
+
147
+ # Update header immediately (cheap)
148
+ try:
149
+ self.query_one(ToolCallHeader).update(self.tool_call_header_content)
150
+ except NoMatches:
151
+ pass
152
+
153
+ if old_status != new_status:
154
+ self.check_expand()
155
+
156
+ # Skip full recompose if nothing material changed
157
+ if old_status == new_status and old_content_len == len(new_content):
158
+ return
140
159
  await self.recompose()
141
160
 
142
161
  def get_block_menu(self) -> Iterable[MenuItem]:
@@ -192,10 +211,18 @@ class ToolCall(containers.VerticalGroup):
192
211
  return
193
212
  tool_call = self.tool_call
194
213
  assert tool_call is not None
195
- if tool_call.get("kind", "") == "read":
214
+ kind = tool_call.get("kind", "")
215
+ if kind == "read":
196
216
  return
197
- tool_call_expand = self.app.settings.get("tools.expand", str, expand=False)
217
+
198
218
  status = tool_call.get("status")
219
+
220
+ # write/edit: always expanded
221
+ if kind == "edit":
222
+ self.expanded = True
223
+ return
224
+
225
+ tool_call_expand = self.app.settings.get("tools.expand", str, expand=False)
199
226
  if tool_call_expand == "always":
200
227
  self.expanded = True
201
228
  elif tool_call_expand != "never" and status is not None:
@@ -303,6 +330,12 @@ class ToolCall(containers.VerticalGroup):
303
330
  def _compose_content(
304
331
  self, tool_call_content: list[protocol.ToolCallContent]
305
332
  ) -> ComposeResult:
333
+ # TodoCreate/TodoList/TodoUpdate return trivial "updated" text - skip display
334
+ title = self.tool_call.get("title", "") if self.tool_call else ""
335
+ tool_name = title.split(":")[0] if ":" in title else title
336
+ if tool_name in ("TodoCreate", "TodoList", "TodoUpdate"):
337
+ return
338
+
306
339
  found = False
307
340
  for widget in compose_tool_content(tool_call_content, self.app):
308
341
  yield widget
@@ -1,114 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- import fnmatch
5
- from dataclasses import dataclass
6
- from pathlib import Path
7
- from typing import Optional
8
-
9
-
10
- @dataclass
11
- class OffloadConfig:
12
- enabled: bool = True
13
- mild_ratio: float = 0.5
14
- aggressive_ratio: float = 0.85
15
- mmd_max_token_ratio: float = 0.2
16
-
17
-
18
- class ContextOffloader:
19
- def __init__(
20
- self,
21
- refs_dir: Optional[Path] = None,
22
- config: Optional[OffloadConfig] = None,
23
- ):
24
- from onecode.config import CLOUD_DEV_HARNESS_DIR
25
- self.refs_dir = refs_dir or (CLOUD_DEV_HARNESS_DIR / "memory" / "refs")
26
- self.refs_dir.mkdir(parents=True, exist_ok=True)
27
- self.config = config or OffloadConfig()
28
- self._offloaded_refs: dict[str, Path] = {}
29
-
30
- def should_offload(self, context_length: int, max_tokens: int) -> tuple[bool, str]:
31
- if not self.config.enabled:
32
- return False, "disabled"
33
-
34
- ratio = context_length / max_tokens if max_tokens > 0 else 0
35
-
36
- if ratio >= self.config.aggressive_ratio:
37
- return True, "aggressive"
38
- elif ratio >= self.config.mild_ratio:
39
- return True, "mild"
40
- return False, "none"
41
-
42
- def offload_content(
43
- self,
44
- content: str,
45
- node_id: str,
46
- file_pattern: str = "*.md",
47
- ) -> str:
48
- ext = file_pattern.replace("*", "md")
49
- ref_file = self.refs_dir / f"{node_id}.{ext}"
50
- ref_file.write_text(content, encoding="utf-8")
51
- self._offloaded_refs[node_id] = ref_file
52
- return f"See refs/{node_id}.{ext}"
53
-
54
- def recall_ref(self, node_id: str) -> Optional[str]:
55
- ref_file = self._offloaded_refs.get(node_id)
56
- if ref_file and ref_file.exists():
57
- return ref_file.read_text(encoding="utf-8")
58
- for ext in ["md", "txt", "log"]:
59
- alt_path = self.refs_dir / f"{node_id}.{ext}"
60
- if alt_path.exists():
61
- return alt_path.read_text(encoding="utf-8")
62
- return None
63
-
64
- def list_offloaded(self) -> list[str]:
65
- return list(self._offloaded_refs.keys())
66
-
67
- def cleanup_old_refs(self, max_age_hours: int = 24) -> int:
68
- import time
69
- from datetime import datetime, timezone
70
- removed = 0
71
- for ref_file in self.refs_dir.iterdir():
72
- if ref_file.is_file():
73
- age_hours = (time.time() - ref_file.stat().st_mtime) / 3600
74
- if age_hours > max_age_hours:
75
- ref_file.unlink()
76
- node_id = ref_file.stem
77
- if node_id in self._offloaded_refs:
78
- del self._offloaded_refs[node_id]
79
- removed += 1
80
- return removed
81
-
82
-
83
- class ToolCallExtractor:
84
- PATTERNS = [
85
- re.compile(r'\[TOOL_CALL\]\s*(\w+)\s*:\s*(.+)', re.DOTALL),
86
- re.compile(r'"tool":\s*"(\w+)"[^}]*"args":\s*({.+?})', re.DOTALL),
87
- re.compile(r'(\w+)\((.*?)\)\s*=>\s*(.+)', re.DOTALL),
88
- ]
89
-
90
- @classmethod
91
- def extract_calls(cls, text: str) -> list[dict]:
92
- calls = []
93
- for pattern in cls.PATTERNS:
94
- matches = pattern.finditer(text)
95
- for match in matches:
96
- if pattern == cls.PATTERNS[0]:
97
- calls.append({"tool": match.group(1), "args": match.group(2)})
98
- elif pattern == cls.PATTERNS[1]:
99
- calls.append({"tool": match.group(1), "args": match.group(2)})
100
- else:
101
- calls.append({"tool": match.group(1), "args": match.group(2), "result": match.group(3)})
102
- return calls
103
-
104
- @classmethod
105
- def format_as_mermaid(cls, calls: list[dict]) -> str:
106
- lines = ["graph LR"]
107
- for i, call in enumerate(calls):
108
- node_id = f"C{i:03d}"
109
- tool_name = call.get("tool", "unknown")
110
- lines.append(f" {node_id}[{tool_name}]")
111
- if i > 0:
112
- prev_id = f"C{i-1:03d}"
113
- lines.append(f" {prev_id} --> {node_id}")
114
- return "\n".join(lines)
@@ -1,164 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- import uuid
5
- from dataclasses import dataclass, field
6
- from datetime import datetime, timezone
7
- from enum import Enum
8
- from typing import Optional
9
-
10
-
11
- class NodeType(Enum):
12
- TASK = "task"
13
- ACTION = "action"
14
- RESULT = "result"
15
- ERROR = "error"
16
- CHECKPOINT = "checkpoint"
17
-
18
-
19
- @dataclass
20
- class MermaidNode:
21
- node_id: str
22
- node_type: NodeType
23
- label: str
24
- metadata: dict = field(default_factory=dict)
25
-
26
- def to_mermaid(self) -> str:
27
- type_styles = {
28
- NodeType.TASK: "([,shape=ellipse",
29
- NodeType.ACTION: "[,shape=box",
30
- NodeType.RESULT: "[(,shape=cylinder",
31
- NodeType.ERROR: "([,shape=ellipse,color=red",
32
- NodeType.CHECKPOINT: "[,shape=diamond",
33
- }
34
- style = type_styles.get(self.node_type, "[")
35
- return f" {self.node_id}{style} {self.label}])"
36
-
37
-
38
- @dataclass
39
- class MermaidEdge:
40
- from_node: str
41
- to_node: str
42
- label: Optional[str] = None
43
- style: str = ""
44
-
45
- def to_mermaid(self) -> str:
46
- edge_str = f" {self.from_node} --> {self.to_node}"
47
- if self.label:
48
- edge_str = f" {self.from_node} -->|{self.label}| {self.to_node}"
49
- if self.style:
50
- edge_str += f" -. {self.style} .-> {self.to_node}"
51
- return edge_str
52
-
53
-
54
- class MermaidCanvas:
55
- def __init__(self, task_id: Optional[str] = None):
56
- self.task_id = task_id or str(uuid.uuid4())[:8]
57
- self.nodes: list[MermaidNode] = []
58
- self.edges: list[MermaidEdge] = []
59
- self.refs: dict[str, str] = {}
60
-
61
- def add_node(
62
- self,
63
- node_type: NodeType,
64
- label: str,
65
- metadata: Optional[dict] = None,
66
- ) -> str:
67
- node_id = f"N{len(self.nodes):03d}"
68
- node = MermaidNode(node_id, node_type, label, metadata or {})
69
- self.nodes.append(node)
70
- return node_id
71
-
72
- def add_edge(
73
- self,
74
- from_node: str,
75
- to_node: str,
76
- label: Optional[str] = None,
77
- style: str = "",
78
- ) -> None:
79
- edge = MermaidEdge(from_node, to_node, label, style)
80
- self.edges.append(edge)
81
-
82
- def add_ref(self, node_id: str, content: str) -> None:
83
- self.refs[node_id] = content
84
-
85
- def get_ref(self, node_id: str) -> Optional[str]:
86
- return self.refs.get(node_id)
87
-
88
- def to_mermaid(self) -> str:
89
- if not self.nodes:
90
- return "graph LR\n empty((empty))"
91
-
92
- lines = ["graph LR"]
93
- for node in self.nodes:
94
- lines.append(node.to_mermaid())
95
- for edge in self.edges:
96
- lines.append(edge.to_mermaid())
97
- return "\n".join(lines)
98
-
99
- def to_markdown(self) -> str:
100
- lines = [f"## Task Canvas: {self.task_id}\n"]
101
- lines.append(f"Generated: {datetime.now(timezone.utc).isoformat()}\n")
102
- lines.append("### Mermaid Diagram\n```mermaid")
103
- lines.append(self.to_mermaid())
104
- lines.append("```\n")
105
- if self.refs:
106
- lines.append("### References\n")
107
- for node_id, content in self.refs.items():
108
- lines.append(f"**{node_id}**: {content[:200]}...\n")
109
- return "\n".join(lines)
110
-
111
- @classmethod
112
- def from_mermaid(cls, mermaid_text: str) -> Optional["MermaidCanvas"]:
113
- try:
114
- canvas = cls()
115
- node_pattern = re.compile(r"(\w+)\[(.*?)\]")
116
- for match in node_pattern.finditer(mermaid_text):
117
- node_id, label = match.groups()
118
- if node_id.startswith("N"):
119
- canvas.add_node(NodeType.ACTION, label)
120
- return canvas
121
- except Exception:
122
- return None
123
-
124
-
125
- class SymbolicMemory:
126
- def __init__(self, canvas: Optional[MermaidCanvas] = None):
127
- self.canvas = canvas or MermaidCanvas()
128
- self._verbose_cache: dict[str, str] = {}
129
-
130
- def offload_log(self, node_id: str, verbose_content: str) -> str:
131
- self._verbose_cache[node_id] = verbose_content
132
- self.canvas.add_ref(node_id, f"See ref: {node_id}")
133
- return f"[{node_id}]"
134
-
135
- def recall_log(self, node_id: str) -> Optional[str]:
136
- return self._verbose_cache.get(node_id)
137
-
138
- def create_task_node(self, label: str, metadata: Optional[dict] = None) -> str:
139
- return self.canvas.add_node(NodeType.TASK, label, metadata)
140
-
141
- def create_action_node(self, label: str, metadata: Optional[dict] = None) -> str:
142
- return self.canvas.add_node(NodeType.ACTION, label, metadata)
143
-
144
- def create_result_node(self, label: str, result_content: str, metadata: Optional[dict] = None) -> str:
145
- node_id = self.canvas.add_node(NodeType.RESULT, label, metadata)
146
- self.offload_log(node_id, result_content)
147
- return node_id
148
-
149
- def create_error_node(self, label: str, error_content: str, metadata: Optional[dict] = None) -> str:
150
- node_id = self.canvas.add_node(NodeType.ERROR, label, metadata)
151
- self.offload_log(node_id, error_content)
152
- return node_id
153
-
154
- def link(self, from_id: str, to_id: str, label: Optional[str] = None) -> None:
155
- self.canvas.add_edge(from_id, to_id, label)
156
-
157
- def get_canvas_markdown(self) -> str:
158
- return self.canvas.to_markdown()
159
-
160
- def get_canvas_mermaid(self) -> str:
161
- return self.canvas.to_mermaid()
162
-
163
- def get_ref_content(self, node_id: str) -> Optional[str]:
164
- return self._verbose_cache.get(node_id)