@cloudcli-ai/cloudcli 1.36.2 → 1.37.0

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 (539) hide show
  1. package/dist/assets/index-DSlds633.css +32 -0
  2. package/dist/assets/{index-CDGzV0LN.js → index-eGqPeIEu.js} +375 -267
  3. package/dist/index.html +2 -2
  4. package/dist-server/server/constants/config.js.map +1 -1
  5. package/dist-server/server/index.js +73 -1224
  6. package/dist-server/server/index.js.map +1 -1
  7. package/dist-server/server/load-env.js +17 -4
  8. package/dist-server/server/load-env.js.map +1 -1
  9. package/dist-server/server/modules/agent/agent.module.js +37 -0
  10. package/dist-server/server/modules/agent/agent.module.js.map +1 -0
  11. package/dist-server/server/modules/agent/agent.routes.js +1161 -0
  12. package/dist-server/server/modules/agent/agent.routes.js.map +1 -0
  13. package/dist-server/server/modules/agent/index.js +3 -0
  14. package/dist-server/server/modules/agent/index.js.map +1 -0
  15. package/dist-server/server/modules/agent/tests/agent.routes.test.js +176 -0
  16. package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -0
  17. package/dist-server/server/modules/assets/assets.routes.js +56 -15
  18. package/dist-server/server/modules/assets/assets.routes.js.map +1 -1
  19. package/dist-server/server/modules/assets/index.js +2 -2
  20. package/dist-server/server/modules/assets/index.js.map +1 -1
  21. package/dist-server/server/modules/assets/services/image-assets.service.js +39 -1
  22. package/dist-server/server/modules/assets/services/image-assets.service.js.map +1 -1
  23. package/dist-server/server/modules/assets/tests/image-assets.service.test.js +21 -1
  24. package/dist-server/server/modules/assets/tests/image-assets.service.test.js.map +1 -1
  25. package/dist-server/server/{middleware/auth.js → modules/auth/auth.middleware.js} +30 -8
  26. package/dist-server/server/modules/auth/auth.middleware.js.map +1 -0
  27. package/dist-server/server/modules/auth/auth.module.js +29 -0
  28. package/dist-server/server/modules/auth/auth.module.js.map +1 -0
  29. package/dist-server/server/modules/auth/auth.routes.js +45 -0
  30. package/dist-server/server/modules/auth/auth.routes.js.map +1 -0
  31. package/dist-server/server/modules/auth/auth.service.js +113 -0
  32. package/dist-server/server/modules/auth/auth.service.js.map +1 -0
  33. package/dist-server/server/modules/auth/index.js +9 -0
  34. package/dist-server/server/modules/auth/index.js.map +1 -0
  35. package/dist-server/server/modules/auth/tests/auth.service.test.js +80 -0
  36. package/dist-server/server/modules/auth/tests/auth.service.test.js.map +1 -0
  37. package/dist-server/server/{browser-use-mcp.js → modules/browser-use/browser-use-mcp.js} +3 -1
  38. package/dist-server/server/modules/browser-use/browser-use-mcp.js.map +1 -0
  39. package/dist-server/server/modules/browser-use/browser-use.service.js +3 -4
  40. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
  41. package/dist-server/server/modules/browser-use/index.js +9 -0
  42. package/dist-server/server/modules/browser-use/index.js.map +1 -0
  43. package/dist-server/server/modules/cli/cli.js +11 -0
  44. package/dist-server/server/modules/cli/cli.js.map +1 -0
  45. package/dist-server/server/modules/cli/cli.module.js +86 -0
  46. package/dist-server/server/modules/cli/cli.module.js.map +1 -0
  47. package/dist-server/server/modules/cli/cli.service.js +244 -0
  48. package/dist-server/server/modules/cli/cli.service.js.map +1 -0
  49. package/dist-server/server/modules/cli/index.js +3 -0
  50. package/dist-server/server/modules/cli/index.js.map +1 -0
  51. package/dist-server/server/modules/cli/sandbox.service.js +332 -0
  52. package/dist-server/server/modules/cli/sandbox.service.js.map +1 -0
  53. package/dist-server/server/modules/cli/tests/cli.service.test.js +78 -0
  54. package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -0
  55. package/dist-server/server/modules/cli/tests/sandbox.service.test.js +55 -0
  56. package/dist-server/server/modules/cli/tests/sandbox.service.test.js.map +1 -0
  57. package/dist-server/server/modules/commands/commands.module.js +20 -0
  58. package/dist-server/server/modules/commands/commands.module.js.map +1 -0
  59. package/dist-server/server/modules/commands/commands.routes.js +497 -0
  60. package/dist-server/server/modules/commands/commands.routes.js.map +1 -0
  61. package/dist-server/server/modules/commands/index.js +3 -0
  62. package/dist-server/server/modules/commands/index.js.map +1 -0
  63. package/dist-server/server/modules/commands/tests/commands.test.js +96 -0
  64. package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -0
  65. package/dist-server/server/modules/database/index.js +1 -0
  66. package/dist-server/server/modules/database/index.js.map +1 -1
  67. package/dist-server/server/modules/database/migrations.js +13 -0
  68. package/dist-server/server/modules/database/migrations.js.map +1 -1
  69. package/dist-server/server/modules/database/repositories/sessions.db.js +14 -1
  70. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  71. package/dist-server/server/modules/database/schema.js +4 -0
  72. package/dist-server/server/modules/database/schema.js.map +1 -1
  73. package/dist-server/server/modules/file-tree/file-tree.module.js +91 -0
  74. package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -0
  75. package/dist-server/server/modules/file-tree/file-tree.routes.js +208 -0
  76. package/dist-server/server/modules/file-tree/file-tree.routes.js.map +1 -0
  77. package/dist-server/server/modules/file-tree/file-tree.service.js +544 -0
  78. package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -0
  79. package/dist-server/server/modules/file-tree/index.js +3 -0
  80. package/dist-server/server/modules/file-tree/index.js.map +1 -0
  81. package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js +123 -0
  82. package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js.map +1 -0
  83. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +184 -0
  84. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -0
  85. package/dist-server/server/modules/git/git-parsing.service.js +61 -0
  86. package/dist-server/server/modules/git/git-parsing.service.js.map +1 -0
  87. package/dist-server/server/modules/git/git.module.js +14 -0
  88. package/dist-server/server/modules/git/git.module.js.map +1 -0
  89. package/dist-server/server/modules/git/git.routes.js +1339 -0
  90. package/dist-server/server/modules/git/git.routes.js.map +1 -0
  91. package/dist-server/server/modules/git/index.js +3 -0
  92. package/dist-server/server/modules/git/index.js.map +1 -0
  93. package/dist-server/server/modules/git/tests/git-init.routes.test.js +48 -0
  94. package/dist-server/server/modules/git/tests/git-init.routes.test.js.map +1 -0
  95. package/dist-server/server/{routes → modules/git/tests}/git.test.js +1 -1
  96. package/dist-server/server/modules/git/tests/git.test.js.map +1 -0
  97. package/dist-server/server/modules/notifications/index.js +15 -1
  98. package/dist-server/server/modules/notifications/index.js.map +1 -1
  99. package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js +44 -0
  100. package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js.map +1 -0
  101. package/dist-server/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.js} +3 -2
  102. package/dist-server/server/modules/notifications/vapid-keys.service.js.map +1 -0
  103. package/dist-server/server/modules/plugins/index.js +9 -0
  104. package/dist-server/server/modules/plugins/index.js.map +1 -0
  105. package/dist-server/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.js} +3 -2
  106. package/dist-server/server/modules/plugins/plugin-process.service.js.map +1 -0
  107. package/dist-server/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.js} +2 -1
  108. package/dist-server/server/modules/plugins/plugin-registry.service.js.map +1 -0
  109. package/dist-server/server/modules/plugins/plugins.module.js +30 -0
  110. package/dist-server/server/modules/plugins/plugins.module.js.map +1 -0
  111. package/dist-server/server/modules/plugins/plugins.routes.js +70 -0
  112. package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -0
  113. package/dist-server/server/modules/plugins/plugins.service.js +123 -0
  114. package/dist-server/server/modules/plugins/plugins.service.js.map +1 -0
  115. package/dist-server/server/modules/plugins/tests/plugins.service.test.js +28 -0
  116. package/dist-server/server/modules/plugins/tests/plugins.service.test.js.map +1 -0
  117. package/dist-server/server/modules/projects/index.js +5 -0
  118. package/dist-server/server/modules/projects/index.js.map +1 -1
  119. package/dist-server/server/modules/providers/index.js +3 -0
  120. package/dist-server/server/modules/providers/index.js.map +1 -1
  121. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +6 -0
  122. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  123. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +1 -4
  124. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  125. package/dist-server/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +62 -41
  126. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -0
  127. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +6 -4
  128. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
  129. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +23 -4
  130. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  131. package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
  132. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  133. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +1 -4
  134. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  135. package/dist-server/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +48 -34
  136. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -0
  137. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +19 -1
  138. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  139. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +290 -6
  140. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  141. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +5 -0
  142. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -1
  143. package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
  144. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  145. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +1 -4
  146. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
  147. package/dist-server/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +40 -28
  148. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -0
  149. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +21 -14
  150. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
  151. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +2 -0
  152. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
  153. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +7 -5
  154. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  155. package/dist-server/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +38 -25
  156. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -0
  157. package/dist-server/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +27 -4
  158. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.test.js.map +1 -0
  159. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +11 -5
  160. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
  161. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +2 -0
  162. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -1
  163. package/dist-server/server/modules/providers/provider.routes.js +33 -9
  164. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  165. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +5 -1
  166. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  167. package/dist-server/server/modules/providers/services/provider-models.service.js +110 -15
  168. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  169. package/dist-server/server/modules/providers/services/provider-runtime.service.js +66 -0
  170. package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -0
  171. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +268 -0
  172. package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -0
  173. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +2 -0
  174. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  175. package/dist-server/server/modules/providers/services/sessions.service.js +54 -0
  176. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  177. package/dist-server/server/modules/providers/shared/base/abstract.provider.js +2 -3
  178. package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
  179. package/dist-server/server/modules/providers/tests/claude-auth.test.js +122 -0
  180. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -0
  181. package/dist-server/server/modules/providers/tests/claude-sessions.test.js +44 -0
  182. package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -0
  183. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +70 -0
  184. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  185. package/dist-server/server/modules/providers/tests/{provider-image-history.test.js → provider-attachment-history.test.js} +60 -2
  186. package/dist-server/server/modules/providers/tests/provider-attachment-history.test.js.map +1 -0
  187. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +148 -61
  188. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
  189. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js +130 -0
  190. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js.map +1 -0
  191. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +165 -0
  192. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -0
  193. package/dist-server/server/modules/providers/tests/sessions-details.test.js +62 -0
  194. package/dist-server/server/modules/providers/tests/sessions-details.test.js.map +1 -0
  195. package/dist-server/server/modules/providers/tests/skills.test.js +4 -2
  196. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  197. package/dist-server/server/modules/settings/index.js +3 -0
  198. package/dist-server/server/modules/settings/index.js.map +1 -0
  199. package/dist-server/server/modules/settings/settings.module.js +35 -0
  200. package/dist-server/server/modules/settings/settings.module.js.map +1 -0
  201. package/dist-server/server/modules/settings/settings.routes.js +34 -0
  202. package/dist-server/server/modules/settings/settings.routes.js.map +1 -0
  203. package/dist-server/server/modules/settings/settings.service.js +116 -0
  204. package/dist-server/server/modules/settings/settings.service.js.map +1 -0
  205. package/dist-server/server/modules/settings/tests/settings.service.test.js +48 -0
  206. package/dist-server/server/modules/settings/tests/settings.service.test.js.map +1 -0
  207. package/dist-server/server/modules/system/index.js +3 -0
  208. package/dist-server/server/modules/system/index.js.map +1 -0
  209. package/dist-server/server/modules/system/system.module.js +44 -0
  210. package/dist-server/server/modules/system/system.module.js.map +1 -0
  211. package/dist-server/server/modules/system/system.routes.js +16 -0
  212. package/dist-server/server/modules/system/system.routes.js.map +1 -0
  213. package/dist-server/server/modules/system/system.service.js +45 -0
  214. package/dist-server/server/modules/system/system.service.js.map +1 -0
  215. package/dist-server/server/modules/system/tests/system.service.test.js +100 -0
  216. package/dist-server/server/modules/system/tests/system.service.test.js.map +1 -0
  217. package/dist-server/server/modules/taskmaster/index.js +3 -0
  218. package/dist-server/server/modules/taskmaster/index.js.map +1 -0
  219. package/dist-server/server/modules/taskmaster/taskmaster.module.js +20 -0
  220. package/dist-server/server/modules/taskmaster/taskmaster.module.js.map +1 -0
  221. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +1372 -0
  222. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -0
  223. package/dist-server/server/modules/taskmaster/taskmaster.service.js +107 -0
  224. package/dist-server/server/modules/taskmaster/taskmaster.service.js.map +1 -0
  225. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +132 -0
  226. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -0
  227. package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js +85 -0
  228. package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js.map +1 -0
  229. package/dist-server/server/modules/user/index.js +3 -0
  230. package/dist-server/server/modules/user/index.js.map +1 -0
  231. package/dist-server/server/modules/user/tests/user.service.test.js +53 -0
  232. package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -0
  233. package/dist-server/server/modules/user/user.module.js +47 -0
  234. package/dist-server/server/modules/user/user.module.js.map +1 -0
  235. package/dist-server/server/modules/user/user.routes.js +44 -0
  236. package/dist-server/server/modules/user/user.routes.js.map +1 -0
  237. package/dist-server/server/modules/user/user.service.js +59 -0
  238. package/dist-server/server/modules/user/user.service.js.map +1 -0
  239. package/dist-server/server/modules/voice/index.js +3 -0
  240. package/dist-server/server/modules/voice/index.js.map +1 -0
  241. package/dist-server/server/modules/voice/tests/voice.service.test.js +86 -0
  242. package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -0
  243. package/dist-server/server/modules/voice/voice.module.js +44 -0
  244. package/dist-server/server/modules/voice/voice.module.js.map +1 -0
  245. package/dist-server/server/modules/voice/voice.routes.js +87 -0
  246. package/dist-server/server/modules/voice/voice.routes.js.map +1 -0
  247. package/dist-server/server/modules/voice/voice.service.js +148 -0
  248. package/dist-server/server/modules/voice/voice.service.js.map +1 -0
  249. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +2 -2
  250. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +40 -29
  251. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  252. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +76 -8
  253. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  254. package/dist-server/server/modules/websocket/services/websocket-server.service.js +51 -20
  255. package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
  256. package/dist-server/server/modules/websocket/tests/{chat-image-filter.test.js → chat-attachment-filter.test.js} +10 -3
  257. package/dist-server/server/modules/websocket/tests/chat-attachment-filter.test.js.map +1 -0
  258. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +97 -0
  259. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -0
  260. package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js +61 -0
  261. package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js.map +1 -0
  262. package/dist-server/server/modules/worktrees/index.js +3 -0
  263. package/dist-server/server/modules/worktrees/index.js.map +1 -0
  264. package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js +37 -0
  265. package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js.map +1 -0
  266. package/dist-server/server/modules/worktrees/services/worktree-create.service.js +67 -0
  267. package/dist-server/server/modules/worktrees/services/worktree-create.service.js.map +1 -0
  268. package/dist-server/server/modules/worktrees/services/worktree-git.service.js +172 -0
  269. package/dist-server/server/modules/worktrees/services/worktree-git.service.js.map +1 -0
  270. package/dist-server/server/modules/worktrees/services/worktree-list.service.js +91 -0
  271. package/dist-server/server/modules/worktrees/services/worktree-list.service.js.map +1 -0
  272. package/dist-server/server/modules/worktrees/services/worktree-merge.service.js +118 -0
  273. package/dist-server/server/modules/worktrees/services/worktree-merge.service.js.map +1 -0
  274. package/dist-server/server/modules/worktrees/services/worktree-open.service.js +58 -0
  275. package/dist-server/server/modules/worktrees/services/worktree-open.service.js.map +1 -0
  276. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js +65 -0
  277. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js.map +1 -0
  278. package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js +58 -0
  279. package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js.map +1 -0
  280. package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js +85 -0
  281. package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js.map +1 -0
  282. package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js +58 -0
  283. package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js.map +1 -0
  284. package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js +34 -0
  285. package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js.map +1 -0
  286. package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js +137 -0
  287. package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js.map +1 -0
  288. package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js +99 -0
  289. package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js.map +1 -0
  290. package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js +157 -0
  291. package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js.map +1 -0
  292. package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js +167 -0
  293. package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js.map +1 -0
  294. package/dist-server/server/modules/worktrees/worktrees.module.js +97 -0
  295. package/dist-server/server/modules/worktrees/worktrees.module.js.map +1 -0
  296. package/dist-server/server/modules/worktrees/worktrees.routes.js +84 -0
  297. package/dist-server/server/modules/worktrees/worktrees.routes.js.map +1 -0
  298. package/dist-server/server/shared/image-attachments.js +90 -17
  299. package/dist-server/server/shared/image-attachments.js.map +1 -1
  300. package/dist-server/server/shared/tests/image-attachments.test.js +43 -1
  301. package/dist-server/server/shared/tests/image-attachments.test.js.map +1 -1
  302. package/dist-server/server/shared/utils.js +66 -142
  303. package/dist-server/server/shared/utils.js.map +1 -1
  304. package/electron/localServer.js +1 -2
  305. package/package.json +8 -4
  306. package/server/index.ts +401 -0
  307. package/server/{load-env.js → load-env.ts} +18 -5
  308. package/server/modules/agent/agent.module.ts +51 -0
  309. package/server/modules/agent/agent.routes.ts +1287 -0
  310. package/server/modules/agent/index.ts +2 -0
  311. package/server/modules/agent/tests/agent.routes.test.ts +205 -0
  312. package/server/modules/assets/assets.routes.ts +63 -16
  313. package/server/modules/assets/index.ts +2 -2
  314. package/server/modules/assets/services/image-assets.service.ts +46 -1
  315. package/server/modules/assets/tests/image-assets.service.test.ts +28 -0
  316. package/server/{middleware/auth.js → modules/auth/auth.middleware.ts} +38 -7
  317. package/server/modules/auth/auth.module.ts +38 -0
  318. package/server/modules/auth/auth.routes.ts +57 -0
  319. package/server/modules/auth/auth.service.ts +155 -0
  320. package/server/modules/auth/index.ts +9 -0
  321. package/server/modules/auth/tests/auth.service.test.ts +95 -0
  322. package/server/{browser-use-mcp.ts → modules/browser-use/browser-use-mcp.ts} +3 -1
  323. package/server/modules/browser-use/browser-use.service.ts +3 -4
  324. package/server/modules/browser-use/index.ts +8 -0
  325. package/server/modules/cli/cli.module.ts +96 -0
  326. package/server/modules/cli/cli.service.ts +281 -0
  327. package/server/modules/cli/cli.ts +13 -0
  328. package/server/modules/cli/index.ts +2 -0
  329. package/server/modules/cli/sandbox.service.ts +413 -0
  330. package/server/modules/cli/tests/cli.service.test.ts +91 -0
  331. package/server/modules/cli/tests/sandbox.service.test.ts +61 -0
  332. package/server/modules/commands/commands.module.ts +22 -0
  333. package/server/{routes/commands.js → modules/commands/commands.routes.ts} +44 -36
  334. package/server/modules/commands/index.ts +2 -0
  335. package/server/modules/commands/tests/commands.test.ts +119 -0
  336. package/server/modules/database/index.ts +1 -0
  337. package/server/modules/database/migrations.ts +15 -0
  338. package/server/modules/database/repositories/sessions.db.ts +19 -1
  339. package/server/modules/database/schema.ts +4 -0
  340. package/server/modules/file-tree/file-tree.module.ts +112 -0
  341. package/server/modules/file-tree/file-tree.routes.ts +258 -0
  342. package/server/modules/file-tree/file-tree.service.ts +616 -0
  343. package/server/modules/file-tree/index.ts +2 -0
  344. package/server/modules/file-tree/tests/file-tree.routes.test.ts +157 -0
  345. package/server/modules/file-tree/tests/file-tree.service.test.ts +220 -0
  346. package/server/modules/git/git-parsing.service.ts +66 -0
  347. package/server/modules/git/git.module.ts +22 -0
  348. package/server/{routes/git.js → modules/git/git.routes.ts} +91 -132
  349. package/server/modules/git/index.ts +2 -0
  350. package/server/modules/git/tests/git-init.routes.test.ts +52 -0
  351. package/server/{routes/git.test.js → modules/git/tests/git.test.ts} +3 -3
  352. package/server/modules/notifications/index.ts +9 -0
  353. package/server/modules/notifications/tests/notification-orchestrator.integration.test.ts +55 -0
  354. package/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.ts} +3 -1
  355. package/server/modules/plugins/index.ts +9 -0
  356. package/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.ts} +3 -1
  357. package/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.ts} +1 -0
  358. package/server/modules/plugins/plugins.module.ts +33 -0
  359. package/server/modules/plugins/plugins.routes.ts +66 -0
  360. package/server/modules/plugins/plugins.service.ts +147 -0
  361. package/server/modules/plugins/tests/plugins.service.test.ts +32 -0
  362. package/server/modules/projects/index.ts +5 -0
  363. package/server/modules/providers/README.md +41 -9
  364. package/server/modules/providers/index.ts +4 -0
  365. package/server/modules/providers/list/claude/claude-auth.provider.ts +8 -0
  366. package/server/modules/providers/list/claude/claude-models.provider.ts +1 -12
  367. package/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +66 -44
  368. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +6 -4
  369. package/server/modules/providers/list/claude/claude-sessions.provider.ts +27 -4
  370. package/server/modules/providers/list/claude/claude.provider.ts +3 -0
  371. package/server/modules/providers/list/codex/codex-models.provider.ts +0 -9
  372. package/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +54 -36
  373. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +21 -1
  374. package/server/modules/providers/list/codex/codex-sessions.provider.ts +331 -7
  375. package/server/modules/providers/list/codex/codex-skills.provider.ts +5 -0
  376. package/server/modules/providers/list/codex/codex.provider.ts +3 -0
  377. package/server/modules/providers/list/cursor/cursor-models.provider.ts +0 -9
  378. package/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +58 -27
  379. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +26 -15
  380. package/server/modules/providers/list/cursor/cursor.provider.ts +3 -0
  381. package/server/modules/providers/list/opencode/opencode-models.provider.ts +7 -10
  382. package/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +56 -28
  383. package/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +41 -3
  384. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +13 -5
  385. package/server/modules/providers/list/opencode/opencode.provider.ts +3 -0
  386. package/server/modules/providers/provider.routes.ts +47 -10
  387. package/server/modules/providers/services/provider-capabilities.service.ts +7 -1
  388. package/server/modules/providers/services/provider-models.service.ts +131 -23
  389. package/server/modules/providers/services/provider-runtime.service.ts +109 -0
  390. package/server/modules/providers/services/provider-token-usage.service.ts +357 -0
  391. package/server/modules/providers/services/sessions-watcher.service.ts +2 -0
  392. package/server/modules/providers/services/sessions.service.ts +79 -0
  393. package/server/modules/providers/shared/base/abstract.provider.ts +4 -3
  394. package/server/modules/providers/tests/claude-auth.test.ts +150 -0
  395. package/server/modules/providers/tests/claude-sessions.test.ts +61 -0
  396. package/server/modules/providers/tests/codex-sessions.test.ts +81 -0
  397. package/server/modules/providers/tests/{provider-image-history.test.ts → provider-attachment-history.test.ts} +68 -1
  398. package/server/modules/providers/tests/provider-models.service.test.ts +450 -349
  399. package/server/modules/providers/tests/provider-runtime.service.test.ts +142 -0
  400. package/server/modules/providers/tests/provider-token-usage.service.test.ts +185 -0
  401. package/server/modules/providers/tests/sessions-details.test.ts +73 -0
  402. package/server/modules/providers/tests/skills.test.ts +9 -2
  403. package/server/modules/settings/index.ts +2 -0
  404. package/server/modules/settings/settings.module.ts +50 -0
  405. package/server/modules/settings/settings.routes.ts +51 -0
  406. package/server/modules/settings/settings.service.ts +186 -0
  407. package/server/modules/settings/tests/settings.service.test.ts +54 -0
  408. package/server/modules/system/index.ts +2 -0
  409. package/server/modules/system/system.module.ts +62 -0
  410. package/server/modules/system/system.routes.ts +21 -0
  411. package/server/modules/system/system.service.ts +76 -0
  412. package/server/modules/system/tests/system.service.test.ts +118 -0
  413. package/server/modules/taskmaster/index.ts +2 -0
  414. package/server/modules/taskmaster/taskmaster.module.ts +24 -0
  415. package/server/modules/taskmaster/taskmaster.routes.ts +1482 -0
  416. package/server/modules/taskmaster/taskmaster.service.ts +132 -0
  417. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +144 -0
  418. package/server/modules/taskmaster/tests/taskmaster.service.test.ts +97 -0
  419. package/server/modules/user/index.ts +2 -0
  420. package/server/modules/user/tests/user.service.test.ts +61 -0
  421. package/server/modules/user/user.module.ts +58 -0
  422. package/server/modules/user/user.routes.ts +50 -0
  423. package/server/modules/user/user.service.ts +85 -0
  424. package/server/modules/voice/index.ts +2 -0
  425. package/server/modules/voice/tests/voice.service.test.ts +98 -0
  426. package/server/modules/voice/voice.module.ts +47 -0
  427. package/server/modules/voice/voice.routes.ts +113 -0
  428. package/server/modules/voice/voice.service.ts +192 -0
  429. package/server/modules/websocket/README.md +7 -7
  430. package/server/modules/websocket/services/chat-session-writer.service.ts +2 -2
  431. package/server/modules/websocket/services/chat-websocket.service.ts +73 -61
  432. package/server/modules/websocket/services/shell-websocket.service.ts +88 -12
  433. package/server/modules/websocket/services/websocket-server.service.ts +63 -19
  434. package/server/modules/websocket/tests/{chat-image-filter.test.ts → chat-attachment-filter.test.ts} +17 -2
  435. package/server/modules/websocket/tests/shell-websocket.service.test.ts +119 -0
  436. package/server/modules/websocket/tests/websocket-heartbeat.service.test.ts +78 -0
  437. package/server/modules/worktrees/index.ts +2 -0
  438. package/server/modules/worktrees/services/worktree-create-and-open.service.ts +53 -0
  439. package/server/modules/worktrees/services/worktree-create.service.ts +104 -0
  440. package/server/modules/worktrees/services/worktree-git.service.ts +207 -0
  441. package/server/modules/worktrees/services/worktree-list.service.ts +143 -0
  442. package/server/modules/worktrees/services/worktree-merge.service.ts +157 -0
  443. package/server/modules/worktrees/services/worktree-open.service.ts +85 -0
  444. package/server/modules/worktrees/services/worktree-remove.service.ts +88 -0
  445. package/server/modules/worktrees/tests/worktree-create-and-open.service.test.ts +68 -0
  446. package/server/modules/worktrees/tests/worktree-create.service.test.ts +142 -0
  447. package/server/modules/worktrees/tests/worktree-git.service.test.ts +84 -0
  448. package/server/modules/worktrees/tests/worktree-list.service.test.ts +45 -0
  449. package/server/modules/worktrees/tests/worktree-merge.service.test.ts +213 -0
  450. package/server/modules/worktrees/tests/worktree-open.service.test.ts +122 -0
  451. package/server/modules/worktrees/tests/worktree-remove.service.test.ts +227 -0
  452. package/server/modules/worktrees/tests/worktrees.routes.test.ts +199 -0
  453. package/server/modules/worktrees/worktrees.module.ts +113 -0
  454. package/server/modules/worktrees/worktrees.routes.ts +116 -0
  455. package/server/shared/image-attachments.ts +112 -18
  456. package/server/shared/interfaces.ts +29 -19
  457. package/server/shared/tests/image-attachments.test.ts +55 -0
  458. package/server/shared/types.ts +658 -21
  459. package/server/shared/utils.ts +77 -212
  460. package/dist/assets/index-DLmhQ15L.css +0 -32
  461. package/dist-server/server/browser-use-mcp.js.map +0 -1
  462. package/dist-server/server/claude-sdk.js.map +0 -1
  463. package/dist-server/server/cli.js +0 -647
  464. package/dist-server/server/cli.js.map +0 -1
  465. package/dist-server/server/cursor-cli.js.map +0 -1
  466. package/dist-server/server/middleware/auth.js.map +0 -1
  467. package/dist-server/server/modules/providers/tests/provider-image-history.test.js.map +0 -1
  468. package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js.map +0 -1
  469. package/dist-server/server/openai-codex.js.map +0 -1
  470. package/dist-server/server/opencode-cli.js.map +0 -1
  471. package/dist-server/server/opencode-cli.test.js.map +0 -1
  472. package/dist-server/server/routes/agent.js +0 -1155
  473. package/dist-server/server/routes/agent.js.map +0 -1
  474. package/dist-server/server/routes/auth.js +0 -119
  475. package/dist-server/server/routes/auth.js.map +0 -1
  476. package/dist-server/server/routes/commands.js +0 -492
  477. package/dist-server/server/routes/commands.js.map +0 -1
  478. package/dist-server/server/routes/cursor.js +0 -49
  479. package/dist-server/server/routes/cursor.js.map +0 -1
  480. package/dist-server/server/routes/git.js +0 -1389
  481. package/dist-server/server/routes/git.js.map +0 -1
  482. package/dist-server/server/routes/git.test.js.map +0 -1
  483. package/dist-server/server/routes/mcp-utils.js +0 -29
  484. package/dist-server/server/routes/mcp-utils.js.map +0 -1
  485. package/dist-server/server/routes/plugins.js +0 -266
  486. package/dist-server/server/routes/plugins.js.map +0 -1
  487. package/dist-server/server/routes/settings.js +0 -249
  488. package/dist-server/server/routes/settings.js.map +0 -1
  489. package/dist-server/server/routes/taskmaster.js +0 -1362
  490. package/dist-server/server/routes/taskmaster.js.map +0 -1
  491. package/dist-server/server/routes/tests/commands.test.js +0 -76
  492. package/dist-server/server/routes/tests/commands.test.js.map +0 -1
  493. package/dist-server/server/routes/user.js +0 -116
  494. package/dist-server/server/routes/user.js.map +0 -1
  495. package/dist-server/server/services/notification-orchestrator.js +0 -2
  496. package/dist-server/server/services/notification-orchestrator.js.map +0 -1
  497. package/dist-server/server/services/tests/notification-orchestrator.test.js +0 -64
  498. package/dist-server/server/services/tests/notification-orchestrator.test.js.map +0 -1
  499. package/dist-server/server/services/vapid-keys.js.map +0 -1
  500. package/dist-server/server/utils/colors.js +0 -20
  501. package/dist-server/server/utils/colors.js.map +0 -1
  502. package/dist-server/server/utils/commandParser.js +0 -255
  503. package/dist-server/server/utils/commandParser.js.map +0 -1
  504. package/dist-server/server/utils/gitConfig.js +0 -37
  505. package/dist-server/server/utils/gitConfig.js.map +0 -1
  506. package/dist-server/server/utils/mcp-detector.js +0 -134
  507. package/dist-server/server/utils/mcp-detector.js.map +0 -1
  508. package/dist-server/server/utils/plugin-loader.js.map +0 -1
  509. package/dist-server/server/utils/plugin-process-manager.js.map +0 -1
  510. package/dist-server/server/utils/runtime-paths.js +0 -30
  511. package/dist-server/server/utils/runtime-paths.js.map +0 -1
  512. package/dist-server/server/utils/taskmaster-websocket.js +0 -124
  513. package/dist-server/server/utils/taskmaster-websocket.js.map +0 -1
  514. package/dist-server/server/utils/url-detection.js +0 -58
  515. package/dist-server/server/utils/url-detection.js.map +0 -1
  516. package/dist-server/server/voice-proxy.js +0 -226
  517. package/dist-server/server/voice-proxy.js.map +0 -1
  518. package/server/cli.js +0 -695
  519. package/server/index.js +0 -1651
  520. package/server/routes/agent.js +0 -1257
  521. package/server/routes/auth.js +0 -137
  522. package/server/routes/cursor.js +0 -52
  523. package/server/routes/mcp-utils.js +0 -31
  524. package/server/routes/plugins.js +0 -307
  525. package/server/routes/settings.js +0 -282
  526. package/server/routes/taskmaster.js +0 -1470
  527. package/server/routes/tests/commands.test.js +0 -82
  528. package/server/routes/user.js +0 -124
  529. package/server/services/notification-orchestrator.js +0 -7
  530. package/server/services/tests/notification-orchestrator.test.js +0 -80
  531. package/server/utils/colors.js +0 -21
  532. package/server/utils/commandParser.js +0 -305
  533. package/server/utils/gitConfig.js +0 -35
  534. package/server/utils/mcp-detector.js +0 -147
  535. package/server/utils/runtime-paths.js +0 -37
  536. package/server/utils/taskmaster-websocket.js +0 -135
  537. package/server/utils/url-detection.js +0 -71
  538. package/server/voice-proxy.js +0 -224
  539. /package/server/constants/{config.js → config.ts} +0 -0
