@scemoon/cdh 1.0.6 → 1.0.8

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 (551) hide show
  1. package/package.json +2 -2
  2. package/run.js +2 -1
  3. package/ai-dlc-skill/SKILL.md +0 -77
  4. package/ai-dlc-skill/agents/deliver-agent.md +0 -24
  5. package/ai-dlc-skill/agents/master.md +0 -50
  6. package/ai-dlc-skill/agents/plan-agent.md +0 -23
  7. package/ai-dlc-skill/agents/understand-agent.md +0 -23
  8. package/ai-dlc-skill/agents/verify-agent.md +0 -27
  9. package/ai-dlc-skill/architecture/project-structure.md +0 -78
  10. package/ai-dlc-skill/brownfield/README.md +0 -30
  11. package/ai-dlc-skill/brownfield/generate-context.sh +0 -64
  12. package/ai-dlc-skill/brownfield/scripts/deps.sh +0 -27
  13. package/ai-dlc-skill/brownfield/scripts/discover.sh +0 -34
  14. package/ai-dlc-skill/brownfield/scripts/extract-api.sh +0 -45
  15. package/ai-dlc-skill/components/backend.md +0 -34
  16. package/ai-dlc-skill/components/desktop.md +0 -30
  17. package/ai-dlc-skill/components/mya.md +0 -29
  18. package/ai-dlc-skill/components/native.md +0 -30
  19. package/ai-dlc-skill/components/tta.md +0 -29
  20. package/ai-dlc-skill/components/web.md +0 -31
  21. package/ai-dlc-skill/components/wxa.md +0 -29
  22. package/ai-dlc-skill/contracts/README.md +0 -25
  23. package/ai-dlc-skill/core/adaptive-flow.md +0 -36
  24. package/ai-dlc-skill/core/security.md +0 -29
  25. package/ai-dlc-skill/cross-tool/cline/export.sh +0 -31
  26. package/ai-dlc-skill/cross-tool/copilot/export.sh +0 -47
  27. package/ai-dlc-skill/cross-tool/cursor/export.sh +0 -32
  28. package/ai-dlc-skill/cross-tool/export.sh +0 -22
  29. package/ai-dlc-skill/cross-tool/onecode/config.yaml.tpl +0 -17
  30. package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +0 -35
  31. package/ai-dlc-skill/cross-tool/onecode/export.sh +0 -54
  32. package/ai-dlc-skill/phases/deliver/entry.md +0 -26
  33. package/ai-dlc-skill/phases/deliver/lifecycle.md +0 -185
  34. package/ai-dlc-skill/phases/deliver/prompt.md +0 -40
  35. package/ai-dlc-skill/phases/deliver/rules.md +0 -61
  36. package/ai-dlc-skill/phases/plan/entry.md +0 -25
  37. package/ai-dlc-skill/phases/plan/lifecycle.md +0 -204
  38. package/ai-dlc-skill/phases/plan/prompt.md +0 -38
  39. package/ai-dlc-skill/phases/plan/rules.md +0 -25
  40. package/ai-dlc-skill/phases/understand/entry.md +0 -26
  41. package/ai-dlc-skill/phases/understand/lifecycle.md +0 -184
  42. package/ai-dlc-skill/phases/understand/prompt.md +0 -41
  43. package/ai-dlc-skill/phases/understand/rules.md +0 -37
  44. package/ai-dlc-skill/phases/verify/entry.md +0 -26
  45. package/ai-dlc-skill/phases/verify/lifecycle.md +0 -192
  46. package/ai-dlc-skill/phases/verify/prompt.md +0 -43
  47. package/ai-dlc-skill/phases/verify/rules.md +0 -73
  48. package/ai-dlc-skill/practices/bdd.md +0 -91
  49. package/ai-dlc-skill/practices/sdd.md +0 -84
  50. package/ai-dlc-skill/practices/tdd.md +0 -95
  51. package/ai-dlc-skill/providers/README.md +0 -114
  52. package/ai-dlc-skill/providers/aliyun/deployment.yaml +0 -66
  53. package/ai-dlc-skill/providers/aliyun/preview.yaml +0 -84
  54. package/ai-dlc-skill/providers/aliyun/provider.yaml +0 -160
  55. package/ai-dlc-skill/providers/tcb/deployment.yaml +0 -69
  56. package/ai-dlc-skill/providers/tcb/preview.yaml +0 -83
  57. package/ai-dlc-skill/providers/tcb/provider.yaml +0 -172
  58. package/ai-dlc-skill/requirements.md +0 -140
  59. package/ai-dlc-skill/skill.yaml +0 -147
  60. package/ai-dlc-skill/templates/integration/CHANGELOG.md +0 -41
  61. package/ai-dlc-skill/templates/integration/asyncapi.yaml +0 -43
  62. package/ai-dlc-skill/templates/integration/contract-diff.md +0 -63
  63. package/ai-dlc-skill/templates/integration/contract-spec.md +0 -49
  64. package/ai-dlc-skill/templates/integration/openapi.yaml +0 -18
  65. package/ai-dlc-skill/templates/integration/runtime-contract.yaml +0 -36
  66. package/ai-dlc-skill/templates/plan/design.md +0 -49
  67. package/ai-dlc-skill/templates/plan/task-list.md +0 -32
  68. package/ai-dlc-skill/templates/project/README.md +0 -126
  69. package/ai-dlc-skill/templates/project/template.md +0 -119
  70. package/ai-dlc-skill/templates/understand/feature.feature +0 -23
  71. package/ai-dlc-skill/templates/understand/intent.md +0 -29
  72. package/ai-dlc-skill/templates/understand/spec-delta.md +0 -47
  73. package/ai-dlc-skill/templates/verify/test-unit.py +0 -27
  74. package/ai-dlc-skill/walkthrough/collect.sh +0 -50
  75. package/ai-dlc-skill/walkthrough/template.md +0 -54
  76. package/ai-dlc-skill/workflows/ai-dlc.yaml +0 -110
  77. package/ai-dlc-skill/workflows/deployment.yaml +0 -199
  78. package/cdh/__init__.py +0 -0
  79. package/cdh/__main__.py +0 -4
  80. package/cdh/__pycache__/__init__.cpython-314.pyc +0 -0
  81. package/cdh/__pycache__/cdh_mcp_injector.cpython-314.pyc +0 -0
  82. package/cdh/__pycache__/cdh_mcp_loader.cpython-314.pyc +0 -0
  83. package/cdh/__pycache__/cdh_mcp_manager.cpython-314.pyc +0 -0
  84. package/cdh/__pycache__/cdh_session_aggregator.cpython-314.pyc +0 -0
  85. package/cdh/__pycache__/cdh_skill_loader.cpython-314.pyc +0 -0
  86. package/cdh/__pycache__/cdh_skill_manager.cpython-314.pyc +0 -0
  87. package/cdh/__pycache__/cli.cpython-313.pyc +0 -0
  88. package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
  89. package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
  90. package/cdh/cdh_mcp_injector.py +0 -115
  91. package/cdh/cdh_mcp_loader.py +0 -67
  92. package/cdh/cdh_mcp_manager.py +0 -73
  93. package/cdh/cdh_session_aggregator.py +0 -258
  94. package/cdh/cdh_skill_manager.py +0 -105
  95. package/cdh/cli.py +0 -807
  96. package/cdh/scaffold.py +0 -728
  97. package/onecode/__init__.py +0 -1
  98. package/onecode/__main__.py +0 -4
  99. package/onecode/__pycache__/__init__.cpython-314.pyc +0 -0
  100. package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
  101. package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
  102. package/onecode/__pycache__/config.cpython-314.pyc +0 -0
  103. package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
  104. package/onecode/__pycache__/migrate.cpython-314.pyc +0 -0
  105. package/onecode/agent/__init__.py +0 -0
  106. package/onecode/agent/__pycache__/__init__.cpython-314.pyc +0 -0
  107. package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
  108. package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
  109. package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
  110. package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
  111. package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
  112. package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
  113. package/onecode/agent/__pycache__/permissions_store.cpython-314.pyc +0 -0
  114. package/onecode/agent/__pycache__/project_doc.cpython-314.pyc +0 -0
  115. package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
  116. package/onecode/agent/agents/__init__.py +0 -29
  117. package/onecode/agent/agents/__pycache__/__init__.cpython-314.pyc +0 -0
  118. package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
  119. package/onecode/agent/agents/types.py +0 -589
  120. package/onecode/agent/attachments.py +0 -112
  121. package/onecode/agent/cdh_loader.py +0 -267
  122. package/onecode/agent/context.py +0 -343
  123. package/onecode/agent/engine.py +0 -2700
  124. package/onecode/agent/hooks.py +0 -86
  125. package/onecode/agent/onecode_agent_acp.py +0 -2743
  126. package/onecode/agent/onecode_agent_acp.py.bak +0 -2447
  127. package/onecode/agent/permissions.py +0 -150
  128. package/onecode/agent/permissions_store.py +0 -62
  129. package/onecode/agent/project_doc.py +0 -19
  130. package/onecode/agent/session.py +0 -195
  131. package/onecode/agent/snapshot.py +0 -144
  132. package/onecode/agent/tools/__init__.py +0 -17
  133. package/onecode/agent/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  134. package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
  135. package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
  136. package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
  137. package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
  138. package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
  139. package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
  140. package/onecode/agent/tools/__pycache__/errors.cpython-314.pyc +0 -0
  141. package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
  142. package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
  143. package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
  144. package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
  145. package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
  146. package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
  147. package/onecode/agent/tools/__pycache__/permissions.cpython-314.pyc +0 -0
  148. package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
  149. package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
  150. package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
  151. package/onecode/agent/tools/__pycache__/schema_validation.cpython-314.pyc +0 -0
  152. package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
  153. package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
  154. package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
  155. package/onecode/agent/tools/agent_tools.py +0 -132
  156. package/onecode/agent/tools/apply_patch_tool.py +0 -138
  157. package/onecode/agent/tools/bash_tool.py +0 -111
  158. package/onecode/agent/tools/communication_tools.py +0 -148
  159. package/onecode/agent/tools/config_tool.py +0 -130
  160. package/onecode/agent/tools/cron_tools.py +0 -178
  161. package/onecode/agent/tools/errors.py +0 -17
  162. package/onecode/agent/tools/file_ops.py +0 -221
  163. package/onecode/agent/tools/file_tools.py +0 -225
  164. package/onecode/agent/tools/git_tools.py +0 -88
  165. package/onecode/agent/tools/lsp_tools.py +0 -404
  166. package/onecode/agent/tools/mcp_tools.py +0 -127
  167. package/onecode/agent/tools/permission_handler.py +0 -46
  168. package/onecode/agent/tools/permissions.py +0 -77
  169. package/onecode/agent/tools/protocol.py +0 -20
  170. package/onecode/agent/tools/registry.py +0 -201
  171. package/onecode/agent/tools/sandbox.py +0 -358
  172. package/onecode/agent/tools/schema_validation.py +0 -100
  173. package/onecode/agent/tools/skill_tools.py +0 -52
  174. package/onecode/agent/tools/todo_tools.py +0 -207
  175. package/onecode/agent/tools/web_tools.py +0 -169
  176. package/onecode/builtin_skills/agent-browser/SKILL.md +0 -212
  177. package/onecode/builtin_skills/agent-browser/skill.yaml +0 -8
  178. package/onecode/builtin_skills/git/SKILL.md +0 -64
  179. package/onecode/builtin_skills/git/skill.yaml +0 -7
  180. package/onecode/builtin_skills/shell/SKILL.md +0 -58
  181. package/onecode/builtin_skills/shell/skill.yaml +0 -8
  182. package/onecode/builtin_skills/skill-creator/SKILL.md +0 -106
  183. package/onecode/builtin_skills/skill-creator/skill.yaml +0 -8
  184. package/onecode/cli.py +0 -935
  185. package/onecode/cloud/__init__.py +0 -0
  186. package/onecode/cloud/base.py +0 -20
  187. package/onecode/codebase/__init__.py +0 -69
  188. package/onecode/codebase/__pycache__/__init__.cpython-314.pyc +0 -0
  189. package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
  190. package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
  191. package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
  192. package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
  193. package/onecode/codebase/__pycache__/tools.cpython-314.pyc +0 -0
  194. package/onecode/codebase/chunker.py +0 -125
  195. package/onecode/codebase/indexer.py +0 -149
  196. package/onecode/codebase/retriever.py +0 -203
  197. package/onecode/codebase/storage.py +0 -131
  198. package/onecode/codebase/tools.py +0 -59
  199. package/onecode/commands.py +0 -596
  200. package/onecode/config.py +0 -314
  201. package/onecode/config_screen.py +0 -600
  202. package/onecode/cron/__init__.py +0 -3
  203. package/onecode/lsp/__init__.py +0 -3
  204. package/onecode/mcp/__init__.py +0 -4
  205. package/onecode/mcp/__pycache__/__init__.cpython-314.pyc +0 -0
  206. package/onecode/mcp/__pycache__/client.cpython-314.pyc +0 -0
  207. package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
  208. package/onecode/mcp/client.py +0 -193
  209. package/onecode/mcp/manager.py +0 -190
  210. package/onecode/memory/__init__.py +0 -49
  211. package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
  212. package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
  213. package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
  214. package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
  215. package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
  216. package/onecode/memory/__pycache__/symbolic.cpython-314.pyc +0 -0
  217. package/onecode/memory/backend.py +0 -159
  218. package/onecode/memory/pyramid.py +0 -133
  219. package/onecode/memory/recall.py +0 -203
  220. package/onecode/migrate.py +0 -122
  221. package/onecode/models/__init__.py +0 -4
  222. package/onecode/models/__pycache__/__init__.cpython-314.pyc +0 -0
  223. package/onecode/models/__pycache__/errors.cpython-314.pyc +0 -0
  224. package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
  225. package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
  226. package/onecode/models/__pycache__/registry.cpython-314.pyc +0 -0
  227. package/onecode/models/errors.py +0 -104
  228. package/onecode/models/messages.py +0 -739
  229. package/onecode/models/provider.py +0 -699
  230. package/onecode/models/providers/__init__.py +0 -17
  231. package/onecode/models/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  232. package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
  233. package/onecode/models/providers/__pycache__/deepseek_provider.cpython-314.pyc +0 -0
  234. package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
  235. package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
  236. package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
  237. package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
  238. package/onecode/models/providers/__pycache__/openai_provider.cpython-314.pyc +0 -0
  239. package/onecode/models/providers/anthropic_provider.py +0 -273
  240. package/onecode/models/providers/deepseek_provider.py +0 -212
  241. package/onecode/models/providers/glm_provider.py +0 -195
  242. package/onecode/models/providers/minimax_provider.py +0 -158
  243. package/onecode/models/providers/minimaxi_provider.py +0 -417
  244. package/onecode/models/providers/ollama_provider.py +0 -100
  245. package/onecode/models/providers/openai_provider.py +0 -237
  246. package/onecode/models/registry.py +0 -212
  247. package/onecode/server/__init__.py +0 -3
  248. package/onecode/server/app.py +0 -224
  249. package/onecode/skills/__init__.py +0 -11
  250. package/onecode/skills/__pycache__/__init__.cpython-314.pyc +0 -0
  251. package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
  252. package/onecode/skills/__pycache__/bootstrap.cpython-314.pyc +0 -0
  253. package/onecode/skills/__pycache__/create.cpython-314.pyc +0 -0
  254. package/onecode/skills/__pycache__/frontmatter.cpython-314.pyc +0 -0
  255. package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
  256. package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
  257. package/onecode/skills/__pycache__/model.cpython-314.pyc +0 -0
  258. package/onecode/skills/argument_substitution.py +0 -38
  259. package/onecode/skills/bootstrap.py +0 -124
  260. package/onecode/skills/create.py +0 -56
  261. package/onecode/skills/frontmatter.py +0 -33
  262. package/onecode/skills/loader.py +0 -139
  263. package/onecode/skills/manager.py +0 -75
  264. package/onecode/skills/model.py +0 -43
  265. package/onecode/storage/__init__.py +0 -0
  266. package/onecode/storage/activity.py +0 -63
  267. package/onecode/storage/project.py +0 -54
  268. package/onecode/storage/session.py +0 -112
  269. package/onecode/tasks/__init__.py +0 -26
  270. package/onecode/tasks/manager.py +0 -232
  271. package/onecode/tasks/models.py +0 -240
  272. package/onecode/trace/__init__.py +0 -0
  273. package/onecode/trace/tracer.py +0 -173
  274. package/onecode/utils/__init__.py +0 -0
  275. package/onecode/utils/helpers.py +0 -20
  276. package/pyproject.toml +0 -74
  277. package/tui/__init__.py +0 -46
  278. package/tui/__main__.py +0 -4
  279. package/tui/__pycache__/__init__.cpython-314.pyc +0 -0
  280. package/tui/__pycache__/_loop.cpython-314.pyc +0 -0
  281. package/tui/__pycache__/_path_fuzzy_search.cpython-314.pyc +0 -0
  282. package/tui/__pycache__/_path_match.cpython-314.pyc +0 -0
  283. package/tui/__pycache__/about.cpython-314.pyc +0 -0
  284. package/tui/__pycache__/agent.cpython-314.pyc +0 -0
  285. package/tui/__pycache__/agent_schema.cpython-314.pyc +0 -0
  286. package/tui/__pycache__/agents.cpython-314.pyc +0 -0
  287. package/tui/__pycache__/answer.cpython-314.pyc +0 -0
  288. package/tui/__pycache__/app.cpython-313.pyc +0 -0
  289. package/tui/__pycache__/app.cpython-314.pyc +0 -0
  290. package/tui/__pycache__/atomic.cpython-314.pyc +0 -0
  291. package/tui/__pycache__/complete.cpython-314.pyc +0 -0
  292. package/tui/__pycache__/constants.cpython-314.pyc +0 -0
  293. package/tui/__pycache__/conversation_markdown.cpython-314.pyc +0 -0
  294. package/tui/__pycache__/danger.cpython-314.pyc +0 -0
  295. package/tui/__pycache__/db.cpython-314.pyc +0 -0
  296. package/tui/__pycache__/dec.cpython-314.pyc +0 -0
  297. package/tui/__pycache__/directory.cpython-314.pyc +0 -0
  298. package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
  299. package/tui/__pycache__/directory_watcher.cpython-314.pyc +0 -0
  300. package/tui/__pycache__/format_path.cpython-314.pyc +0 -0
  301. package/tui/__pycache__/fuzzy.cpython-314.pyc +0 -0
  302. package/tui/__pycache__/fuzzy_index.cpython-314.pyc +0 -0
  303. package/tui/__pycache__/history.cpython-314.pyc +0 -0
  304. package/tui/__pycache__/jsonrpc.cpython-314.pyc +0 -0
  305. package/tui/__pycache__/menus.cpython-314.pyc +0 -0
  306. package/tui/__pycache__/message_log.cpython-314.pyc +0 -0
  307. package/tui/__pycache__/messages.cpython-314.pyc +0 -0
  308. package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
  309. package/tui/__pycache__/path_filter.cpython-314.pyc +0 -0
  310. package/tui/__pycache__/paths.cpython-314.pyc +0 -0
  311. package/tui/__pycache__/pill.cpython-314.pyc +0 -0
  312. package/tui/__pycache__/platform_commands.cpython-314.pyc +0 -0
  313. package/tui/__pycache__/protocol.cpython-314.pyc +0 -0
  314. package/tui/__pycache__/session_tracker.cpython-314.pyc +0 -0
  315. package/tui/__pycache__/settings.cpython-314.pyc +0 -0
  316. package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
  317. package/tui/__pycache__/shell.cpython-314.pyc +0 -0
  318. package/tui/__pycache__/shell_read.cpython-314.pyc +0 -0
  319. package/tui/__pycache__/slash_command.cpython-314.pyc +0 -0
  320. package/tui/__pycache__/version.cpython-314.pyc +0 -0
  321. package/tui/_loop.py +0 -86
  322. package/tui/_path_fuzzy_search.py +0 -48
  323. package/tui/_path_match.py +0 -141
  324. package/tui/about.py +0 -95
  325. package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
  326. package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
  327. package/tui/acp/__pycache__/encode_tool_call_id.cpython-314.pyc +0 -0
  328. package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
  329. package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
  330. package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
  331. package/tui/acp/agent.py +0 -1002
  332. package/tui/acp/api.py +0 -73
  333. package/tui/acp/encode_tool_call_id.py +0 -12
  334. package/tui/acp/messages.py +0 -203
  335. package/tui/acp/prompt.py +0 -53
  336. package/tui/acp/protocol.py +0 -499
  337. package/tui/agent.py +0 -70
  338. package/tui/agent_schema.py +0 -76
  339. package/tui/agents.py +0 -46
  340. package/tui/ansi/__init__.py +0 -1
  341. package/tui/ansi/__pycache__/__init__.cpython-314.pyc +0 -0
  342. package/tui/ansi/__pycache__/_ansi.cpython-314.pyc +0 -0
  343. package/tui/ansi/__pycache__/_ansi_colors.cpython-314.pyc +0 -0
  344. package/tui/ansi/__pycache__/_control_codes.cpython-314.pyc +0 -0
  345. package/tui/ansi/__pycache__/_keys.cpython-314.pyc +0 -0
  346. package/tui/ansi/__pycache__/_sgr_styles.cpython-314.pyc +0 -0
  347. package/tui/ansi/__pycache__/_stream_parser.cpython-314.pyc +0 -0
  348. package/tui/ansi/_ansi.py +0 -1689
  349. package/tui/ansi/_ansi_colors.py +0 -264
  350. package/tui/ansi/_control_codes.py +0 -37
  351. package/tui/ansi/_keys.py +0 -251
  352. package/tui/ansi/_sgr_styles.py +0 -64
  353. package/tui/ansi/_stream_parser.py +0 -418
  354. package/tui/answer.py +0 -12
  355. package/tui/app.py +0 -1266
  356. package/tui/atomic.py +0 -37
  357. package/tui/cli.py +0 -313
  358. package/tui/code_analyze.py +0 -27
  359. package/tui/complete.py +0 -38
  360. package/tui/constants.py +0 -61
  361. package/tui/conversation_markdown.py +0 -30
  362. package/tui/danger.py +0 -372
  363. package/tui/data/agents/ampcode.com.toml +0 -52
  364. package/tui/data/agents/augmentcode.com.toml +0 -41
  365. package/tui/data/agents/cdh.cloud-dev-harness.toml +0 -23
  366. package/tui/data/agents/claude.com.toml +0 -41
  367. package/tui/data/agents/copilot.github.com.toml +0 -29
  368. package/tui/data/agents/crowai.dev.toml +0 -36
  369. package/tui/data/agents/cursor.com.toml +0 -38
  370. package/tui/data/agents/deepagents.langchain.com +0 -34
  371. package/tui/data/agents/docker.com.toml +0 -60
  372. package/tui/data/agents/geminicli.com.toml +0 -28
  373. package/tui/data/agents/goose.ai.toml +0 -52
  374. package/tui/data/agents/inference.huggingface.co.toml +0 -41
  375. package/tui/data/agents/kimi.com.toml +0 -35
  376. package/tui/data/agents/kiro.dev.toml +0 -43
  377. package/tui/data/agents/onecode.dev.toml +0 -22
  378. package/tui/data/agents/openai.com.toml +0 -53
  379. package/tui/data/agents/openclaw.ai.toml +0 -37
  380. package/tui/data/agents/opencode.ai.toml +0 -39
  381. package/tui/data/agents/openhands.dev.toml +0 -46
  382. package/tui/data/agents/stakpak.dev.toml +0 -62
  383. package/tui/data/agents/vibe.mistral.ai.toml +0 -31
  384. package/tui/data/agents/vtcode.dev.toml +0 -63
  385. package/tui/data/images/frog.png +0 -0
  386. package/tui/data/sounds/question.wav +0 -0
  387. package/tui/data/sounds/turn-over.wav +0 -0
  388. package/tui/db.py +0 -213
  389. package/tui/dec.py +0 -332
  390. package/tui/directory.py +0 -234
  391. package/tui/directory_suggester.py +0 -93
  392. package/tui/directory_watcher.py +0 -231
  393. package/tui/format_path.py +0 -25
  394. package/tui/fuzzy.py +0 -140
  395. package/tui/fuzzy_index.py +0 -275
  396. package/tui/gist.py +0 -33
  397. package/tui/history.py +0 -138
  398. package/tui/jsonrpc.py +0 -578
  399. package/tui/menus.py +0 -14
  400. package/tui/message_log.py +0 -69
  401. package/tui/messages.py +0 -144
  402. package/tui/option_content.py +0 -51
  403. package/tui/os.py +0 -0
  404. package/tui/path_complete.py +0 -217
  405. package/tui/path_filter.py +0 -124
  406. package/tui/paths.py +0 -87
  407. package/tui/pill.py +0 -23
  408. package/tui/platform_commands.py +0 -52
  409. package/tui/prompt/__pycache__/extract.cpython-314.pyc +0 -0
  410. package/tui/prompt/__pycache__/resource.cpython-314.pyc +0 -0
  411. package/tui/prompt/extract.py +0 -19
  412. package/tui/prompt/resource.py +0 -68
  413. package/tui/protocol.py +0 -28
  414. package/tui/screens/__init__.py +0 -0
  415. package/tui/screens/__pycache__/__init__.cpython-314.pyc +0 -0
  416. package/tui/screens/__pycache__/agent_modal.cpython-314.pyc +0 -0
  417. package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
  418. package/tui/screens/__pycache__/diff_screen.cpython-314.pyc +0 -0
  419. package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
  420. package/tui/screens/__pycache__/main.cpython-313.pyc +0 -0
  421. package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
  422. package/tui/screens/__pycache__/permissions.cpython-314.pyc +0 -0
  423. package/tui/screens/__pycache__/projects_app.cpython-314.pyc +0 -0
  424. package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
  425. package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
  426. package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
  427. package/tui/screens/__pycache__/tool_content_screen.cpython-314.pyc +0 -0
  428. package/tui/screens/action_modal.py +0 -94
  429. package/tui/screens/agent_modal.py +0 -175
  430. package/tui/screens/command_edit_modal.py +0 -74
  431. package/tui/screens/component_picker.py +0 -168
  432. package/tui/screens/diff_screen.py +0 -84
  433. package/tui/screens/log.py +0 -553
  434. package/tui/screens/main.py +0 -385
  435. package/tui/screens/main.tcss +0 -655
  436. package/tui/screens/permissions.py +0 -404
  437. package/tui/screens/permissions.tcss +0 -72
  438. package/tui/screens/projects.tcss +0 -80
  439. package/tui/screens/projects_app.py +0 -347
  440. package/tui/screens/projects_screen.py +0 -284
  441. package/tui/screens/session_resume_modal.py +0 -172
  442. package/tui/screens/session_resume_modal.tcss +0 -35
  443. package/tui/screens/sessions.py +0 -164
  444. package/tui/screens/sessions.tcss +0 -119
  445. package/tui/screens/settings.py +0 -254
  446. package/tui/screens/settings.tcss +0 -101
  447. package/tui/screens/store.py +0 -500
  448. package/tui/screens/store.tcss +0 -274
  449. package/tui/screens/tool_content_screen.py +0 -59
  450. package/tui/session_tracker.py +0 -123
  451. package/tui/settings.py +0 -353
  452. package/tui/settings_schema.py +0 -431
  453. package/tui/shell.py +0 -304
  454. package/tui/shell_read.py +0 -42
  455. package/tui/slash_command.py +0 -34
  456. package/tui/tui.tcss +0 -246
  457. package/tui/twitter.py +0 -38
  458. package/tui/version.py +0 -80
  459. package/tui/visuals/__init__.py +0 -0
  460. package/tui/visuals/__pycache__/__init__.cpython-314.pyc +0 -0
  461. package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
  462. package/tui/visuals/columns.py +0 -272
  463. package/tui/widgets/__init__.py +0 -0
  464. package/tui/widgets/__pycache__/__init__.cpython-314.pyc +0 -0
  465. package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
  466. package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
  467. package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
  468. package/tui/widgets/__pycache__/ask_user.cpython-314.pyc +0 -0
  469. package/tui/widgets/__pycache__/condensed_path.cpython-314.pyc +0 -0
  470. package/tui/widgets/__pycache__/context_stats.cpython-314.pyc +0 -0
  471. package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
  472. package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
  473. package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
  474. package/tui/widgets/__pycache__/flash.cpython-314.pyc +0 -0
  475. package/tui/widgets/__pycache__/future_text.cpython-314.pyc +0 -0
  476. package/tui/widgets/__pycache__/grid_select.cpython-314.pyc +0 -0
  477. package/tui/widgets/__pycache__/highlighted_textarea.cpython-314.pyc +0 -0
  478. package/tui/widgets/__pycache__/load_more.cpython-314.pyc +0 -0
  479. package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
  480. package/tui/widgets/__pycache__/markdown_note.cpython-314.pyc +0 -0
  481. package/tui/widgets/__pycache__/menu.cpython-314.pyc +0 -0
  482. package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
  483. package/tui/widgets/__pycache__/non_selectable_label.cpython-314.pyc +0 -0
  484. package/tui/widgets/__pycache__/note.cpython-314.pyc +0 -0
  485. package/tui/widgets/__pycache__/path_search.cpython-314.pyc +0 -0
  486. package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
  487. package/tui/widgets/__pycache__/project_directory_tree.cpython-314.pyc +0 -0
  488. package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
  489. package/tui/widgets/__pycache__/project_summary.cpython-314.pyc +0 -0
  490. package/tui/widgets/__pycache__/prompt.cpython-314.pyc +0 -0
  491. package/tui/widgets/__pycache__/question.cpython-314.pyc +0 -0
  492. package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
  493. package/tui/widgets/__pycache__/session_summary.cpython-314.pyc +0 -0
  494. package/tui/widgets/__pycache__/session_tabs.cpython-314.pyc +0 -0
  495. package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
  496. package/tui/widgets/__pycache__/shell_terminal.cpython-314.pyc +0 -0
  497. package/tui/widgets/__pycache__/side_bar.cpython-314.pyc +0 -0
  498. package/tui/widgets/__pycache__/slash_complete.cpython-314.pyc +0 -0
  499. package/tui/widgets/__pycache__/strike_text.cpython-314.pyc +0 -0
  500. package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
  501. package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
  502. package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
  503. package/tui/widgets/__pycache__/throbber.cpython-314.pyc +0 -0
  504. package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
  505. package/tui/widgets/__pycache__/user_input.cpython-314.pyc +0 -0
  506. package/tui/widgets/acp_content.py +0 -70
  507. package/tui/widgets/agent_response.py +0 -76
  508. package/tui/widgets/agent_thought.py +0 -167
  509. package/tui/widgets/ask_user.py +0 -264
  510. package/tui/widgets/command_pane.py +0 -223
  511. package/tui/widgets/condensed_path.py +0 -115
  512. package/tui/widgets/context_stats.py +0 -66
  513. package/tui/widgets/conversation.py +0 -2641
  514. package/tui/widgets/danger_warning.py +0 -65
  515. package/tui/widgets/diff_view.py +0 -69
  516. package/tui/widgets/directory_input.py +0 -8
  517. package/tui/widgets/flash.py +0 -81
  518. package/tui/widgets/future_text.py +0 -126
  519. package/tui/widgets/grid_select.py +0 -234
  520. package/tui/widgets/highlighted_textarea.py +0 -180
  521. package/tui/widgets/load_more.py +0 -24
  522. package/tui/widgets/mandelbrot.py +0 -294
  523. package/tui/widgets/markdown_note.py +0 -13
  524. package/tui/widgets/menu.py +0 -147
  525. package/tui/widgets/modified_files.py +0 -306
  526. package/tui/widgets/non_selectable_label.py +0 -5
  527. package/tui/widgets/note.py +0 -18
  528. package/tui/widgets/path_search.py +0 -492
  529. package/tui/widgets/plan.py +0 -285
  530. package/tui/widgets/project_directory_tree.py +0 -102
  531. package/tui/widgets/project_grid_select.py +0 -64
  532. package/tui/widgets/project_summary.py +0 -33
  533. package/tui/widgets/prompt.py +0 -795
  534. package/tui/widgets/question.py +0 -360
  535. package/tui/widgets/session_grid_select.py +0 -230
  536. package/tui/widgets/session_summary.py +0 -115
  537. package/tui/widgets/session_tabs.py +0 -188
  538. package/tui/widgets/shell_result.py +0 -35
  539. package/tui/widgets/shell_terminal.py +0 -40
  540. package/tui/widgets/side_bar.py +0 -124
  541. package/tui/widgets/slash_complete.py +0 -235
  542. package/tui/widgets/strike_text.py +0 -70
  543. package/tui/widgets/subagent.py +0 -287
  544. package/tui/widgets/subagent_screen.py +0 -709
  545. package/tui/widgets/terminal.py +0 -576
  546. package/tui/widgets/terminal_tool.py +0 -360
  547. package/tui/widgets/throbber.py +0 -100
  548. package/tui/widgets/tool_call.py +0 -398
  549. package/tui/widgets/user_input.py +0 -23
  550. package/tui/widgets/version.py +0 -5
  551. 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()