@scemoon/cdh 1.0.6 → 1.0.7

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 (550) hide show
  1. package/package.json +2 -2
  2. package/ai-dlc-skill/SKILL.md +0 -77
  3. package/ai-dlc-skill/agents/deliver-agent.md +0 -24
  4. package/ai-dlc-skill/agents/master.md +0 -50
  5. package/ai-dlc-skill/agents/plan-agent.md +0 -23
  6. package/ai-dlc-skill/agents/understand-agent.md +0 -23
  7. package/ai-dlc-skill/agents/verify-agent.md +0 -27
  8. package/ai-dlc-skill/architecture/project-structure.md +0 -78
  9. package/ai-dlc-skill/brownfield/README.md +0 -30
  10. package/ai-dlc-skill/brownfield/generate-context.sh +0 -64
  11. package/ai-dlc-skill/brownfield/scripts/deps.sh +0 -27
  12. package/ai-dlc-skill/brownfield/scripts/discover.sh +0 -34
  13. package/ai-dlc-skill/brownfield/scripts/extract-api.sh +0 -45
  14. package/ai-dlc-skill/components/backend.md +0 -34
  15. package/ai-dlc-skill/components/desktop.md +0 -30
  16. package/ai-dlc-skill/components/mya.md +0 -29
  17. package/ai-dlc-skill/components/native.md +0 -30
  18. package/ai-dlc-skill/components/tta.md +0 -29
  19. package/ai-dlc-skill/components/web.md +0 -31
  20. package/ai-dlc-skill/components/wxa.md +0 -29
  21. package/ai-dlc-skill/contracts/README.md +0 -25
  22. package/ai-dlc-skill/core/adaptive-flow.md +0 -36
  23. package/ai-dlc-skill/core/security.md +0 -29
  24. package/ai-dlc-skill/cross-tool/cline/export.sh +0 -31
  25. package/ai-dlc-skill/cross-tool/copilot/export.sh +0 -47
  26. package/ai-dlc-skill/cross-tool/cursor/export.sh +0 -32
  27. package/ai-dlc-skill/cross-tool/export.sh +0 -22
  28. package/ai-dlc-skill/cross-tool/onecode/config.yaml.tpl +0 -17
  29. package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +0 -35
  30. package/ai-dlc-skill/cross-tool/onecode/export.sh +0 -54
  31. package/ai-dlc-skill/phases/deliver/entry.md +0 -26
  32. package/ai-dlc-skill/phases/deliver/lifecycle.md +0 -185
  33. package/ai-dlc-skill/phases/deliver/prompt.md +0 -40
  34. package/ai-dlc-skill/phases/deliver/rules.md +0 -61
  35. package/ai-dlc-skill/phases/plan/entry.md +0 -25
  36. package/ai-dlc-skill/phases/plan/lifecycle.md +0 -204
  37. package/ai-dlc-skill/phases/plan/prompt.md +0 -38
  38. package/ai-dlc-skill/phases/plan/rules.md +0 -25
  39. package/ai-dlc-skill/phases/understand/entry.md +0 -26
  40. package/ai-dlc-skill/phases/understand/lifecycle.md +0 -184
  41. package/ai-dlc-skill/phases/understand/prompt.md +0 -41
  42. package/ai-dlc-skill/phases/understand/rules.md +0 -37
  43. package/ai-dlc-skill/phases/verify/entry.md +0 -26
  44. package/ai-dlc-skill/phases/verify/lifecycle.md +0 -192
  45. package/ai-dlc-skill/phases/verify/prompt.md +0 -43
  46. package/ai-dlc-skill/phases/verify/rules.md +0 -73
  47. package/ai-dlc-skill/practices/bdd.md +0 -91
  48. package/ai-dlc-skill/practices/sdd.md +0 -84
  49. package/ai-dlc-skill/practices/tdd.md +0 -95
  50. package/ai-dlc-skill/providers/README.md +0 -114
  51. package/ai-dlc-skill/providers/aliyun/deployment.yaml +0 -66
  52. package/ai-dlc-skill/providers/aliyun/preview.yaml +0 -84
  53. package/ai-dlc-skill/providers/aliyun/provider.yaml +0 -160
  54. package/ai-dlc-skill/providers/tcb/deployment.yaml +0 -69
  55. package/ai-dlc-skill/providers/tcb/preview.yaml +0 -83
  56. package/ai-dlc-skill/providers/tcb/provider.yaml +0 -172
  57. package/ai-dlc-skill/requirements.md +0 -140
  58. package/ai-dlc-skill/skill.yaml +0 -147
  59. package/ai-dlc-skill/templates/integration/CHANGELOG.md +0 -41
  60. package/ai-dlc-skill/templates/integration/asyncapi.yaml +0 -43
  61. package/ai-dlc-skill/templates/integration/contract-diff.md +0 -63
  62. package/ai-dlc-skill/templates/integration/contract-spec.md +0 -49
  63. package/ai-dlc-skill/templates/integration/openapi.yaml +0 -18
  64. package/ai-dlc-skill/templates/integration/runtime-contract.yaml +0 -36
  65. package/ai-dlc-skill/templates/plan/design.md +0 -49
  66. package/ai-dlc-skill/templates/plan/task-list.md +0 -32
  67. package/ai-dlc-skill/templates/project/README.md +0 -126
  68. package/ai-dlc-skill/templates/project/template.md +0 -119
  69. package/ai-dlc-skill/templates/understand/feature.feature +0 -23
  70. package/ai-dlc-skill/templates/understand/intent.md +0 -29
  71. package/ai-dlc-skill/templates/understand/spec-delta.md +0 -47
  72. package/ai-dlc-skill/templates/verify/test-unit.py +0 -27
  73. package/ai-dlc-skill/walkthrough/collect.sh +0 -50
  74. package/ai-dlc-skill/walkthrough/template.md +0 -54
  75. package/ai-dlc-skill/workflows/ai-dlc.yaml +0 -110
  76. package/ai-dlc-skill/workflows/deployment.yaml +0 -199
  77. package/cdh/__init__.py +0 -0
  78. package/cdh/__main__.py +0 -4
  79. package/cdh/__pycache__/__init__.cpython-314.pyc +0 -0
  80. package/cdh/__pycache__/cdh_mcp_injector.cpython-314.pyc +0 -0
  81. package/cdh/__pycache__/cdh_mcp_loader.cpython-314.pyc +0 -0
  82. package/cdh/__pycache__/cdh_mcp_manager.cpython-314.pyc +0 -0
  83. package/cdh/__pycache__/cdh_session_aggregator.cpython-314.pyc +0 -0
  84. package/cdh/__pycache__/cdh_skill_loader.cpython-314.pyc +0 -0
  85. package/cdh/__pycache__/cdh_skill_manager.cpython-314.pyc +0 -0
  86. package/cdh/__pycache__/cli.cpython-313.pyc +0 -0
  87. package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
  88. package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
  89. package/cdh/cdh_mcp_injector.py +0 -115
  90. package/cdh/cdh_mcp_loader.py +0 -67
  91. package/cdh/cdh_mcp_manager.py +0 -73
  92. package/cdh/cdh_session_aggregator.py +0 -258
  93. package/cdh/cdh_skill_manager.py +0 -105
  94. package/cdh/cli.py +0 -807
  95. package/cdh/scaffold.py +0 -728
  96. package/onecode/__init__.py +0 -1
  97. package/onecode/__main__.py +0 -4
  98. package/onecode/__pycache__/__init__.cpython-314.pyc +0 -0
  99. package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
  100. package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
  101. package/onecode/__pycache__/config.cpython-314.pyc +0 -0
  102. package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
  103. package/onecode/__pycache__/migrate.cpython-314.pyc +0 -0
  104. package/onecode/agent/__init__.py +0 -0
  105. package/onecode/agent/__pycache__/__init__.cpython-314.pyc +0 -0
  106. package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
  107. package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
  108. package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
  109. package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
  110. package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
  111. package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
  112. package/onecode/agent/__pycache__/permissions_store.cpython-314.pyc +0 -0
  113. package/onecode/agent/__pycache__/project_doc.cpython-314.pyc +0 -0
  114. package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
  115. package/onecode/agent/agents/__init__.py +0 -29
  116. package/onecode/agent/agents/__pycache__/__init__.cpython-314.pyc +0 -0
  117. package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
  118. package/onecode/agent/agents/types.py +0 -589
  119. package/onecode/agent/attachments.py +0 -112
  120. package/onecode/agent/cdh_loader.py +0 -267
  121. package/onecode/agent/context.py +0 -343
  122. package/onecode/agent/engine.py +0 -2700
  123. package/onecode/agent/hooks.py +0 -86
  124. package/onecode/agent/onecode_agent_acp.py +0 -2743
  125. package/onecode/agent/onecode_agent_acp.py.bak +0 -2447
  126. package/onecode/agent/permissions.py +0 -150
  127. package/onecode/agent/permissions_store.py +0 -62
  128. package/onecode/agent/project_doc.py +0 -19
  129. package/onecode/agent/session.py +0 -195
  130. package/onecode/agent/snapshot.py +0 -144
  131. package/onecode/agent/tools/__init__.py +0 -17
  132. package/onecode/agent/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  133. package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
  134. package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
  135. package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
  136. package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
  137. package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
  138. package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
  139. package/onecode/agent/tools/__pycache__/errors.cpython-314.pyc +0 -0
  140. package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
  141. package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
  142. package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
  143. package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
  144. package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
  145. package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
  146. package/onecode/agent/tools/__pycache__/permissions.cpython-314.pyc +0 -0
  147. package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
  148. package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
  149. package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
  150. package/onecode/agent/tools/__pycache__/schema_validation.cpython-314.pyc +0 -0
  151. package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
  152. package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
  153. package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
  154. package/onecode/agent/tools/agent_tools.py +0 -132
  155. package/onecode/agent/tools/apply_patch_tool.py +0 -138
  156. package/onecode/agent/tools/bash_tool.py +0 -111
  157. package/onecode/agent/tools/communication_tools.py +0 -148
  158. package/onecode/agent/tools/config_tool.py +0 -130
  159. package/onecode/agent/tools/cron_tools.py +0 -178
  160. package/onecode/agent/tools/errors.py +0 -17
  161. package/onecode/agent/tools/file_ops.py +0 -221
  162. package/onecode/agent/tools/file_tools.py +0 -225
  163. package/onecode/agent/tools/git_tools.py +0 -88
  164. package/onecode/agent/tools/lsp_tools.py +0 -404
  165. package/onecode/agent/tools/mcp_tools.py +0 -127
  166. package/onecode/agent/tools/permission_handler.py +0 -46
  167. package/onecode/agent/tools/permissions.py +0 -77
  168. package/onecode/agent/tools/protocol.py +0 -20
  169. package/onecode/agent/tools/registry.py +0 -201
  170. package/onecode/agent/tools/sandbox.py +0 -358
  171. package/onecode/agent/tools/schema_validation.py +0 -100
  172. package/onecode/agent/tools/skill_tools.py +0 -52
  173. package/onecode/agent/tools/todo_tools.py +0 -207
  174. package/onecode/agent/tools/web_tools.py +0 -169
  175. package/onecode/builtin_skills/agent-browser/SKILL.md +0 -212
  176. package/onecode/builtin_skills/agent-browser/skill.yaml +0 -8
  177. package/onecode/builtin_skills/git/SKILL.md +0 -64
  178. package/onecode/builtin_skills/git/skill.yaml +0 -7
  179. package/onecode/builtin_skills/shell/SKILL.md +0 -58
  180. package/onecode/builtin_skills/shell/skill.yaml +0 -8
  181. package/onecode/builtin_skills/skill-creator/SKILL.md +0 -106
  182. package/onecode/builtin_skills/skill-creator/skill.yaml +0 -8
  183. package/onecode/cli.py +0 -935
  184. package/onecode/cloud/__init__.py +0 -0
  185. package/onecode/cloud/base.py +0 -20
  186. package/onecode/codebase/__init__.py +0 -69
  187. package/onecode/codebase/__pycache__/__init__.cpython-314.pyc +0 -0
  188. package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
  189. package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
  190. package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
  191. package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
  192. package/onecode/codebase/__pycache__/tools.cpython-314.pyc +0 -0
  193. package/onecode/codebase/chunker.py +0 -125
  194. package/onecode/codebase/indexer.py +0 -149
  195. package/onecode/codebase/retriever.py +0 -203
  196. package/onecode/codebase/storage.py +0 -131
  197. package/onecode/codebase/tools.py +0 -59
  198. package/onecode/commands.py +0 -596
  199. package/onecode/config.py +0 -314
  200. package/onecode/config_screen.py +0 -600
  201. package/onecode/cron/__init__.py +0 -3
  202. package/onecode/lsp/__init__.py +0 -3
  203. package/onecode/mcp/__init__.py +0 -4
  204. package/onecode/mcp/__pycache__/__init__.cpython-314.pyc +0 -0
  205. package/onecode/mcp/__pycache__/client.cpython-314.pyc +0 -0
  206. package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
  207. package/onecode/mcp/client.py +0 -193
  208. package/onecode/mcp/manager.py +0 -190
  209. package/onecode/memory/__init__.py +0 -49
  210. package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
  211. package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
  212. package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
  213. package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
  214. package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
  215. package/onecode/memory/__pycache__/symbolic.cpython-314.pyc +0 -0
  216. package/onecode/memory/backend.py +0 -159
  217. package/onecode/memory/pyramid.py +0 -133
  218. package/onecode/memory/recall.py +0 -203
  219. package/onecode/migrate.py +0 -122
  220. package/onecode/models/__init__.py +0 -4
  221. package/onecode/models/__pycache__/__init__.cpython-314.pyc +0 -0
  222. package/onecode/models/__pycache__/errors.cpython-314.pyc +0 -0
  223. package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
  224. package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
  225. package/onecode/models/__pycache__/registry.cpython-314.pyc +0 -0
  226. package/onecode/models/errors.py +0 -104
  227. package/onecode/models/messages.py +0 -739
  228. package/onecode/models/provider.py +0 -699
  229. package/onecode/models/providers/__init__.py +0 -17
  230. package/onecode/models/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  231. package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
  232. package/onecode/models/providers/__pycache__/deepseek_provider.cpython-314.pyc +0 -0
  233. package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
  234. package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
  235. package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
  236. package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
  237. package/onecode/models/providers/__pycache__/openai_provider.cpython-314.pyc +0 -0
  238. package/onecode/models/providers/anthropic_provider.py +0 -273
  239. package/onecode/models/providers/deepseek_provider.py +0 -212
  240. package/onecode/models/providers/glm_provider.py +0 -195
  241. package/onecode/models/providers/minimax_provider.py +0 -158
  242. package/onecode/models/providers/minimaxi_provider.py +0 -417
  243. package/onecode/models/providers/ollama_provider.py +0 -100
  244. package/onecode/models/providers/openai_provider.py +0 -237
  245. package/onecode/models/registry.py +0 -212
  246. package/onecode/server/__init__.py +0 -3
  247. package/onecode/server/app.py +0 -224
  248. package/onecode/skills/__init__.py +0 -11
  249. package/onecode/skills/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
  251. package/onecode/skills/__pycache__/bootstrap.cpython-314.pyc +0 -0
  252. package/onecode/skills/__pycache__/create.cpython-314.pyc +0 -0
  253. package/onecode/skills/__pycache__/frontmatter.cpython-314.pyc +0 -0
  254. package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
  255. package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
  256. package/onecode/skills/__pycache__/model.cpython-314.pyc +0 -0
  257. package/onecode/skills/argument_substitution.py +0 -38
  258. package/onecode/skills/bootstrap.py +0 -124
  259. package/onecode/skills/create.py +0 -56
  260. package/onecode/skills/frontmatter.py +0 -33
  261. package/onecode/skills/loader.py +0 -139
  262. package/onecode/skills/manager.py +0 -75
  263. package/onecode/skills/model.py +0 -43
  264. package/onecode/storage/__init__.py +0 -0
  265. package/onecode/storage/activity.py +0 -63
  266. package/onecode/storage/project.py +0 -54
  267. package/onecode/storage/session.py +0 -112
  268. package/onecode/tasks/__init__.py +0 -26
  269. package/onecode/tasks/manager.py +0 -232
  270. package/onecode/tasks/models.py +0 -240
  271. package/onecode/trace/__init__.py +0 -0
  272. package/onecode/trace/tracer.py +0 -173
  273. package/onecode/utils/__init__.py +0 -0
  274. package/onecode/utils/helpers.py +0 -20
  275. package/pyproject.toml +0 -74
  276. package/tui/__init__.py +0 -46
  277. package/tui/__main__.py +0 -4
  278. package/tui/__pycache__/__init__.cpython-314.pyc +0 -0
  279. package/tui/__pycache__/_loop.cpython-314.pyc +0 -0
  280. package/tui/__pycache__/_path_fuzzy_search.cpython-314.pyc +0 -0
  281. package/tui/__pycache__/_path_match.cpython-314.pyc +0 -0
  282. package/tui/__pycache__/about.cpython-314.pyc +0 -0
  283. package/tui/__pycache__/agent.cpython-314.pyc +0 -0
  284. package/tui/__pycache__/agent_schema.cpython-314.pyc +0 -0
  285. package/tui/__pycache__/agents.cpython-314.pyc +0 -0
  286. package/tui/__pycache__/answer.cpython-314.pyc +0 -0
  287. package/tui/__pycache__/app.cpython-313.pyc +0 -0
  288. package/tui/__pycache__/app.cpython-314.pyc +0 -0
  289. package/tui/__pycache__/atomic.cpython-314.pyc +0 -0
  290. package/tui/__pycache__/complete.cpython-314.pyc +0 -0
  291. package/tui/__pycache__/constants.cpython-314.pyc +0 -0
  292. package/tui/__pycache__/conversation_markdown.cpython-314.pyc +0 -0
  293. package/tui/__pycache__/danger.cpython-314.pyc +0 -0
  294. package/tui/__pycache__/db.cpython-314.pyc +0 -0
  295. package/tui/__pycache__/dec.cpython-314.pyc +0 -0
  296. package/tui/__pycache__/directory.cpython-314.pyc +0 -0
  297. package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
  298. package/tui/__pycache__/directory_watcher.cpython-314.pyc +0 -0
  299. package/tui/__pycache__/format_path.cpython-314.pyc +0 -0
  300. package/tui/__pycache__/fuzzy.cpython-314.pyc +0 -0
  301. package/tui/__pycache__/fuzzy_index.cpython-314.pyc +0 -0
  302. package/tui/__pycache__/history.cpython-314.pyc +0 -0
  303. package/tui/__pycache__/jsonrpc.cpython-314.pyc +0 -0
  304. package/tui/__pycache__/menus.cpython-314.pyc +0 -0
  305. package/tui/__pycache__/message_log.cpython-314.pyc +0 -0
  306. package/tui/__pycache__/messages.cpython-314.pyc +0 -0
  307. package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
  308. package/tui/__pycache__/path_filter.cpython-314.pyc +0 -0
  309. package/tui/__pycache__/paths.cpython-314.pyc +0 -0
  310. package/tui/__pycache__/pill.cpython-314.pyc +0 -0
  311. package/tui/__pycache__/platform_commands.cpython-314.pyc +0 -0
  312. package/tui/__pycache__/protocol.cpython-314.pyc +0 -0
  313. package/tui/__pycache__/session_tracker.cpython-314.pyc +0 -0
  314. package/tui/__pycache__/settings.cpython-314.pyc +0 -0
  315. package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
  316. package/tui/__pycache__/shell.cpython-314.pyc +0 -0
  317. package/tui/__pycache__/shell_read.cpython-314.pyc +0 -0
  318. package/tui/__pycache__/slash_command.cpython-314.pyc +0 -0
  319. package/tui/__pycache__/version.cpython-314.pyc +0 -0
  320. package/tui/_loop.py +0 -86
  321. package/tui/_path_fuzzy_search.py +0 -48
  322. package/tui/_path_match.py +0 -141
  323. package/tui/about.py +0 -95
  324. package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
  325. package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
  326. package/tui/acp/__pycache__/encode_tool_call_id.cpython-314.pyc +0 -0
  327. package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
  328. package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
  329. package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
  330. package/tui/acp/agent.py +0 -1002
  331. package/tui/acp/api.py +0 -73
  332. package/tui/acp/encode_tool_call_id.py +0 -12
  333. package/tui/acp/messages.py +0 -203
  334. package/tui/acp/prompt.py +0 -53
  335. package/tui/acp/protocol.py +0 -499
  336. package/tui/agent.py +0 -70
  337. package/tui/agent_schema.py +0 -76
  338. package/tui/agents.py +0 -46
  339. package/tui/ansi/__init__.py +0 -1
  340. package/tui/ansi/__pycache__/__init__.cpython-314.pyc +0 -0
  341. package/tui/ansi/__pycache__/_ansi.cpython-314.pyc +0 -0
  342. package/tui/ansi/__pycache__/_ansi_colors.cpython-314.pyc +0 -0
  343. package/tui/ansi/__pycache__/_control_codes.cpython-314.pyc +0 -0
  344. package/tui/ansi/__pycache__/_keys.cpython-314.pyc +0 -0
  345. package/tui/ansi/__pycache__/_sgr_styles.cpython-314.pyc +0 -0
  346. package/tui/ansi/__pycache__/_stream_parser.cpython-314.pyc +0 -0
  347. package/tui/ansi/_ansi.py +0 -1689
  348. package/tui/ansi/_ansi_colors.py +0 -264
  349. package/tui/ansi/_control_codes.py +0 -37
  350. package/tui/ansi/_keys.py +0 -251
  351. package/tui/ansi/_sgr_styles.py +0 -64
  352. package/tui/ansi/_stream_parser.py +0 -418
  353. package/tui/answer.py +0 -12
  354. package/tui/app.py +0 -1266
  355. package/tui/atomic.py +0 -37
  356. package/tui/cli.py +0 -313
  357. package/tui/code_analyze.py +0 -27
  358. package/tui/complete.py +0 -38
  359. package/tui/constants.py +0 -61
  360. package/tui/conversation_markdown.py +0 -30
  361. package/tui/danger.py +0 -372
  362. package/tui/data/agents/ampcode.com.toml +0 -52
  363. package/tui/data/agents/augmentcode.com.toml +0 -41
  364. package/tui/data/agents/cdh.cloud-dev-harness.toml +0 -23
  365. package/tui/data/agents/claude.com.toml +0 -41
  366. package/tui/data/agents/copilot.github.com.toml +0 -29
  367. package/tui/data/agents/crowai.dev.toml +0 -36
  368. package/tui/data/agents/cursor.com.toml +0 -38
  369. package/tui/data/agents/deepagents.langchain.com +0 -34
  370. package/tui/data/agents/docker.com.toml +0 -60
  371. package/tui/data/agents/geminicli.com.toml +0 -28
  372. package/tui/data/agents/goose.ai.toml +0 -52
  373. package/tui/data/agents/inference.huggingface.co.toml +0 -41
  374. package/tui/data/agents/kimi.com.toml +0 -35
  375. package/tui/data/agents/kiro.dev.toml +0 -43
  376. package/tui/data/agents/onecode.dev.toml +0 -22
  377. package/tui/data/agents/openai.com.toml +0 -53
  378. package/tui/data/agents/openclaw.ai.toml +0 -37
  379. package/tui/data/agents/opencode.ai.toml +0 -39
  380. package/tui/data/agents/openhands.dev.toml +0 -46
  381. package/tui/data/agents/stakpak.dev.toml +0 -62
  382. package/tui/data/agents/vibe.mistral.ai.toml +0 -31
  383. package/tui/data/agents/vtcode.dev.toml +0 -63
  384. package/tui/data/images/frog.png +0 -0
  385. package/tui/data/sounds/question.wav +0 -0
  386. package/tui/data/sounds/turn-over.wav +0 -0
  387. package/tui/db.py +0 -213
  388. package/tui/dec.py +0 -332
  389. package/tui/directory.py +0 -234
  390. package/tui/directory_suggester.py +0 -93
  391. package/tui/directory_watcher.py +0 -231
  392. package/tui/format_path.py +0 -25
  393. package/tui/fuzzy.py +0 -140
  394. package/tui/fuzzy_index.py +0 -275
  395. package/tui/gist.py +0 -33
  396. package/tui/history.py +0 -138
  397. package/tui/jsonrpc.py +0 -578
  398. package/tui/menus.py +0 -14
  399. package/tui/message_log.py +0 -69
  400. package/tui/messages.py +0 -144
  401. package/tui/option_content.py +0 -51
  402. package/tui/os.py +0 -0
  403. package/tui/path_complete.py +0 -217
  404. package/tui/path_filter.py +0 -124
  405. package/tui/paths.py +0 -87
  406. package/tui/pill.py +0 -23
  407. package/tui/platform_commands.py +0 -52
  408. package/tui/prompt/__pycache__/extract.cpython-314.pyc +0 -0
  409. package/tui/prompt/__pycache__/resource.cpython-314.pyc +0 -0
  410. package/tui/prompt/extract.py +0 -19
  411. package/tui/prompt/resource.py +0 -68
  412. package/tui/protocol.py +0 -28
  413. package/tui/screens/__init__.py +0 -0
  414. package/tui/screens/__pycache__/__init__.cpython-314.pyc +0 -0
  415. package/tui/screens/__pycache__/agent_modal.cpython-314.pyc +0 -0
  416. package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
  417. package/tui/screens/__pycache__/diff_screen.cpython-314.pyc +0 -0
  418. package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
  419. package/tui/screens/__pycache__/main.cpython-313.pyc +0 -0
  420. package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
  421. package/tui/screens/__pycache__/permissions.cpython-314.pyc +0 -0
  422. package/tui/screens/__pycache__/projects_app.cpython-314.pyc +0 -0
  423. package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
  424. package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
  425. package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
  426. package/tui/screens/__pycache__/tool_content_screen.cpython-314.pyc +0 -0
  427. package/tui/screens/action_modal.py +0 -94
  428. package/tui/screens/agent_modal.py +0 -175
  429. package/tui/screens/command_edit_modal.py +0 -74
  430. package/tui/screens/component_picker.py +0 -168
  431. package/tui/screens/diff_screen.py +0 -84
  432. package/tui/screens/log.py +0 -553
  433. package/tui/screens/main.py +0 -385
  434. package/tui/screens/main.tcss +0 -655
  435. package/tui/screens/permissions.py +0 -404
  436. package/tui/screens/permissions.tcss +0 -72
  437. package/tui/screens/projects.tcss +0 -80
  438. package/tui/screens/projects_app.py +0 -347
  439. package/tui/screens/projects_screen.py +0 -284
  440. package/tui/screens/session_resume_modal.py +0 -172
  441. package/tui/screens/session_resume_modal.tcss +0 -35
  442. package/tui/screens/sessions.py +0 -164
  443. package/tui/screens/sessions.tcss +0 -119
  444. package/tui/screens/settings.py +0 -254
  445. package/tui/screens/settings.tcss +0 -101
  446. package/tui/screens/store.py +0 -500
  447. package/tui/screens/store.tcss +0 -274
  448. package/tui/screens/tool_content_screen.py +0 -59
  449. package/tui/session_tracker.py +0 -123
  450. package/tui/settings.py +0 -353
  451. package/tui/settings_schema.py +0 -431
  452. package/tui/shell.py +0 -304
  453. package/tui/shell_read.py +0 -42
  454. package/tui/slash_command.py +0 -34
  455. package/tui/tui.tcss +0 -246
  456. package/tui/twitter.py +0 -38
  457. package/tui/version.py +0 -80
  458. package/tui/visuals/__init__.py +0 -0
  459. package/tui/visuals/__pycache__/__init__.cpython-314.pyc +0 -0
  460. package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
  461. package/tui/visuals/columns.py +0 -272
  462. package/tui/widgets/__init__.py +0 -0
  463. package/tui/widgets/__pycache__/__init__.cpython-314.pyc +0 -0
  464. package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
  465. package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
  466. package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
  467. package/tui/widgets/__pycache__/ask_user.cpython-314.pyc +0 -0
  468. package/tui/widgets/__pycache__/condensed_path.cpython-314.pyc +0 -0
  469. package/tui/widgets/__pycache__/context_stats.cpython-314.pyc +0 -0
  470. package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
  471. package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
  472. package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
  473. package/tui/widgets/__pycache__/flash.cpython-314.pyc +0 -0
  474. package/tui/widgets/__pycache__/future_text.cpython-314.pyc +0 -0
  475. package/tui/widgets/__pycache__/grid_select.cpython-314.pyc +0 -0
  476. package/tui/widgets/__pycache__/highlighted_textarea.cpython-314.pyc +0 -0
  477. package/tui/widgets/__pycache__/load_more.cpython-314.pyc +0 -0
  478. package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
  479. package/tui/widgets/__pycache__/markdown_note.cpython-314.pyc +0 -0
  480. package/tui/widgets/__pycache__/menu.cpython-314.pyc +0 -0
  481. package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
  482. package/tui/widgets/__pycache__/non_selectable_label.cpython-314.pyc +0 -0
  483. package/tui/widgets/__pycache__/note.cpython-314.pyc +0 -0
  484. package/tui/widgets/__pycache__/path_search.cpython-314.pyc +0 -0
  485. package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
  486. package/tui/widgets/__pycache__/project_directory_tree.cpython-314.pyc +0 -0
  487. package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
  488. package/tui/widgets/__pycache__/project_summary.cpython-314.pyc +0 -0
  489. package/tui/widgets/__pycache__/prompt.cpython-314.pyc +0 -0
  490. package/tui/widgets/__pycache__/question.cpython-314.pyc +0 -0
  491. package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
  492. package/tui/widgets/__pycache__/session_summary.cpython-314.pyc +0 -0
  493. package/tui/widgets/__pycache__/session_tabs.cpython-314.pyc +0 -0
  494. package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
  495. package/tui/widgets/__pycache__/shell_terminal.cpython-314.pyc +0 -0
  496. package/tui/widgets/__pycache__/side_bar.cpython-314.pyc +0 -0
  497. package/tui/widgets/__pycache__/slash_complete.cpython-314.pyc +0 -0
  498. package/tui/widgets/__pycache__/strike_text.cpython-314.pyc +0 -0
  499. package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
  500. package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
  501. package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
  502. package/tui/widgets/__pycache__/throbber.cpython-314.pyc +0 -0
  503. package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
  504. package/tui/widgets/__pycache__/user_input.cpython-314.pyc +0 -0
  505. package/tui/widgets/acp_content.py +0 -70
  506. package/tui/widgets/agent_response.py +0 -76
  507. package/tui/widgets/agent_thought.py +0 -167
  508. package/tui/widgets/ask_user.py +0 -264
  509. package/tui/widgets/command_pane.py +0 -223
  510. package/tui/widgets/condensed_path.py +0 -115
  511. package/tui/widgets/context_stats.py +0 -66
  512. package/tui/widgets/conversation.py +0 -2641
  513. package/tui/widgets/danger_warning.py +0 -65
  514. package/tui/widgets/diff_view.py +0 -69
  515. package/tui/widgets/directory_input.py +0 -8
  516. package/tui/widgets/flash.py +0 -81
  517. package/tui/widgets/future_text.py +0 -126
  518. package/tui/widgets/grid_select.py +0 -234
  519. package/tui/widgets/highlighted_textarea.py +0 -180
  520. package/tui/widgets/load_more.py +0 -24
  521. package/tui/widgets/mandelbrot.py +0 -294
  522. package/tui/widgets/markdown_note.py +0 -13
  523. package/tui/widgets/menu.py +0 -147
  524. package/tui/widgets/modified_files.py +0 -306
  525. package/tui/widgets/non_selectable_label.py +0 -5
  526. package/tui/widgets/note.py +0 -18
  527. package/tui/widgets/path_search.py +0 -492
  528. package/tui/widgets/plan.py +0 -285
  529. package/tui/widgets/project_directory_tree.py +0 -102
  530. package/tui/widgets/project_grid_select.py +0 -64
  531. package/tui/widgets/project_summary.py +0 -33
  532. package/tui/widgets/prompt.py +0 -795
  533. package/tui/widgets/question.py +0 -360
  534. package/tui/widgets/session_grid_select.py +0 -230
  535. package/tui/widgets/session_summary.py +0 -115
  536. package/tui/widgets/session_tabs.py +0 -188
  537. package/tui/widgets/shell_result.py +0 -35
  538. package/tui/widgets/shell_terminal.py +0 -40
  539. package/tui/widgets/side_bar.py +0 -124
  540. package/tui/widgets/slash_complete.py +0 -235
  541. package/tui/widgets/strike_text.py +0 -70
  542. package/tui/widgets/subagent.py +0 -287
  543. package/tui/widgets/subagent_screen.py +0 -709
  544. package/tui/widgets/terminal.py +0 -576
  545. package/tui/widgets/terminal_tool.py +0 -360
  546. package/tui/widgets/throbber.py +0 -100
  547. package/tui/widgets/tool_call.py +0 -398
  548. package/tui/widgets/user_input.py +0 -23
  549. package/tui/widgets/version.py +0 -5
  550. package/tui/widgets/welcome.py +0 -31