@@ -2,7 +2,7 @@ import crypto from 'node:crypto';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
 
5
- import { parseImagesInputTag } from '@/shared/image-attachments.js';
5
+ import { parseFilesInputTag, parseImagesInputTag } from '@/shared/image-attachments.js';
6
6
  import type { IProviderSessions } from '@/shared/interfaces.js';
7
7
  import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
8
8
  import {
@@ -85,16 +85,22 @@ function unwrapUserQueryText(value: string, role: 'user' | 'assistant'): string
85
85
  function extractUserTextAndImages(
86
86
  value: string,
87
87
  role: 'user' | 'assistant',
88
- ): { text: string; images?: Array<{ path: string; name?: string }> } {
88
+ ): {
89
+ text: string;
90
+ images?: Array<{ path: string; name?: string }>;
91
+ files?: Array<{ path: string; name?: string }>;
92
+ } {
89
93
  const unwrapped = unwrapUserQueryText(value, role);
90
94
  if (role !== 'user') {
91
95
  return { text: unwrapped };
92
96
  }
93
97
 
94
- const { text, attachments } = parseImagesInputTag(unwrapped);
98
+ const parsedImages = parseImagesInputTag(unwrapped);
99
+ const parsedFiles = parseFilesInputTag(parsedImages.text);
95
100
  return {
96
- text,
97
- images: attachments.length > 0 ? attachments : undefined,
101
+ text: parsedFiles.text,
102
+ images: parsedImages.attachments.length > 0 ? parsedImages.attachments : undefined,
103
+ files: parsedFiles.attachments.length > 0 ? parsedFiles.attachments : undefined,
98
104
  };
99
105
  }
100
106
 
@@ -471,16 +477,18 @@ export class CursorSessionsProvider implements IProviderSessions {
471
477
  text = unwrapUserQueryText(content.message.content, role);
472
478
  }
473
479
  }
474
- const { text: cleanText, images } = role === 'user'
480
+ const { text: cleanText, images, files } = role === 'user'
475
481
  ? (() => {
476
- const parsed = parseImagesInputTag(text);
482
+ const parsedImages = parseImagesInputTag(text);
483
+ const parsedFiles = parseFilesInputTag(parsedImages.text);
477
484
  return {
478
- text: parsed.text,
479
- images: parsed.attachments.length > 0 ? parsed.attachments : undefined,
485
+ text: parsedFiles.text,
486
+ images: parsedImages.attachments.length > 0 ? parsedImages.attachments : undefined,
487
+ files: parsedFiles.attachments.length > 0 ? parsedFiles.attachments : undefined,
480
488
  };
481
489
  })()
482
- : { text, images: undefined };
483
- if (cleanText?.trim() || images) {
490
+ : { text, images: undefined, files: undefined };
491
+ if (cleanText?.trim() || images || files) {
484
492
  messages.push(createNormalizedMessage({
485
493
  id: baseId,
486
494
  sessionId,
@@ -490,6 +498,7 @@ export class CursorSessionsProvider implements IProviderSessions {
490
498
  role,
491
499
  content: cleanText,
492
500
  images,
501
+ files,
493
502
  sequence: blob.sequence,
494
503
  rowid: blob.rowid,
495
504
  }));
@@ -541,8 +550,8 @@ export class CursorSessionsProvider implements IProviderSessions {
541
550
  }
542
551
 
543
552
  if (part?.type === 'text' && part?.text) {
544
- const { text: normalizedPartText, images } = extractUserTextAndImages(part.text, role);
545
- if (!normalizedPartText && !images) {
553
+ const { text: normalizedPartText, images, files } = extractUserTextAndImages(part.text, role);
554
+ if (!normalizedPartText && !images && !files) {
546
555
  continue;
547
556
  }
548
557
  messages.push(createNormalizedMessage({
@@ -554,6 +563,7 @@ export class CursorSessionsProvider implements IProviderSessions {
554
563
  role,
555
564
  content: normalizedPartText,
556
565
  images,
566
+ files,
557
567
  sequence: blob.sequence,
558
568
  rowid: blob.rowid,
559
569
  }));
@@ -593,8 +603,8 @@ export class CursorSessionsProvider implements IProviderSessions {
593
603
  && content.content.trim()
594
604
  && !isInternalCursorText(content.content)
595
605
  ) {
596
- const { text: normalizedText, images } = extractUserTextAndImages(content.content, role);
597
- if (!normalizedText && !images) {
606
+ const { text: normalizedText, images, files } = extractUserTextAndImages(content.content, role);
607
+ if (!normalizedText && !images && !files) {
598
608
  continue;
599
609
  }
600
610
  messages.push(createNormalizedMessage({
@@ -606,6 +616,7 @@ export class CursorSessionsProvider implements IProviderSessions {
606
616
  role,
607
617
  content: normalizedText,
608
618
  images,
619
+ files,
609
620
  sequence: blob.sequence,
610
621
  rowid: blob.rowid,
611
622
  }));
@@ -1,6 +1,7 @@
1
1
  import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
2
2
  import { CursorProviderAuth } from '@/modules/providers/list/cursor/cursor-auth.provider.js';
3
3
  import { CursorProviderModels } from '@/modules/providers/list/cursor/cursor-models.provider.js';
4
+ import { cursorRuntime } from '@/modules/providers/list/cursor/cursor-runtime.provider.js';
4
5
  import { CursorMcpProvider } from '@/modules/providers/list/cursor/cursor-mcp.provider.js';
5
6
  import { CursorSessionSynchronizer } from '@/modules/providers/list/cursor/cursor-session-synchronizer.provider.js';
6
7
  import { CursorSessionsProvider } from '@/modules/providers/list/cursor/cursor-sessions.provider.js';
@@ -8,12 +9,14 @@ import { CursorSkillsProvider } from '@/modules/providers/list/cursor/cursor-ski
8
9
  import type {
9
10
  IProviderAuth,
10
11
  IProviderModels,
12
+ IProviderRuntime,
11
13
  IProviderSessionSynchronizer,
12
14
  IProviderSkills,
13
15
  IProviderSessions,
14
16
  } from '@/shared/interfaces.js';
15
17
 
16
18
  export class CursorProvider extends AbstractProvider {
19
+ readonly runtime: IProviderRuntime = cursorRuntime;
17
20
  readonly models: IProviderModels = new CursorProviderModels();
18
21
  readonly mcp = new CursorMcpProvider();
19
22
  readonly auth: IProviderAuth = new CursorProviderAuth();
@@ -1,20 +1,18 @@
1
1
  import Database from 'better-sqlite3';
2
2
  import crossSpawn from 'cross-spawn';
3
3
 
4
+ import { sessionsDb } from '@/modules/database/index.js';
4
5
  import type { IProviderModels } from '@/shared/interfaces.js';
5
6
  import type {
6
- ProviderChangeActiveModelInput,
7
7
  ProviderCurrentActiveModel,
8
8
  ProviderModelOption,
9
9
  ProviderModelsDefinition,
10
- ProviderSessionActiveModelChange,
11
10
  } from '@/shared/types.js';
12
11
  import {
13
12
  buildDefaultProviderCurrentActiveModel,
14
13
  getOpenCodeDatabasePath,
15
14
  readObjectRecord,
16
15
  readOptionalString,
17
- writeProviderSessionActiveModelChange,
18
16
  } from '@/shared/utils.js';
19
17
 
20
18
  export const OPENCODE_FALLBACK_MODELS: ProviderModelsDefinition = {
@@ -464,6 +462,11 @@ export class OpenCodeProviderModels implements IProviderModels {
464
462
  return buildDefaultProviderCurrentActiveModel(await this.getSupportedModels());
465
463
  }
466
464
 
465
+ // OpenCode's `session` table is keyed by its own session id, so the stable
466
+ // app id has to be translated first; sessions discovered on disk store the
467
+ // provider id in both columns and resolve to themselves.
468
+ const providerSessionId = sessionsDb.getSessionById(sessionId)?.provider_session_id ?? sessionId;
469
+
467
470
  try {
468
471
  const dbPath = getOpenCodeDatabasePath();
469
472
  const db = new Database(dbPath, { readonly: true, fileMustExist: true });
@@ -481,7 +484,7 @@ export class OpenCodeProviderModels implements IProviderModels {
481
484
  WHERE s.id = ?
482
485
  ORDER BY COALESCE(s.time_updated, s.time_created, 0) DESC
483
486
  LIMIT 1
484
- `).get(sessionId) as {
487
+ `).get(providerSessionId) as {
485
488
  sessionId?: string;
486
489
  model?: unknown;
487
490
  agent?: string | null;
@@ -505,10 +508,4 @@ export class OpenCodeProviderModels implements IProviderModels {
505
508
 
506
509
  return buildDefaultProviderCurrentActiveModel(await this.getSupportedModels());
507
510
  }
508
-
509
- async changeActiveModel(
510
- input: ProviderChangeActiveModelInput,
511
- ): Promise<ProviderSessionActiveModelChange> {
512
- return writeProviderSessionActiveModelChange('opencode', input);
513
- }
514
511
  }
@@ -3,12 +3,13 @@ import fsSync from 'node:fs';
3
3
  import crossSpawn from 'cross-spawn';
4
4
  import Database from 'better-sqlite3';
5
5
 
6
- import { appendImagesInputTag } from './shared/image-attachments.js';
7
- import { sessionsService } from './modules/providers/services/sessions.service.js';
8
- import { providerAuthService } from './modules/providers/services/provider-auth.service.js';
9
- import { providerModelsService } from './modules/providers/services/provider-models.service.js';
10
- import { notifyRunFailed, notifyRunStopped } from './services/notification-orchestrator.js';
11
- import { createCompleteMessage, createNormalizedMessage, flattenPromptForWindowsShell, getOpenCodeDatabasePath } from './shared/utils.js';
6
+ import {
7
+ appendFilesInputTag,
8
+ appendImagesInputTag,
9
+ normalizeAttachmentDescriptors
10
+ } from '@/shared/image-attachments.js';
11
+ import { notifyRunFailed, notifyRunStopped } from '@/modules/notifications/index.js';
12
+ import { createCompleteMessage, createNormalizedMessage, flattenPromptForWindowsShell, getOpenCodeDatabasePath } from '@/shared/utils.js';
12
13
 
13
14
  // cross-spawn resolves .cmd shims/PATHEXT on Windows and delegates to
14
15
  // child_process.spawn everywhere else.
@@ -122,12 +123,27 @@ function readOpenCodeTokenUsage(sessionId) {
122
123
  }
123
124
  }
124
125
 
125
- async function spawnOpenCode(command, options = {}, ws) {
126
+ async function spawnOpenCode(command, options = {}, ws, context) {
126
127
  return new Promise((resolve, reject) => {
127
- const { sessionId, projectPath, cwd, model, effort, sessionSummary, images, permissionMode } = options;
128
+ const {
129
+ sessionId,
130
+ projectPath,
131
+ cwd,
132
+ model,
133
+ effort,
134
+ sessionSummary,
135
+ images,
136
+ files,
137
+ permissionMode
138
+ } = options;
139
+ // Callers pass the stable app session id; the CLI resumes with the
140
+ // provider-native id recorded on the session row.
141
+ const providerSessionId = context.resolveProviderSessionId(sessionId);
128
142
  const workingDir = cwd || projectPath || process.cwd();
143
+ // Process-map key: the app session id when the caller supplied one, so
144
+ // abort-by-app-id always works.
129
145
  const processKey = sessionId || Date.now().toString();
130
- let capturedSessionId = sessionId || null;
146
+ let capturedSessionId = providerSessionId;
131
147
  let sessionCreatedSent = false;
132
148
  let stdoutLineBuffer = '';
133
149
  let terminalNotificationSent = false;
@@ -142,7 +158,8 @@ async function spawnOpenCode(command, options = {}, ws) {
142
158
  }
143
159
 
144
160
  terminalNotificationSent = true;
145
- const finalSessionId = capturedSessionId || sessionId || processKey;
161
+ // Notifications are app-facing, so they carry the app session id.
162
+ const finalSessionId = sessionId || capturedSessionId || processKey;
146
163
  if (code === 0 && !error) {
147
164
  notifyRunStopped({
148
165
  userId: ws?.userId || null,
@@ -169,7 +186,9 @@ async function spawnOpenCode(command, options = {}, ws) {
169
186
  }
170
187
 
171
188
  capturedSessionId = nextSessionId;
172
- if (processKey !== capturedSessionId && opencodeProcess) {
189
+ // Legacy/direct callers without an app session id re-key the process
190
+ // under the provider-native id once it is known.
191
+ if (!sessionId && processKey !== capturedSessionId && opencodeProcess) {
173
192
  activeOpenCodeProcesses.delete(processKey);
174
193
  activeOpenCodeProcesses.set(capturedSessionId, opencodeProcess);
175
194
  }
@@ -181,7 +200,7 @@ async function spawnOpenCode(command, options = {}, ws) {
181
200
  ws.setSessionId(capturedSessionId);
182
201
  }
183
202
 
184
- if (!sessionId && !sessionCreatedSent) {
203
+ if (!providerSessionId && !sessionCreatedSent) {
185
204
  sessionCreatedSent = true;
186
205
  ws.send(createNormalizedMessage({
187
206
  kind: 'session_created',
@@ -212,11 +231,7 @@ async function spawnOpenCode(command, options = {}, ws) {
212
231
 
213
232
  try {
214
233
  registerSession(readOpenCodeSessionId(response));
215
- const normalized = sessionsService.normalizeMessage(
216
- 'opencode',
217
- response,
218
- capturedSessionId || sessionId || null,
219
- );
234
+ const normalized = context.normalizeMessage(response, capturedSessionId || sessionId || null);
220
235
  for (const msg of normalized) {
221
236
  ws.send(msg);
222
237
  }
@@ -232,10 +247,10 @@ async function spawnOpenCode(command, options = {}, ws) {
232
247
  }
233
248
  };
234
249
 
235
- void providerModelsService.resolveResumeModel('opencode', sessionId, model).then(async (resolvedModel) => {
250
+ void context.resolveResumeModel(sessionId, model).then(async (resolvedModel) => {
236
251
  let effortModels = null;
237
252
  try {
238
- effortModels = (await providerModelsService.getProviderModels('opencode')).models;
253
+ effortModels = await context.getProviderModels();
239
254
  } catch (error) {
240
255
  console.warn('[OpenCode] Unable to load provider models for effort validation:', error);
241
256
  }
@@ -246,8 +261,8 @@ async function spawnOpenCode(command, options = {}, ws) {
246
261
  // Relying on the child-process cwd alone is not enough on Linux, where
247
262
  // the CLI can still resolve the session under the server install dir.
248
263
  args.push('--dir', workingDir);
249
- if (sessionId) {
250
- args.push('--session', sessionId);
264
+ if (providerSessionId) {
265
+ args.push('--session', providerSessionId);
251
266
  }
252
267
  if (resolvedModel) {
253
268
  args.push('--model', resolvedModel);
@@ -257,12 +272,19 @@ async function spawnOpenCode(command, options = {}, ws) {
257
272
  }
258
273
  const permissionOptions = resolveOpenCodePermissionOptions(permissionMode);
259
274
  args.push(...permissionOptions.args);
260
- if (command && command.trim()) {
275
+ const hasAttachments =
276
+ normalizeAttachmentDescriptors(images).length > 0
277
+ || normalizeAttachmentDescriptors(files).length > 0;
278
+ if ((command && command.trim()) || hasAttachments) {
261
279
  // Image attachments ride along as an <images_input> path list appended
262
280
  // to the prompt; the session history reader strips the tag back out.
263
281
  // opencode is a .cmd shim on Windows, so the whole argument must be
264
282
  // newline-free or cmd.exe silently truncates it at the first newline.
265
- args.push(flattenPromptForWindowsShell(appendImagesInputTag(command.trim(), images)));
283
+ const promptWithAttachments = appendFilesInputTag(
284
+ appendImagesInputTag(command?.trim() || '', images),
285
+ files
286
+ );
287
+ args.push(flattenPromptForWindowsShell(promptWithAttachments));
266
288
  }
267
289
 
268
290
  opencodeProcess = spawnFunction('opencode', args, {
@@ -300,7 +322,7 @@ async function spawnOpenCode(command, options = {}, ws) {
300
322
  });
301
323
 
302
324
  opencodeProcess.on('close', async (code) => {
303
- const finalSessionId = capturedSessionId || sessionId || processKey;
325
+ const finalSessionId = sessionId || capturedSessionId || processKey;
304
326
  activeOpenCodeProcesses.delete(finalSessionId);
305
327
  activeOpenCodeProcesses.delete(processKey);
306
328
 
@@ -309,7 +331,8 @@ async function spawnOpenCode(command, options = {}, ws) {
309
331
  stdoutLineBuffer = '';
310
332
  }
311
333
 
312
- const tokenBudget = readOpenCodeTokenUsage(finalSessionId);
334
+ // OpenCode's own database is keyed by the provider-native id.
335
+ const tokenBudget = readOpenCodeTokenUsage(capturedSessionId);
313
336
  if (tokenBudget) {
314
337
  ws.send(createNormalizedMessage({
315
338
  kind: 'status',
@@ -334,7 +357,7 @@ async function spawnOpenCode(command, options = {}, ws) {
334
357
  }
335
358
 
336
359
  if (code === 127 || code === null) {
337
- const installed = await providerAuthService.isProviderInstalled('opencode');
360
+ const installed = await context.isProviderInstalled();
338
361
  if (!installed) {
339
362
  ws.send(createNormalizedMessage({
340
363
  kind: 'error',
@@ -350,11 +373,11 @@ async function spawnOpenCode(command, options = {}, ws) {
350
373
  });
351
374
 
352
375
  opencodeProcess.on('error', async (error) => {
353
- const finalSessionId = capturedSessionId || sessionId || processKey;
376
+ const finalSessionId = sessionId || capturedSessionId || processKey;
354
377
  activeOpenCodeProcesses.delete(finalSessionId);
355
378
  activeOpenCodeProcesses.delete(processKey);
356
379
 
357
- const installed = await providerAuthService.isProviderInstalled('opencode');
380
+ const installed = await context.isProviderInstalled();
358
381
  const errorContent = !installed
359
382
  ? 'OpenCode CLI is not installed. Install it from https://opencode.ai/docs/'
360
383
  : error.message;
@@ -398,6 +421,11 @@ function getActiveOpenCodeSessions() {
398
421
  return Array.from(activeOpenCodeProcesses.keys());
399
422
  }
400
423
 
424
+ export const opencodeRuntime = {
425
+ run: spawnOpenCode,
426
+ abort: abortOpenCodeSession,
427
+ };
428
+
401
429
  export {
402
430
  spawnOpenCode,
403
431
  abortOpenCodeSession,
@@ -4,7 +4,20 @@ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import test from 'node:test';
6
6
 
7
- import { resolveOpenCodePermissionOptions, spawnOpenCode } from './opencode-cli.js';
7
+ import {
8
+ opencodeRuntime,
9
+ resolveOpenCodePermissionOptions,
10
+ } from './opencode-runtime.provider.js';
11
+ import { OpenCodeSessionsProvider } from './opencode-sessions.provider.js';
12
+
13
+ const sessionsProvider = new OpenCodeSessionsProvider();
14
+ const runtimeContext = {
15
+ resolveProviderSessionId: (sessionId) => sessionId || null,
16
+ resolveResumeModel: async (_sessionId, requestedModel) => requestedModel || undefined,
17
+ getProviderModels: async () => ({ OPTIONS: [], DEFAULT: '' }),
18
+ normalizeMessage: (raw, sessionId) => sessionsProvider.normalizeMessage(raw, sessionId),
19
+ isProviderInstalled: async () => true,
20
+ };
8
21
 
9
22
  const findEnvKey = (name) =>
10
23
  Object.keys(process.env).find((key) => key.toLowerCase() === name.toLowerCase()) || name;
@@ -71,7 +84,7 @@ test('spawnOpenCode emits session_created before normalized live messages for ne
71
84
  : `.COM;.EXE;.BAT;.CMD${previousPathExt ? `;${previousPathExt}` : ''}`;
72
85
  }
73
86
 
74
- await spawnOpenCode('Hi', { cwd: tempRoot }, writer);
87
+ await opencodeRuntime.run('Hi', { cwd: tempRoot }, writer, runtimeContext);
75
88
 
76
89
  const sessionCreatedIndex = messages.findIndex((message) => message.kind === 'session_created');
77
90
  const assistantDeltaIndex = messages.findIndex((message) =>
@@ -98,6 +111,26 @@ test('spawnOpenCode emits session_created before normalized live messages for ne
98
111
  assert.equal(launchedArgs.includes('--auto'), false);
99
112
  assert.equal(launchedArgs.includes('--agent'), false);
100
113
  assert.equal(capture.permissionEnv, null);
114
+
115
+ const attachmentOnlyCapturePath = path.join(tempRoot, 'opencode-attachment-only.json');
116
+ process.env.OPENCODE_ARGS_CAPTURE = attachmentOnlyCapturePath;
117
+ await opencodeRuntime.run(
118
+ '',
119
+ {
120
+ cwd: tempRoot,
121
+ files: [{
122
+ path: path.join(tempRoot, 'brief.pdf'),
123
+ name: 'brief.pdf',
124
+ mimeType: 'application/pdf',
125
+ }],
126
+ },
127
+ writer,
128
+ runtimeContext,
129
+ );
130
+ const attachmentOnlyCapture = JSON.parse(await readFile(attachmentOnlyCapturePath, 'utf8'));
131
+ const attachmentPrompt = attachmentOnlyCapture.args[attachmentOnlyCapture.args.length - 1];
132
+ assert.match(attachmentPrompt, /<files_input>/);
133
+ assert.match(attachmentPrompt, /brief\.pdf/);
101
134
  } finally {
102
135
  if (previousPath === undefined) {
103
136
  delete process.env[pathKey];
@@ -186,7 +219,12 @@ test('spawnOpenCode passes permission mode flags and env to the CLI', async () =
186
219
  const argsCapturePath = path.join(tempRoot, `opencode-args-${scenario.permissionMode}.json`);
187
220
  process.env.OPENCODE_ARGS_CAPTURE = argsCapturePath;
188
221
 
189
- await spawnOpenCode('Hi', { cwd: tempRoot, permissionMode: scenario.permissionMode }, writer);
222
+ await opencodeRuntime.run(
223
+ 'Hi',
224
+ { cwd: tempRoot, permissionMode: scenario.permissionMode },
225
+ writer,
226
+ runtimeContext,
227
+ );
190
228
 
191
229
  const capture = JSON.parse(await readFile(argsCapturePath, 'utf8'));
192
230
  for (const expectedArg of scenario.expectArgs) {
@@ -2,7 +2,7 @@ import fsSync from 'node:fs';
2
2
 
3
3
  import Database from 'better-sqlite3';
4
4
 
5
- import { parseImagesInputTag } from '@/shared/image-attachments.js';
5
+ import { parseFilesInputTag, parseImagesInputTag } from '@/shared/image-attachments.js';
6
6
  import type { IProviderSessions } from '@/shared/interfaces.js';
7
7
  import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
8
8
  import {
@@ -404,10 +404,17 @@ export class OpenCodeSessionsProvider implements IProviderSessions {
404
404
  const rawContent = extractText(partData);
405
405
  // User prompts sent with attachments carry an <images_input> path
406
406
  // list; strip it for display and surface the paths as images.
407
- const { text: content, attachments } = messageRole === 'user'
407
+ const parsedImages = messageRole === 'user'
408
408
  ? parseImagesInputTag(rawContent)
409
409
  : { text: rawContent, attachments: [] };
410
- if (content.trim() || attachments.length > 0) {
410
+ const parsedFiles = messageRole === 'user'
411
+ ? parseFilesInputTag(parsedImages.text)
412
+ : { text: rawContent, attachments: [] };
413
+ if (
414
+ parsedFiles.text.trim()
415
+ || parsedImages.attachments.length > 0
416
+ || parsedFiles.attachments.length > 0
417
+ ) {
411
418
  normalized.push(createNormalizedMessage({
412
419
  id: baseId,
413
420
  sessionId,
@@ -415,8 +422,9 @@ export class OpenCodeSessionsProvider implements IProviderSessions {
415
422
  provider: PROVIDER,
416
423
  kind: 'text',
417
424
  role: messageRole === 'user' ? 'user' : 'assistant',
418
- content,
419
- images: attachments.length > 0 ? attachments : undefined,
425
+ content: parsedFiles.text,
426
+ images: parsedImages.attachments.length > 0 ? parsedImages.attachments : undefined,
427
+ files: parsedFiles.attachments.length > 0 ? parsedFiles.attachments : undefined,
420
428
  }));
421
429
  }
422
430
  continue;
@@ -1,5 +1,6 @@
1
1
  import { OpenCodeProviderAuth } from '@/modules/providers/list/opencode/opencode-auth.provider.js';
2
2
  import { OpenCodeProviderModels } from '@/modules/providers/list/opencode/opencode-models.provider.js';
3
+ import { opencodeRuntime } from '@/modules/providers/list/opencode/opencode-runtime.provider.js';
3
4
  import { OpenCodeMcpProvider } from '@/modules/providers/list/opencode/opencode-mcp.provider.js';
4
5
  import { OpenCodeSessionSynchronizer } from '@/modules/providers/list/opencode/opencode-session-synchronizer.provider.js';
5
6
  import { OpenCodeSessionsProvider } from '@/modules/providers/list/opencode/opencode-sessions.provider.js';
@@ -8,12 +9,14 @@ import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provi
8
9
  import type {
9
10
  IProviderAuth,
10
11
  IProviderModels,
12
+ IProviderRuntime,
11
13
  IProviderSessionSynchronizer,
12
14
  IProviderSkills,
13
15
  IProviderSessions,
14
16
  } from '@/shared/interfaces.js';
15
17
 
16
18
  export class OpenCodeProvider extends AbstractProvider {
19
+ readonly runtime: IProviderRuntime = opencodeRuntime;
17
20
  readonly models: IProviderModels = new OpenCodeProviderModels();
18
21
  readonly mcp = new OpenCodeMcpProvider();
19
22
  readonly auth: IProviderAuth = new OpenCodeProviderAuth();
@@ -4,6 +4,7 @@ import { providerAuthService } from '@/modules/providers/services/provider-auth.
4
4
  import { providerCapabilitiesService } from '@/modules/providers/services/provider-capabilities.service.js';
5
5
  import { providerMcpService } from '@/modules/providers/services/mcp.service.js';
6
6
  import { providerModelsService } from '@/modules/providers/services/provider-models.service.js';
7
+ import { providerTokenUsageService } from '@/modules/providers/services/provider-token-usage.service.js';
7
8
  import { providerSkillsService } from '@/modules/providers/services/skills.service.js';
8
9
  import { sessionConversationsSearchService } from '@/modules/providers/services/session-conversations-search.service.js';
9
10
  import { sessionsService } from '@/modules/providers/services/sessions.service.js';
@@ -11,7 +12,6 @@ import type {
11
12
  LLMProvider,
12
13
  McpScope,
13
14
  McpTransport,
14
- ProviderChangeActiveModelInput,
15
15
  ProviderSkillCreateFile,
16
16
  ProviderSkillCreateInput,
17
17
  UpsertProviderMcpServerInput,
@@ -352,7 +352,7 @@ const parseSessionSearchLimit = (value: unknown): number => {
352
352
  return Math.max(1, Math.min(parsed, 100));
353
353
  };
354
354
 
355
- const parseChangeActiveModelPayload = (payload: unknown): ProviderChangeActiveModelInput => {
355
+ const parseSessionModelPayload = (payload: unknown): string => {
356
356
  if (!payload || typeof payload !== 'object') {
357
357
  throw new AppError('Request body must be an object.', {
358
358
  code: 'INVALID_REQUEST_BODY',
@@ -369,10 +369,7 @@ const parseChangeActiveModelPayload = (payload: unknown): ProviderChangeActiveMo
369
369
  });
370
370
  }
371
371
 
372
- return {
373
- sessionId: '',
374
- model,
375
- };
372
+ return model;
376
373
  };
377
374
 
378
375
  router.get(
@@ -394,20 +391,40 @@ router.get(
394
391
  }),
395
392
  );
396
393
 
397
- router.post(
394
+ /**
395
+ * Reports which model one session is using. `requestedModel` lets the client
396
+ * pass the default it would otherwise send, so a session that has not been
397
+ * sent on yet resolves to that instead of the catalog default.
398
+ */
399
+ router.get(
398
400
  '/:provider/sessions/:sessionId/active-model',
399
401
  asyncHandler(async (req: Request, res: Response) => {
400
402
  const provider = parseProvider(req.params.provider);
401
403
  const sessionId = parseSessionId(req.params.sessionId);
402
- const payload = parseChangeActiveModelPayload(req.body);
403
- const result = await providerModelsService.changeActiveModel(provider, {
404
- ...payload,
404
+ const requestedModel = readOptionalQueryString(req.query.requestedModel);
405
+ const result = await providerModelsService.resolveSessionModel(provider, {
405
406
  sessionId,
407
+ requestedModel,
406
408
  });
407
409
  res.json(createApiSuccessResponse(result));
408
410
  }),
409
411
  );
410
412
 
413
+ router.post(
414
+ '/:provider/sessions/:sessionId/active-model',
415
+ asyncHandler(async (req: Request, res: Response) => {
416
+ const provider = parseProvider(req.params.provider);
417
+ const sessionId = parseSessionId(req.params.sessionId);
418
+ const model = parseSessionModelPayload(req.body);
419
+ const stored = providerModelsService.setSessionModel(provider, sessionId, model);
420
+ // A session row only exists once the gateway has allocated one. Report the
421
+ // selection back either way so the client can hold it until the first send.
422
+ res.json(createApiSuccessResponse(
423
+ stored ?? { provider, sessionId, model, source: 'session' as const },
424
+ ));
425
+ }),
426
+ );
427
+
411
428
  // ----------------- Skills routes -----------------
412
429
  router.get(
413
430
  '/:provider/skills',
@@ -556,6 +573,26 @@ router.get(
556
573
  }),
557
574
  );
558
575
 
576
+ router.get(
577
+ '/sessions/:sessionId/token-usage',
578
+ asyncHandler(async (req: Request, res: Response) => {
579
+ const sessionId = parseSessionId(req.params.sessionId);
580
+ const result = await providerTokenUsageService.getSessionTokenUsage(sessionId);
581
+ res.json(createApiSuccessResponse(result));
582
+ }),
583
+ );
584
+
585
+ // Must stay registered after the static `/sessions/running` and
586
+ // `/sessions/archived` routes so those literals never match `:sessionId`.
587
+ router.get(
588
+ '/sessions/:sessionId',
589
+ asyncHandler(async (req: Request, res: Response) => {
590
+ const sessionId = parseSessionId(req.params.sessionId);
591
+ const result = sessionsService.getSessionDetailsById(sessionId);
592
+ res.json(createApiSuccessResponse(result));
593
+ }),
594
+ );
595
+
559
596
  router.delete(
560
597
  '/sessions/:sessionId',
561
598
  asyncHandler(async (req: Request, res: Response) => {
@@ -15,6 +15,8 @@ type ProviderCapabilities = {
15
15
  defaultPermissionMode: string;
16
16
  /** Whether image attachments can be included in a chat.send. */
17
17
  supportsImages: boolean;
18
+ /** Whether general file attachments can be included in a chat.send. */
19
+ supportsFiles: boolean;
18
20
  /** Whether an in-flight run can be cancelled via chat.abort. */
19
21
  supportsAbort: boolean;
20
22
  /** Whether interactive tool permission prompts can reach the UI. */
@@ -37,6 +39,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
37
39
  permissionModes: ['default', 'auto', 'acceptEdits', 'bypassPermissions', 'plan'],
38
40
  defaultPermissionMode: 'default',
39
41
  supportsImages: true,
42
+ supportsFiles: true,
40
43
  supportsAbort: true,
41
44
  supportsPermissionRequests: true,
42
45
  supportsTokenUsage: true,
@@ -47,6 +50,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
47
50
  permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'plan'],
48
51
  defaultPermissionMode: 'default',
49
52
  supportsImages: true,
53
+ supportsFiles: true,
50
54
  supportsAbort: true,
51
55
  supportsPermissionRequests: false,
52
56
  supportsTokenUsage: false,
@@ -57,6 +61,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
57
61
  permissionModes: ['default', 'acceptEdits', 'bypassPermissions'],
58
62
  defaultPermissionMode: 'default',
59
63
  supportsImages: true,
64
+ supportsFiles: true,
60
65
  supportsAbort: true,
61
66
  supportsPermissionRequests: false,
62
67
  supportsTokenUsage: true,
@@ -66,10 +71,11 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
66
71
  provider: 'opencode',
67
72
  // Mapped by the runtime onto OpenCode's controls: `--agent plan` (plan),
68
73
  // `--auto` (bypassPermissions) and the OPENCODE_PERMISSION env var
69
- // (acceptEdits). See resolveOpenCodePermissionOptions in opencode-cli.js.
74
+ // (acceptEdits). See resolveOpenCodePermissionOptions in the OpenCode runtime adapter.
70
75
  permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'plan'],
71
76
  defaultPermissionMode: 'default',
72
77
  supportsImages: true,
78
+ supportsFiles: true,
73
79
  supportsAbort: true,
74
80
  supportsPermissionRequests: false,
75
81
  supportsTokenUsage: true,