package/tui/acp/agent.py DELETED
@@ -1,1002 +0,0 @@
1
- import asyncio
2
- import logging
3
- import sys
4
-
5
- logger = logging.getLogger("tui.acp.agent")
6
-
7
- import json
8
- import os
9
- from pathlib import Path
10
- from typing import Any, cast, NamedTuple
11
- from copy import deepcopy
12
-
13
- import rich.repr
14
-
15
- from textual.content import Content
16
- from textual.message import Message
17
- from textual.message_pump import MessagePump
18
-
19
-
20
- from tui import jsonrpc
21
- import tui
22
- from tui.agent_schema import Agent as AgentData
23
- from tui.agent import AgentBase, AgentReady, AgentFail
24
- from tui.acp import protocol
25
- from tui.acp import api
26
- from tui.acp.api import API
27
- from tui.acp import messages
28
- from tui.acp.prompt import build as build_prompt
29
- from tui import messages as tui_messages
30
- from tui.db import DB
31
- from tui import constants
32
- from tui.answer import Answer
33
-
34
- PROTOCOL_VERSION = 1
35
-
36
-
37
- class Mode(NamedTuple):
38
- """An agent mode."""
39
-
40
- id: str
41
- name: str
42
- description: str | None
43
-
44
-
45
- @rich.repr.auto
46
- class Agent(AgentBase):
47
- """An agent that speaks the APC (https://agentclientprotocol.com/overview/introduction) protocol."""
48
-
49
- def __init__(
50
- self,
51
- project_root: Path,
52
- agent: AgentData,
53
- session_id: str | None,
54
- session_pk: int | None = None,
55
- ) -> None:
56
- """
57
-
58
- Args:
59
- project_root: Project root path.
60
- command: Command to launch agent.
61
- """
62
- super().__init__(project_root)
63
-
64
- self._agent_data = agent
65
- self.session_id = session_id
66
-
67
- self.server = jsonrpc.Server()
68
- self.server.expose_instance(self)
69
-
70
- self._agent_task: asyncio.Task | None = None
71
- self._task: asyncio.Task | None = None
72
- self._process: asyncio.subprocess.Process | None = None
73
- self.done_event = asyncio.Event()
74
-
75
- self.agent_capabilities: protocol.AgentCapabilities = {
76
- "loadSession": False,
77
- "promptCapabilities": {
78
- "audio": False,
79
- "embeddedContent": True,
80
- "image": True,
81
- },
82
- }
83
- self.auth_methods: list[protocol.AuthMethod] = []
84
- self.session_pk: int | None = session_pk
85
- self._pending_session_data: dict | None = None
86
- self.tool_calls: dict[str, protocol.ToolCall] = {}
87
- self._message_target: MessagePump | None = None
88
-
89
- self._terminal_count: int = 0
90
- self.session_load_info: dict = {}
91
-
92
- @property
93
- def command(self) -> str | None:
94
- """The command used to launch the agent, or `None` if there isn't one."""
95
- acp_command = tui.get_os_matrix(self._agent_data["run_command"])
96
- return acp_command
97
-
98
- @property
99
- def supports_load_session(self) -> bool:
100
- """Does the agent support loading sessions?"""
101
- return self.agent_capabilities.get("loadSession", False)
102
-
103
- def __rich_repr__(self) -> rich.repr.Result:
104
- yield self.project_root_path
105
- yield self.command
106
-
107
- def get_info(self) -> Content:
108
- agent_name = self._agent_data["name"]
109
- return Content(agent_name)
110
-
111
- async def start(self, message_target: MessagePump | None = None) -> None:
112
- """Start the agent."""
113
- self._message_target = message_target
114
- self._agent_task = asyncio.create_task(self._run_agent())
115
-
116
- def send(self, request: jsonrpc.Request) -> None:
117
- """Send a request to the agent.
118
-
119
- This is called automatically, if you go through `self.request`.
120
-
121
- Args:
122
- request: JSONRPC request object.
123
-
124
- """
125
- if self._process is None:
126
- logger.warning(
127
- "Cannot send request %s: agent process is not running",
128
- request.body,
129
- )
130
- return
131
-
132
- logger.info("[client] send: %s", request.body)
133
- if (stdin := self._process.stdin) is not None:
134
- stdin.write(b"%s\n" % request.body_json)
135
-
136
- def request(self) -> jsonrpc.Request:
137
- """Create a request object."""
138
- return API.request(self.send)
139
-
140
- def post_message(self, message: Message) -> bool:
141
- """Post a message to the message target (the Conversation).
142
-
143
- Args:
144
- message: Message object.
145
-
146
- Returns:
147
- `True` if the message was posted successfully, or `False` if it wasn't.
148
- """
149
- if (message_target := self._message_target) is None:
150
- return False
151
- return message_target.post_message(message)
152
-
153
- @jsonrpc.expose("session/update")
154
- def rpc_session_update(
155
- self,
156
- sessionId: str,
157
- update: protocol.SessionUpdate,
158
- _meta: dict[str, Any] | None = None,
159
- ):
160
- """Agent requests an update.
161
-
162
- https://agentclientprotocol.com/protocol/schema
163
- """
164
- status_line: str | None = None
165
- if _meta and (field_meta := _meta.get("field_meta")) is not None:
166
- if (
167
- open_hands_metrics := field_meta.get("openhands.dev/metrics")
168
- ) is not None:
169
- status_line = open_hands_metrics.get("status_line")
170
-
171
- match update:
172
- case {
173
- "sessionUpdate": "user_message_chunk",
174
- "content": {"type": type, "text": text},
175
- }:
176
- if text:
177
- self.post_message(messages.UserMessage(type, text))
178
-
179
- case {
180
- "sessionUpdate": "agent_message_chunk",
181
- "content": {"type": type, "text": text},
182
- }:
183
- self.post_message(messages.Update(type, text))
184
-
185
- case {
186
- "sessionUpdate": "agent_thought_chunk",
187
- "content": {"type": type, "text": text},
188
- }:
189
- self.post_message(messages.Thinking(type, text))
190
-
191
- case {
192
- "sessionUpdate": "tool_call",
193
- "toolCallId": tool_call_id,
194
- }:
195
- logger.debug(
196
- "session/update: tool_call id=%s title=%s kind=%s status=%s content_blocks=%d",
197
- tool_call_id,
198
- update.get('title','?'), update.get('kind','?'),
199
- update.get('status','?'), len(update.get('content',[]) or []),
200
- )
201
- self.tool_calls[tool_call_id] = deepcopy(update)
202
- self.post_message(messages.ToolCall(deepcopy(update)))
203
-
204
- case {"sessionUpdate": "plan", "entries": entries}:
205
- self.post_message(messages.Plan(entries))
206
-
207
- case {
208
- "sessionUpdate": "tool_call_update",
209
- "toolCallId": tool_call_id,
210
- }:
211
- if tool_call_id in self.tool_calls:
212
- current_tool_call = self.tool_calls[tool_call_id]
213
- for key, value in update.items():
214
- if value is not None:
215
- current_tool_call[key] = value
216
-
217
- logger.debug(
218
- "session/update: tool_call_update id=%s status=%s content_blocks=%d",
219
- tool_call_id,
220
- update.get('status','?'),
221
- len(update.get('content',[]) or []),
222
- )
223
-
224
- self.post_message(
225
- messages.ToolCallUpdate(deepcopy(current_tool_call), update)
226
- )
227
- else:
228
- logger.debug(
229
- "session/update: tool_call_update ORPHAN id=%s "
230
- "(no prior tool_call, creating widget from update)",
231
- tool_call_id,
232
- )
233
- current_tool_call: protocol.ToolCall = {
234
- "sessionUpdate": "tool_call",
235
- "toolCallId": tool_call_id,
236
- "title": "Tool call",
237
- }
238
- for key, value in update.items():
239
- if value is not None:
240
- current_tool_call[key] = value
241
-
242
- self.tool_calls[tool_call_id] = current_tool_call
243
- self.post_message(messages.ToolCall(deepcopy(current_tool_call)))
244
-
245
- case {
246
- "sessionUpdate": "usage_update",
247
- }:
248
- self.post_message(messages.ContextUpdate(
249
- used=update.get("used", 0),
250
- size=update.get("size", 0),
251
- ))
252
-
253
- case {
254
- "sessionUpdate": "ask_user",
255
- "question": question,
256
- }:
257
- self.post_message(messages.AskUser(
258
- question=question,
259
- context=update.get("context", ""),
260
- options=update.get("options", []),
261
- questions=update.get("questions", []),
262
- tool_id=update.get("toolId", ""),
263
- ))
264
-
265
- case {
266
- "sessionUpdate": "ask_user",
267
- "questions": questions,
268
- } if questions:
269
- self.post_message(messages.AskUser(
270
- questions=questions,
271
- context=update.get("context", ""),
272
- tool_id=update.get("toolId", ""),
273
- ))
274
-
275
- case {
276
- "sessionUpdate": "available_commands_update",
277
- "availableCommands": available_commands,
278
- }:
279
- self.post_message(messages.AvailableCommandsUpdate(available_commands))
280
-
281
- case {
282
- "sessionUpdate": "subagent_start",
283
- "subagentId": subagent_id,
284
- "agentType": agent_type,
285
- **rest,
286
- }:
287
- self.post_message(messages.SubAgentStart(
288
- subagent_id, agent_type,
289
- prompt=rest.get("prompt", ""),
290
- ))
291
-
292
- case {
293
- "sessionUpdate": "subagent_chunk",
294
- "subagentId": subagent_id,
295
- "text": text,
296
- }:
297
- self.post_message(messages.SubAgentChunk(subagent_id, text))
298
-
299
- case {
300
- "sessionUpdate": "subagent_thinking",
301
- "subagentId": subagent_id,
302
- "text": text,
303
- }:
304
- self.post_message(messages.SubAgentThinking(subagent_id, text))
305
-
306
- case {
307
- "sessionUpdate": "subagent_end",
308
- "subagentId": subagent_id,
309
- "agentType": agent_type,
310
- **rest,
311
- }:
312
- self.post_message(messages.SubAgentEnd(
313
- subagent_id, agent_type,
314
- status=rest.get("status", "completed"),
315
- error=rest.get("error", ""),
316
- ))
317
-
318
- if status_line is not None:
319
- self.post_message(messages.UpdateStatusLine(status_line))
320
-
321
- @jsonrpc.expose("session/request_permission")
322
- async def rpc_request_permission(
323
- self,
324
- sessionId: str,
325
- options: list[protocol.PermissionOption],
326
- toolCall: protocol.ToolCallUpdatePermissionRequest,
327
- _meta: dict | None = None,
328
- ) -> protocol.RequestPermissionResponse:
329
- """Agent requests permission to make a tool call.
330
-
331
- Args:
332
- sessionId: The session ID.
333
- options: A list of permission options (potential replies).
334
- toolCall: The tool or tools the agent is requesting permission to call.
335
- _meta: Optional meta information.
336
-
337
- Returns:
338
- The response to the permission request.
339
- """
340
- result_future: asyncio.Future[Answer] = asyncio.Future()
341
- tool_call_id = toolCall["toolCallId"]
342
-
343
- permission_tool_call = toolCall.copy()
344
- permission_tool_call.pop("sessionUpdate", None)
345
- tool_call = cast(protocol.ToolCall, permission_tool_call)
346
- if tool_call_id in self.tool_calls:
347
- self.tool_calls[tool_call_id] |= tool_call
348
- else:
349
- self.tool_calls[tool_call_id] = deepcopy(tool_call)
350
-
351
- # Forward content to the ToolCall widget so the user can see what
352
- # the agent is requesting permission for (e.g. the bash command).
353
- merged = deepcopy(self.tool_calls[tool_call_id])
354
- self.post_message(messages.ToolCallUpdate(merged, tool_call))
355
-
356
- message = messages.RequestPermission(options, merged, result_future)
357
- self.post_message(message)
358
- await result_future
359
- ask_result = result_future.result()
360
-
361
- request_permission_outcome: protocol.OutcomeSelected = {
362
- "optionId": ask_result.id,
363
- "outcome": "selected",
364
- }
365
- result: protocol.RequestPermissionResponse = {
366
- "outcome": request_permission_outcome
367
- }
368
- return result
369
-
370
- def send_ask_user_answer(self, answer: str, cancelled: bool) -> None:
371
- """Send the user's answer back to the CDHA agent."""
372
- import uuid
373
-
374
- request = {
375
- "jsonrpc": "2.0",
376
- "method": "session/ask_user_answer",
377
- "params": {"answer": answer, "cancelled": cancelled},
378
- "id": str(uuid.uuid4()),
379
- }
380
- if self._process is not None and self._process.stdin is not None:
381
- self._process.stdin.write(b"%s\n" % json.dumps(request).encode("utf-8"))
382
-
383
- @jsonrpc.expose("fs/read_text_file")
384
- def rpc_read_text_file(
385
- self,
386
- sessionId: str,
387
- path: str,
388
- line: int | None = None,
389
- limit: int | None = None,
390
- ) -> dict[str, str]:
391
- """Read a file in the project."""
392
- # TODO: what if the read is outside of the project path?
393
- # https://agentclientprotocol.com/protocol/file-system#reading-files
394
- read_path = self.project_root_path / path
395
- try:
396
- text = read_path.read_text(encoding="utf-8", errors="ignore")
397
- except IOError:
398
- text = ""
399
- if line is not None:
400
- line = max(0, line - 1)
401
- if limit is None:
402
- text = "\n".join(text.splitlines()[line:])
403
- else:
404
- text = "\n".join(text.splitlines()[line : line + limit])
405
- return {"content": text}
406
-
407
- @jsonrpc.expose("fs/write_text_file")
408
- def rpc_write_text_file(self, sessionId: str, path: str, content: str) -> None:
409
- # TODO: What if the agent wants to write outside of the project path?
410
- # https://agentclientprotocol.com/protocol/file-system#writing-files
411
-
412
- write_path = self.project_root_path / path
413
- write_path.write_text(content, encoding="utf-8", errors="ignore")
414
-
415
- # https://agentclientprotocol.com/protocol/schema#createterminalrequest
416
- @jsonrpc.expose("terminal/create")
417
- async def rpc_terminal_create(
418
- self,
419
- command: str,
420
- _meta: dict | None = None,
421
- args: list[str] | None = None,
422
- cwd: str | None = None,
423
- env: list[protocol.EnvVariable] | None = None,
424
- outputByteLimit: int | None = None,
425
- sessionId: str | None = None,
426
- ) -> protocol.CreateTerminalResponse:
427
- # Assign a terminal id
428
- self._terminal_count = self._terminal_count + 1
429
- terminal_id = f"terminal-{self._terminal_count}"
430
-
431
- terminal_env = (
432
- {variable["name"]: variable["value"] for variable in env} if env else {}
433
- )
434
- result_future: asyncio.Future[bool] = asyncio.Future()
435
- self.post_message(
436
- messages.CreateTerminal(
437
- terminal_id,
438
- command=command,
439
- args=args,
440
- cwd=cwd,
441
- env=terminal_env,
442
- output_byte_limit=outputByteLimit,
443
- result_future=result_future,
444
- )
445
- )
446
- await result_future
447
- if not result_future.result():
448
- raise jsonrpc.JSONRPCError("Failed to create a terminal.")
449
- return {"terminalId": terminal_id}
450
-
451
- # https://agentclientprotocol.com/protocol/schema#killterminalcommandrequest
452
- @jsonrpc.expose("terminal/kill")
453
- def rpc_terminal_kill(
454
- self, sessionID: str, terminalId: str, _meta: dict | None = None
455
- ) -> protocol.KillTerminalCommandResponse:
456
- self.post_message(messages.KillTerminal(terminalId))
457
- return {}
458
-
459
- # https://agentclientprotocol.com/protocol/schema#terminal%2Foutput
460
- @jsonrpc.expose("terminal/output")
461
- async def rpc_terminal_output(
462
- self, sessionId: str, terminalId: str, _meta: dict | None = None
463
- ) -> protocol.TerminalOutputResponse:
464
- from tui.widgets.terminal_tool import ToolState
465
-
466
- result_future: asyncio.Future[ToolState] = asyncio.Future()
467
-
468
- if not self.post_message(messages.GetTerminalState(terminalId, result_future)):
469
- raise RuntimeError("Unable to get terminal output")
470
-
471
- await result_future
472
- terminal_state = result_future.result()
473
-
474
- result: protocol.TerminalOutputResponse = {
475
- "output": terminal_state.output,
476
- "truncated": terminal_state.truncated,
477
- }
478
- if (return_code := terminal_state.return_code) is not None:
479
- result["exitStatus"] = {"exitCode": return_code}
480
- return result
481
-
482
- # https://agentclientprotocol.com/protocol/schema#terminal%2Frelease
483
- @jsonrpc.expose("terminal/release")
484
- def rpc_terminal_release(
485
- self, sessionId: str, terminalId: str, _meta: dict | None = None
486
- ) -> protocol.ReleaseTerminalResponse:
487
- self.post_message(messages.ReleaseTerminal(terminalId))
488
- return {}
489
-
490
- # https://agentclientprotocol.com/protocol/schema#terminal%2Fwait-for-exit
491
- @jsonrpc.expose("terminal/wait_for_exit")
492
- async def rpc_terminal_wait_for_exit(
493
- self, sessionId: str, terminalId: str, _meta: dict | None = None
494
- ) -> protocol.WaitForTerminalExitResponse:
495
- result_future: asyncio.Future[tuple[int, str | None]] = asyncio.Future()
496
- if not self.post_message(
497
- messages.WaitForTerminalExit(terminalId, result_future)
498
- ):
499
- raise RuntimeError("Unable to wait for terminal exit; no terminal found")
500
-
501
- await result_future
502
- return_code, signal = result_future.result()
503
- return {"exitCode": return_code, "signal": signal}
504
-
505
- async def _run_agent(self) -> None:
506
- """Task to communicate with the agent subprocess."""
507
-
508
- PIPE = asyncio.subprocess.PIPE
509
- env = os.environ.copy()
510
- env["TOAD_CWD"] = str(self.project_root_path)
511
- python_bin = str(Path(sys.executable).parent)
512
- env["PATH"] = f"{python_bin}:{env.get('PATH', '')}"
513
-
514
- if (command := self.command) is None:
515
- self.post_message(
516
- AgentFail("Failed to start agent; no run command for this OS")
517
- )
518
- return
519
- try:
520
- process = self._process = await asyncio.create_subprocess_shell(
521
- command,
522
- stdin=PIPE,
523
- stdout=PIPE,
524
- stderr=PIPE,
525
- env=env,
526
- cwd=str(self.project_root_path),
527
- limit=10 * 1024 * 1024,
528
- )
529
- except Exception as error:
530
- self.post_message(AgentFail("Failed to start agent", details=str(error)))
531
- return
532
-
533
- self._task = asyncio.create_task(self.run())
534
-
535
- assert process.stdout is not None
536
- assert process.stdin is not None
537
-
538
- tasks: set[asyncio.Task] = set()
539
-
540
- async def call_jsonrpc(request: jsonrpc.JSONObject | jsonrpc.JSONList) -> None:
541
- try:
542
- if (result := await self.server.call(request)) is not None:
543
- result_json = json.dumps(result).encode("utf-8")
544
- if process.stdin is not None:
545
- process.stdin.write(b"%s\n" % result_json)
546
- finally:
547
- if (task := asyncio.current_task()) is not None:
548
- tasks.discard(task)
549
-
550
- try:
551
- while line := await process.stdout.readline():
552
- # This line should contain JSON, which may be:
553
- # A) a JSONRPC request
554
- # B) a JSONRPC response to a previous request
555
- if not line.strip():
556
- continue
557
-
558
- try:
559
- line_str = line.decode("utf-8")
560
- except Exception as error:
561
- logger.error("Unable to decode utf-8 from agent: %s", error)
562
- continue
563
-
564
- logger.debug("[agent] %s", line_str.rstrip())
565
- try:
566
- agent_data: jsonrpc.JSONType = json.loads(line_str)
567
- except Exception as error:
568
- logger.error("Failed to decode JSON from agent: %s", error)
569
- continue
570
-
571
- if isinstance(agent_data, dict):
572
- if "result" in agent_data or "error" in agent_data:
573
- # Wait for pending notification dispatch tasks so all ACP
574
- # messages are queued before the response future resolves.
575
- if tasks:
576
- await asyncio.gather(*tasks, return_exceptions=True)
577
- tasks.clear()
578
- API.process_response(agent_data)
579
- continue
580
-
581
- elif isinstance(agent_data, list):
582
- if not all(isinstance(datum, dict) for datum in agent_data):
583
- logger.error("Agent sent invalid data: %r", agent_data)
584
- continue
585
-
586
- if not isinstance(agent_data, dict):
587
- logger.error("Invalid JSON from agent: %r", agent_data)
588
- continue
589
-
590
- if not isinstance(agent_data, dict):
591
- logger.error("Invalid JSON from agent: %r", agent_data)
592
- continue
593
-
594
- # By this point we know it is a JSON RPC call
595
- assert isinstance(agent_data, dict)
596
- tasks.add(asyncio.create_task(call_jsonrpc(agent_data)))
597
- await asyncio.sleep(0)
598
- except Exception as e:
599
- logger.exception("Agent read loop failed: %s", e)
600
- finally:
601
- # Cancel all remaining tasks and wait for them to finish
602
- for task in tasks:
603
- task.cancel()
604
-
605
- # Wait for all tasks to complete cancellation
606
- if tasks:
607
- await asyncio.gather(*tasks, return_exceptions=True)
608
-
609
- if process.returncode:
610
- assert process.stderr is not None
611
- fail_details = (await process.stderr.read()).decode("utf-8", "replace")
612
- self.post_message(
613
- AgentFail(
614
- f"Agent returned a failure code: [b]{process.returncode}",
615
- details=fail_details,
616
- )
617
- )
618
-
619
- self._process = None
620
-
621
- async def stop(self) -> None:
622
- """Gracefully stop the process."""
623
- if self.session_pk is not None:
624
- db = DB()
625
- await db.session_update_last_used(self.session_pk)
626
-
627
- # Save session before terminating
628
- if self.session_id is not None:
629
- try:
630
- with self.request():
631
- response = api.session_save(self.session_id)
632
- await asyncio.wait_for(response.wait(), timeout=3)
633
- except Exception:
634
- pass
635
-
636
- if self._process is not None:
637
- try:
638
- self._process.terminate()
639
- except OSError:
640
- pass
641
-
642
- async def run(self) -> None:
643
- """The main logic of the Agent."""
644
- if constants.ACP_INITIALIZE:
645
- try:
646
- await self.acp_initialize()
647
-
648
- if self.session_id is None:
649
- await self.acp_new_session()
650
- else:
651
- if not self.agent_capabilities.get("loadSession", False):
652
- self.post_message(
653
- AgentFail(
654
- "Resume not supported",
655
- f"{self._agent_data['name']} does not currently support resuming sessions.",
656
- help="no_resume",
657
- )
658
- )
659
- return
660
- await self.acp_load_session()
661
- if self.session_pk is not None:
662
- db = DB()
663
- await db.session_update_last_used(self.session_pk)
664
- except jsonrpc.APIError as error:
665
- if isinstance(error.data, dict):
666
- reason = str(
667
- error.data.get("reason") or "Failed to initialize agent"
668
- )
669
- details = str(
670
- error.data.get("details") or error.data.get("error") or ""
671
- )
672
- else:
673
- reason = "Failed to initialize agent"
674
- details = ""
675
- self.post_message(AgentFail(reason, details))
676
- elif self.session_id is None:
677
- await self.acp_new_session()
678
- if self.session_pk is not None:
679
- db = DB()
680
- await db.session_update_last_used(self.session_pk)
681
-
682
- self.post_message(AgentReady())
683
-
684
- async def _ensure_db_session(self) -> None:
685
- if self.session_pk is not None:
686
- return
687
- if not self.supports_load_session:
688
- return
689
- data = getattr(self, "_pending_session_data", None)
690
- if data is None:
691
- return
692
-
693
- db = DB()
694
- self.session_pk = await db.session_new(
695
- data["session_name"],
696
- data["agent_name"],
697
- data["agent_identity"],
698
- data["session_id"],
699
- protocol=data["protocol"],
700
- meta=data["meta"],
701
- )
702
- self.post_message(tui_messages.SessionUpdate(session_pk=self.session_pk))
703
- self._pending_session_data = None
704
- await self._save_last_session_to_cdh()
705
-
706
- async def _save_last_session_to_cdh(self) -> None:
707
- if self.session_id is None:
708
- return
709
- try:
710
- from onecode.agent.cdh_loader import CdhProjectLoader
711
-
712
- cdh_dir = CdhProjectLoader.find_cdh_dir(self.project_root_path)
713
- if cdh_dir is None:
714
- return
715
- data: dict = {
716
- "agent_session_id": self.session_id,
717
- "agent_identity": self._agent_data["identity"],
718
- }
719
- if self.session_pk is not None:
720
- data["session_pk"] = self.session_pk
721
- CdhProjectLoader.save_last_session(cdh_dir, data)
722
- except Exception:
723
- pass
724
-
725
- async def send_prompt(self, prompt: str) -> str | None:
726
- """Send a prompt to the agent.
727
-
728
- !!! note
729
- This method blocks as it may defer to a thread to read resources.
730
-
731
- Args:
732
- prompt: Prompt text.
733
- """
734
- await self._ensure_db_session()
735
- prompt_content_blocks = await asyncio.to_thread(
736
- build_prompt, self.project_root_path, prompt
737
- )
738
- return await self.acp_session_prompt(prompt_content_blocks)
739
-
740
- async def acp_initialize(self):
741
- """Initialize agent."""
742
- with self.request():
743
- initialize_response = api.initialize(
744
- PROTOCOL_VERSION,
745
- {
746
- "fs": {
747
- "readTextFile": True,
748
- "writeTextFile": True,
749
- },
750
- "terminal": True,
751
- },
752
- {
753
- "name": tui.NAME,
754
- "title": tui.TITLE,
755
- "version": tui.get_version(),
756
- },
757
- )
758
-
759
- response = await initialize_response.wait()
760
- assert response is not None
761
-
762
- # Store agents capabilities
763
- if agent_capabilities := response.get("agentCapabilities"):
764
- self.agent_capabilities = agent_capabilities
765
- if auth_methods := response.get("authMethods"):
766
- self.auth_methods = auth_methods
767
-
768
- async def acp_new_session(self) -> None:
769
- """Create a new session."""
770
- with self.request():
771
- session_new_response = api.session_new(
772
- str(self.project_root_path),
773
- [],
774
- )
775
- response = await session_new_response.wait()
776
- assert response is not None
777
- self.session_id = response["sessionId"]
778
-
779
- if self.supports_load_session:
780
- self._pending_session_data = {
781
- "session_name": "New Session",
782
- "agent_name": self._agent_data["name"],
783
- "agent_identity": self._agent_data["identity"],
784
- "session_id": self.session_id,
785
- "protocol": "acp",
786
- "meta": {
787
- "cwd": str(self.project_root_path),
788
- "agent_data": self._agent_data,
789
- },
790
- }
791
-
792
- if (modes := response.get("modes", None)) is not None:
793
- current_mode = modes["currentModeId"]
794
- available_modes = modes["availableModes"]
795
- modes_update = {
796
- mode["id"]: Mode(
797
- mode["id"], mode["name"], mode.get("description", None)
798
- )
799
- for mode in available_modes
800
- }
801
- self.post_message(messages.SetModes(current_mode, modes_update))
802
-
803
- async def acp_load_session(self) -> None:
804
- assert self.session_id is not None, "Session id must be set"
805
- cwd = str(self.project_root_path)
806
- session_title = None
807
- if self.session_pk is not None:
808
- db = DB()
809
- if (session := await db.session_get(self.session_pk)) is not None:
810
- session_title = session.get("title") or "Untitled"
811
- if session["meta_json"]:
812
- meta = json.loads(session["meta_json"])
813
- if session_cwd := meta.get("cwd", None):
814
- cwd = session_cwd
815
- if agent_data := meta.get("agent_data"):
816
- self._agent_data = agent_data
817
- else:
818
- db = DB()
819
- if (session := await db.session_get_by_agent_session_id(self.session_id)) is not None:
820
- self.session_pk = session["id"]
821
- session_title = session.get("title") or "Untitled"
822
-
823
- with self.request():
824
- session_load_response = api.session_load(cwd, [], self.session_id)
825
- self.post_message(messages.SessionReplay(active=True))
826
- try:
827
- response = await session_load_response.wait()
828
- self.session_load_info = {
829
- "total_messages": response.get("_total_messages", 0),
830
- "visible_start": response.get("_visible_start", 0),
831
- }
832
- finally:
833
- self.post_message(messages.SessionReplay(
834
- active=False,
835
- total_messages=self.session_load_info.get("total_messages", 0),
836
- visible_count=self.session_load_info.get("total_messages", 0) - self.session_load_info.get("visible_start", 0),
837
- ))
838
-
839
- if error := response.get("error"):
840
- logger.warning("Session load error: %s", error)
841
-
842
- if (modes := response.get("modes", None)) is not None:
843
- current_mode = modes["currentModeId"]
844
- available_modes = modes["availableModes"]
845
- modes_update = {
846
- mode["id"]: Mode(
847
- mode["id"], mode["name"], mode.get("description", None)
848
- )
849
- for mode in available_modes
850
- }
851
- self.post_message(messages.SetModes(current_mode, modes_update))
852
-
853
- if self.session_pk is None:
854
- self._pending_session_data = {
855
- "session_name": session_title or "Untitled",
856
- "agent_name": self._agent_data["name"],
857
- "agent_identity": self._agent_data["identity"],
858
- "session_id": self.session_id,
859
- "protocol": "acp",
860
- "meta": {
861
- "cwd": str(self.project_root_path),
862
- "agent_data": self._agent_data,
863
- },
864
- }
865
- await self._ensure_db_session()
866
-
867
- if session_title:
868
- self.post_message(tui_messages.SessionUpdate(name=session_title, session_pk=self.session_pk))
869
-
870
- await self._save_last_session_to_cdh()
871
-
872
- async def acp_load_earlier_messages(self, offset: int, limit: int) -> None:
873
- """Load a range of earlier messages from the current session."""
874
- assert self.session_id is not None
875
- with self.request():
876
- response = api.session_load_earlier(self.session_id, offset, limit)
877
- try:
878
- await response.wait()
879
- except jsonrpc.JSONRPCError:
880
- pass
881
-
882
- async def acp_session_prompt(
883
- self, prompt: list[protocol.ContentBlock]
884
- ) -> str | None:
885
- """Send the prompt to the agent.
886
-
887
- Returns:
888
- The stop reason.
889
-
890
- """
891
- if self._process is None:
892
- self.post_message(
893
- AgentFail(
894
- "Agent process is not running",
895
- "The agent subprocess has terminated. Restart the session to continue.",
896
- )
897
- )
898
- return None
899
- with self.request():
900
- session_prompt = api.session_prompt(prompt, self.session_id)
901
- try:
902
- result = await session_prompt.wait()
903
- except jsonrpc.APIError as error:
904
- details = ""
905
- match error.data:
906
- case {"details": details}:
907
- pass
908
-
909
- self.post_message(
910
- AgentFail(
911
- "Failed to send prompt" or error.message,
912
- (
913
- str(details)
914
- if details
915
- else f"{self._agent_data['name']} returned an error"
916
- ),
917
- )
918
- )
919
- return None
920
- except jsonrpc.JSONRPCError as error:
921
- self.post_message(
922
- AgentFail(
923
- "Failed to send prompt" or error.message,
924
- (error.message or f"{self._agent_data['name']} returned an error"),
925
- )
926
- )
927
- return None
928
-
929
- assert result is not None
930
- return result.get("stopReason")
931
-
932
- async def acp_session_set_mode(self, mode_id: str) -> str | None:
933
- """Update the current mode with the agent."""
934
- with self.request():
935
- response = api.session_set_mode(self.session_id, mode_id)
936
- try:
937
- await response.wait()
938
- except jsonrpc.APIError as error:
939
- match error.data:
940
- case {"details": details}:
941
- return details if isinstance(details, str) else "Failed to set mode"
942
- return "Failed to set mode"
943
- else:
944
- return None
945
-
946
- async def set_mode(self, mode_id: str) -> str | None:
947
- return await self.acp_session_set_mode(mode_id)
948
-
949
- async def acp_session_clear_todos(self) -> dict:
950
- """Clear all todos via the session/clear_todos RPC."""
951
- if self.session_id is None:
952
- return {"cleared": False}
953
- with self.request():
954
- response = api.session_clear_todos(self.session_id)
955
- result = await response.wait()
956
- return result or {"cleared": False}
957
-
958
- async def set_session_name(self, name: str) -> None:
959
- if self.session_pk is None:
960
- return
961
- db = DB()
962
- await db.session_update_title(self.session_pk, name)
963
- if self.session_id:
964
- from onecode.agent.session import AgentSession
965
- session = AgentSession(self.session_id)
966
- if session.load():
967
- session.name = name
968
- session.save()
969
- self.post_message(tui_messages.SessionUpdate(name=name, session_pk=self.session_pk))
970
-
971
- async def acp_session_cancel(self) -> bool:
972
- """Send session/cancel notification to the agent subprocess.
973
-
974
- Writes directly to stdin with drain() to ensure the message is
975
- actually flushed through the pipe, unlike the Request path which
976
- is synchronous and cannot drain.
977
- """
978
- if not self.session_id:
979
- logger.info("[client] session/cancel: no session_id, skipping")
980
- return False
981
- cancel_msg = {
982
- "jsonrpc": "2.0",
983
- "method": "session/cancel",
984
- "params": {"sessionId": self.session_id, "_meta": {}},
985
- }
986
- data = json.dumps(cancel_msg).encode("utf-8") + b"\n"
987
- logger.info("[client] session/cancel: sending %s", cancel_msg)
988
- stdin = self._process.stdin if self._process else None
989
- if stdin is None:
990
- logger.info("[client] session/cancel: no stdin pipe, skipping")
991
- return False
992
- stdin.write(data)
993
- try:
994
- await stdin.drain()
995
- except OSError:
996
- logger.info("[client] session/cancel: drain failed", exc_info=True)
997
- return False
998
- logger.info("[client] session/cancel: sent successfully")
999
- return True
1000
-
1001
- async def cancel(self) -> bool:
1002
- return await self.acp_session_cancel()