circuschief 0.1.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 (319) hide show
  1. package/package.json +33 -0
  2. package/packages/server/bin/cli.js +4 -0
  3. package/packages/server/src/agents/AgentGateway.js +64 -0
  4. package/packages/server/src/agents/BaseAgent.js +41 -0
  5. package/packages/server/src/agents/LoggingAgentWrapper.js +73 -0
  6. package/packages/server/src/agents/adapters/ClaudeCodeAdapter.js +33 -0
  7. package/packages/server/src/agents/adapters/CodexAdapter.js +26 -0
  8. package/packages/server/src/agents/types.js +43 -0
  9. package/packages/server/src/agents/vcr/CassetteStore.js +111 -0
  10. package/packages/server/src/agents/vcr/VCRAgentAdapter.js +126 -0
  11. package/packages/server/src/agents/vcr/VCRSummaryWrapper.js +71 -0
  12. package/packages/server/src/api/canvas-helpers.js +249 -0
  13. package/packages/server/src/api/canvas-trash-routes.js +205 -0
  14. package/packages/server/src/api/canvas.js +331 -0
  15. package/packages/server/src/api/commandButtons.js +312 -0
  16. package/packages/server/src/api/commands.js +169 -0
  17. package/packages/server/src/api/filesystem.js +62 -0
  18. package/packages/server/src/api/git.js +85 -0
  19. package/packages/server/src/api/index.js +44 -0
  20. package/packages/server/src/api/kanban.js +342 -0
  21. package/packages/server/src/api/metrics.js +194 -0
  22. package/packages/server/src/api/projects-helpers.js +43 -0
  23. package/packages/server/src/api/projects-session-helpers.js +295 -0
  24. package/packages/server/src/api/projects.js +384 -0
  25. package/packages/server/src/api/providers.js +249 -0
  26. package/packages/server/src/api/quickResponses.js +129 -0
  27. package/packages/server/src/api/sessions-archive.js +69 -0
  28. package/packages/server/src/api/sessions-commands.js +220 -0
  29. package/packages/server/src/api/sessions-conversations.js +168 -0
  30. package/packages/server/src/api/sessions-draft.js +72 -0
  31. package/packages/server/src/api/sessions-lifecycle.js +190 -0
  32. package/packages/server/src/api/sessions-messages.js +141 -0
  33. package/packages/server/src/api/sessions-notes.js +51 -0
  34. package/packages/server/src/api/sessions-patch.js +252 -0
  35. package/packages/server/src/api/sessions-streaming.js +86 -0
  36. package/packages/server/src/api/sessions.js +269 -0
  37. package/packages/server/src/api/settings.js +194 -0
  38. package/packages/server/src/api/templates.js +63 -0
  39. package/packages/server/src/app.js +51 -0
  40. package/packages/server/src/database.js +58 -0
  41. package/packages/server/src/db/AgentCallLogRepository.js +322 -0
  42. package/packages/server/src/db/AttachmentRepository.js +191 -0
  43. package/packages/server/src/db/BaseRepository.js +39 -0
  44. package/packages/server/src/db/CanvasItemRepository.js +315 -0
  45. package/packages/server/src/db/CommandButtonRepository.js +75 -0
  46. package/packages/server/src/db/CommandRunRepository.js +219 -0
  47. package/packages/server/src/db/ConversationRepository.js +379 -0
  48. package/packages/server/src/db/DatabaseManager.js +91 -0
  49. package/packages/server/src/db/KanbanBoardRepository.js +92 -0
  50. package/packages/server/src/db/KanbanCardRepository.js +286 -0
  51. package/packages/server/src/db/KanbanLaneRepository.js +279 -0
  52. package/packages/server/src/db/MessageRepository.js +156 -0
  53. package/packages/server/src/db/ProjectDefaultsRepository.js +173 -0
  54. package/packages/server/src/db/ProjectRepository.js +110 -0
  55. package/packages/server/src/db/ProviderRepository.js +307 -0
  56. package/packages/server/src/db/QuickResponseRepository.js +186 -0
  57. package/packages/server/src/db/SessionNoteRepository.js +60 -0
  58. package/packages/server/src/db/SessionRepository.js +314 -0
  59. package/packages/server/src/db/SessionSummaryRepository.js +200 -0
  60. package/packages/server/src/db/SessionTemplateRepository.js +171 -0
  61. package/packages/server/src/db/SettingsRepository.js +211 -0
  62. package/packages/server/src/db/TodoRepository.js +132 -0
  63. package/packages/server/src/db/WorkLogRepository.js +122 -0
  64. package/packages/server/src/db/conversation-helpers.js +119 -0
  65. package/packages/server/src/db/index.js +100 -0
  66. package/packages/server/src/db/migrations/canvasItemsMigrations.js +109 -0
  67. package/packages/server/src/db/migrations/conversationsMigrations.js +183 -0
  68. package/packages/server/src/db/migrations/index.js +199 -0
  69. package/packages/server/src/db/migrations/kanbanMigrations.js +99 -0
  70. package/packages/server/src/db/migrations/migrationUtils.js +55 -0
  71. package/packages/server/src/db/migrations/miscMigrations.js +242 -0
  72. package/packages/server/src/db/migrations/projectsMigrations.js +95 -0
  73. package/packages/server/src/db/migrations/sessionsMigrations.js +282 -0
  74. package/packages/server/src/db/session-helpers.js +150 -0
  75. package/packages/server/src/index.js +106 -0
  76. package/packages/server/src/logger.js +22 -0
  77. package/packages/server/src/middleware/sessionLookup.js +57 -0
  78. package/packages/server/src/middleware/upload.js +94 -0
  79. package/packages/server/src/schema.sql +363 -0
  80. package/packages/server/src/services/agentCallLogger.js +116 -0
  81. package/packages/server/src/services/canvasStore.js +56 -0
  82. package/packages/server/src/services/childSessionContext.js +61 -0
  83. package/packages/server/src/services/commandRunner.js +422 -0
  84. package/packages/server/src/services/conversationContext.js +72 -0
  85. package/packages/server/src/services/diffService.js +172 -0
  86. package/packages/server/src/services/draftSessionService.js +181 -0
  87. package/packages/server/src/services/encryption.js +134 -0
  88. package/packages/server/src/services/ghService.js +169 -0
  89. package/packages/server/src/services/gitService.js +520 -0
  90. package/packages/server/src/services/gitSessionSetup.js +48 -0
  91. package/packages/server/src/services/hookService.js +60 -0
  92. package/packages/server/src/services/kanbanService.js +262 -0
  93. package/packages/server/src/services/kanbanTriggers.js +273 -0
  94. package/packages/server/src/services/nodeSpawnHelper.js +63 -0
  95. package/packages/server/src/services/prStatusService.js +204 -0
  96. package/packages/server/src/services/prUrlService.js +224 -0
  97. package/packages/server/src/services/providerTestService.js +81 -0
  98. package/packages/server/src/services/scheduleService.js +110 -0
  99. package/packages/server/src/services/schedulerService.js +281 -0
  100. package/packages/server/src/services/sessionDuplicator.js +63 -0
  101. package/packages/server/src/services/sessionErrors.js +173 -0
  102. package/packages/server/src/services/sessionExecution.js +378 -0
  103. package/packages/server/src/services/sessionManager.js +356 -0
  104. package/packages/server/src/services/sessionPrompts.js +427 -0
  105. package/packages/server/src/services/sessionProvider.js +107 -0
  106. package/packages/server/src/services/slashCommandDiscovery.js +258 -0
  107. package/packages/server/src/services/slashCommandPluginDiscovery.js +216 -0
  108. package/packages/server/src/services/slashCommandService.js +306 -0
  109. package/packages/server/src/services/streamEventCallbacks.js +170 -0
  110. package/packages/server/src/services/streamEventHandler.js +488 -0
  111. package/packages/server/src/services/streamUsageHandler.js +228 -0
  112. package/packages/server/src/services/summaryBroadcast.js +61 -0
  113. package/packages/server/src/services/summaryClaudeClient.js +180 -0
  114. package/packages/server/src/services/summaryPrompts.js +169 -0
  115. package/packages/server/src/services/summaryService.js +552 -0
  116. package/packages/server/src/services/summaryStaleCheck.js +35 -0
  117. package/packages/server/src/services/systemMonitor.js +281 -0
  118. package/packages/server/src/services/templateTriggerService.js +197 -0
  119. package/packages/server/src/services/terminalOutput.js +160 -0
  120. package/packages/server/src/services/todoStore.js +58 -0
  121. package/packages/server/src/services/usageTracker.js +69 -0
  122. package/packages/server/src/services/withConcurrencyGuard.js +110 -0
  123. package/packages/server/src/websocket.js +10 -0
  124. package/packages/server/src/ws/WebSocketManager.js +240 -0
  125. package/packages/server/src/ws/index.js +50 -0
  126. package/packages/shared/package.json +27 -0
  127. package/packages/shared/src/constants.js +44 -0
  128. package/packages/shared/src/contracts/canvas.js +25 -0
  129. package/packages/shared/src/contracts/commandButtons.js +36 -0
  130. package/packages/shared/src/contracts/kanban.js +142 -0
  131. package/packages/shared/src/contracts/projects.js +63 -0
  132. package/packages/shared/src/contracts/providers.js +81 -0
  133. package/packages/shared/src/contracts/quickResponses.js +44 -0
  134. package/packages/shared/src/contracts/sessions.js +112 -0
  135. package/packages/shared/src/contracts/templates.js +51 -0
  136. package/packages/shared/src/index.js +5 -0
  137. package/packages/shared/src/protocol.js +76 -0
  138. package/packages/shared/src/routeParams.js +36 -0
  139. package/packages/shared/src/types.js +167 -0
  140. package/packages/shared/src/utils.js +101 -0
  141. package/packages/web/dist/assets/ActiveSessionsView-BQc76Jc8.js +1 -0
  142. package/packages/web/dist/assets/ActiveSessionsView-ofSvx-K1.css +1 -0
  143. package/packages/web/dist/assets/AgentLogsView-CTCjHjsu.js +2 -0
  144. package/packages/web/dist/assets/AgentLogsView-D90PnQVk.css +1 -0
  145. package/packages/web/dist/assets/ApiClient-Dbs1H78V.js +1 -0
  146. package/packages/web/dist/assets/ArchiveConfirmModal-CCxSZ52u.js +1 -0
  147. package/packages/web/dist/assets/ArchiveConfirmModal-CQZeuYBz.css +1 -0
  148. package/packages/web/dist/assets/CommandButtonDetailView-CF_-LXpU.js +1 -0
  149. package/packages/web/dist/assets/CommandButtonDetailView-DBm3rzhw.css +1 -0
  150. package/packages/web/dist/assets/EffortLevelSelector-BQaQmU2d.css +1 -0
  151. package/packages/web/dist/assets/EffortLevelSelector-DPofLvm-.js +1 -0
  152. package/packages/web/dist/assets/GeneralSettingsView-BCf53fpC.css +1 -0
  153. package/packages/web/dist/assets/GeneralSettingsView-BY1G-Kv8.js +1 -0
  154. package/packages/web/dist/assets/InterpolationHelp-CgdbNcJB.js +1 -0
  155. package/packages/web/dist/assets/InterpolationHelp-iNxTxmhs.css +1 -0
  156. package/packages/web/dist/assets/MarkdownEditor-CqT1U8lo.js +2 -0
  157. package/packages/web/dist/assets/MarkdownEditor-enuH2yvP.css +1 -0
  158. package/packages/web/dist/assets/ModelSelector-BBn_Ve0D.js +1 -0
  159. package/packages/web/dist/assets/ModelSelector-DPPD-92R.css +1 -0
  160. package/packages/web/dist/assets/NewSessionView-Bo5l49nu.js +3 -0
  161. package/packages/web/dist/assets/NewSessionView-Byoi1XdQ.css +1 -0
  162. package/packages/web/dist/assets/PathChooser-BoMGzeg2.css +1 -0
  163. package/packages/web/dist/assets/PathChooser-Cx9gQ-Qt.js +1 -0
  164. package/packages/web/dist/assets/ProjectEditView-BFuscj-V.js +1 -0
  165. package/packages/web/dist/assets/ProjectEditView-DNwBUNRk.css +1 -0
  166. package/packages/web/dist/assets/ProjectListView-C55H1JHQ.css +1 -0
  167. package/packages/web/dist/assets/ProjectListView-Dj0jBZ46.js +1 -0
  168. package/packages/web/dist/assets/ProjectNewView-Brdp-xUu.js +1 -0
  169. package/packages/web/dist/assets/ProjectNewView-CpgE4R-l.css +1 -0
  170. package/packages/web/dist/assets/ProvidersView-B_QQF3RM.css +1 -0
  171. package/packages/web/dist/assets/ProvidersView-Cxc-1skq.js +1 -0
  172. package/packages/web/dist/assets/QuickResponseSettings-B2eVAtHW.js +1 -0
  173. package/packages/web/dist/assets/QuickResponseSettings-B8188A1D.css +1 -0
  174. package/packages/web/dist/assets/QuickResponsesPanel-DIBQFj0W.css +1 -0
  175. package/packages/web/dist/assets/QuickResponsesPanel-lU8pW2B0.js +1 -0
  176. package/packages/web/dist/assets/ResizableTextarea-B5nAA0RV.css +1 -0
  177. package/packages/web/dist/assets/ResizableTextarea-DSy1mWGY.js +1 -0
  178. package/packages/web/dist/assets/SessionCard-BvjLwVYg.js +1 -0
  179. package/packages/web/dist/assets/SessionCard-D20G3bX8.css +1 -0
  180. package/packages/web/dist/assets/SessionDetailView-BQbPg-RJ.js +36 -0
  181. package/packages/web/dist/assets/SessionDetailView-BrMG4p2-.css +1 -0
  182. package/packages/web/dist/assets/SessionFormOptions-BgqFR-5f.js +1 -0
  183. package/packages/web/dist/assets/SessionFormOptions-BuLlDF-7.css +1 -0
  184. package/packages/web/dist/assets/SessionListView-BAIBtJF7.css +1 -0
  185. package/packages/web/dist/assets/SessionListView-CYIHI8qF.js +1 -0
  186. package/packages/web/dist/assets/SessionLogStream-B-FwUMJQ.js +18 -0
  187. package/packages/web/dist/assets/SessionLogStream-zPUTiGbe.css +1 -0
  188. package/packages/web/dist/assets/SettingsView-DC8-hTQ-.css +1 -0
  189. package/packages/web/dist/assets/SettingsView-fZxpiGp7.js +1 -0
  190. package/packages/web/dist/assets/SlashCommandWizard-BB30cSvo.css +1 -0
  191. package/packages/web/dist/assets/SlashCommandWizard-BgaOw9W3.js +1 -0
  192. package/packages/web/dist/assets/SummarySettingsView-DcsmSVJI.css +1 -0
  193. package/packages/web/dist/assets/SummarySettingsView-eeu1Xq86.js +1 -0
  194. package/packages/web/dist/assets/TemplateDetailView-DEPKSwDo.js +1 -0
  195. package/packages/web/dist/assets/TemplateDetailView-DT2m06W7.css +1 -0
  196. package/packages/web/dist/assets/apl-B4CMkyY2.js +1 -0
  197. package/packages/web/dist/assets/asciiarmor-Df11BRmG.js +1 -0
  198. package/packages/web/dist/assets/asn1-EdZsLKOL.js +1 -0
  199. package/packages/web/dist/assets/asterisk-B-8jnY81.js +1 -0
  200. package/packages/web/dist/assets/brainfuck-C4LP7Hcl.js +1 -0
  201. package/packages/web/dist/assets/clike-B9uivgTg.js +1 -0
  202. package/packages/web/dist/assets/clojure-BMjYHr_A.js +1 -0
  203. package/packages/web/dist/assets/cmake-BQqOBYOt.js +1 -0
  204. package/packages/web/dist/assets/cobol-CWcv1MsR.js +1 -0
  205. package/packages/web/dist/assets/coffeescript-S37ZYGWr.js +1 -0
  206. package/packages/web/dist/assets/commandButtons-DNSHH8IA.js +4 -0
  207. package/packages/web/dist/assets/commonlisp-DBKNyK5s.js +1 -0
  208. package/packages/web/dist/assets/crystal-SjHAIU92.js +1 -0
  209. package/packages/web/dist/assets/css-BnMrqG3P.js +1 -0
  210. package/packages/web/dist/assets/cypher-C_CwsFkJ.js +1 -0
  211. package/packages/web/dist/assets/d-pRatUO7H.js +1 -0
  212. package/packages/web/dist/assets/diff-DbItnlRl.js +1 -0
  213. package/packages/web/dist/assets/dockerfile-BKs6k2Af.js +1 -0
  214. package/packages/web/dist/assets/dtd-DF_7sFjM.js +1 -0
  215. package/packages/web/dist/assets/dylan-DwRh75JA.js +1 -0
  216. package/packages/web/dist/assets/ebnf-CDyGwa7X.js +1 -0
  217. package/packages/web/dist/assets/ecl-Cabwm37j.js +1 -0
  218. package/packages/web/dist/assets/eiffel-CnydiIhH.js +1 -0
  219. package/packages/web/dist/assets/elm-vLlmbW-K.js +1 -0
  220. package/packages/web/dist/assets/erlang-BNw1qcRV.js +1 -0
  221. package/packages/web/dist/assets/factor-kuTfRLto.js +1 -0
  222. package/packages/web/dist/assets/fcl-Kvtd6kyn.js +1 -0
  223. package/packages/web/dist/assets/forth-Ffai-XNe.js +1 -0
  224. package/packages/web/dist/assets/fortran-DYz_wnZ1.js +1 -0
  225. package/packages/web/dist/assets/gas-Bneqetm1.js +1 -0
  226. package/packages/web/dist/assets/gherkin-heZmZLOM.js +1 -0
  227. package/packages/web/dist/assets/groovy-D9Dt4D0W.js +1 -0
  228. package/packages/web/dist/assets/haskell-BWDZoCOh.js +1 -0
  229. package/packages/web/dist/assets/haxe-H-WmDvRZ.js +1 -0
  230. package/packages/web/dist/assets/http-DBlCnlav.js +1 -0
  231. package/packages/web/dist/assets/idl-BEugSyMb.js +1 -0
  232. package/packages/web/dist/assets/index-BZlHgDSz.js +1 -0
  233. package/packages/web/dist/assets/index-BhWX8AfE.js +2 -0
  234. package/packages/web/dist/assets/index-Bi3XvF_f.js +1 -0
  235. package/packages/web/dist/assets/index-BqXoPf_D.js +1 -0
  236. package/packages/web/dist/assets/index-CAuTOZSD.js +1 -0
  237. package/packages/web/dist/assets/index-CKYk-fkb.js +1 -0
  238. package/packages/web/dist/assets/index-CTumW_tV.js +318 -0
  239. package/packages/web/dist/assets/index-CVOJVSsC.js +82 -0
  240. package/packages/web/dist/assets/index-CXK2Z3_z.js +1 -0
  241. package/packages/web/dist/assets/index-CYllQ3Vd.js +1 -0
  242. package/packages/web/dist/assets/index-CpsfI08O.js +1 -0
  243. package/packages/web/dist/assets/index-DQkhDeTA.js +3 -0
  244. package/packages/web/dist/assets/index-DWP8iCBp.js +1 -0
  245. package/packages/web/dist/assets/index-DkVb9W_J.js +1 -0
  246. package/packages/web/dist/assets/index-DmKHPbIa.js +1 -0
  247. package/packages/web/dist/assets/index-DrlQi03X.js +1 -0
  248. package/packages/web/dist/assets/index-gmCCsCQ1.css +1 -0
  249. package/packages/web/dist/assets/index-prTEzzgO.js +1 -0
  250. package/packages/web/dist/assets/index-wqgejMCM.js +1 -0
  251. package/packages/web/dist/assets/index-yh0ZHIWw.js +7 -0
  252. package/packages/web/dist/assets/javascript-qCveANmP.js +1 -0
  253. package/packages/web/dist/assets/julia-DuME0IfC.js +1 -0
  254. package/packages/web/dist/assets/livescript-BwQOo05w.js +1 -0
  255. package/packages/web/dist/assets/lua-BgMRiT3U.js +1 -0
  256. package/packages/web/dist/assets/mathematica-DTrFuWx2.js +1 -0
  257. package/packages/web/dist/assets/mbox-CNhZ1qSd.js +1 -0
  258. package/packages/web/dist/assets/mirc-CjQqDB4T.js +1 -0
  259. package/packages/web/dist/assets/mllike-CXdrOF99.js +1 -0
  260. package/packages/web/dist/assets/modelica-Dc1JOy9r.js +1 -0
  261. package/packages/web/dist/assets/mscgen-BA5vi2Kp.js +1 -0
  262. package/packages/web/dist/assets/mumps-BT43cFF4.js +1 -0
  263. package/packages/web/dist/assets/nginx-DdIZxoE0.js +1 -0
  264. package/packages/web/dist/assets/nsis-LdVXkNf5.js +1 -0
  265. package/packages/web/dist/assets/ntriples-BfvgReVJ.js +1 -0
  266. package/packages/web/dist/assets/octave-Ck1zUtKM.js +1 -0
  267. package/packages/web/dist/assets/oz-BzwKVEFT.js +1 -0
  268. package/packages/web/dist/assets/pascal--L3eBynH.js +1 -0
  269. package/packages/web/dist/assets/perl-CdXCOZ3F.js +1 -0
  270. package/packages/web/dist/assets/pig-CevX1Tat.js +1 -0
  271. package/packages/web/dist/assets/powershell-CFHJl5sT.js +1 -0
  272. package/packages/web/dist/assets/projects-DbBQQH-V.js +1 -0
  273. package/packages/web/dist/assets/properties-C78fOPTZ.js +1 -0
  274. package/packages/web/dist/assets/protobuf-ChK-085T.js +1 -0
  275. package/packages/web/dist/assets/providers-ceCc4xRU.js +1 -0
  276. package/packages/web/dist/assets/pug-DukmZTjD.js +1 -0
  277. package/packages/web/dist/assets/puppet-DMA9R1ak.js +1 -0
  278. package/packages/web/dist/assets/python-BuPzkPfP.js +1 -0
  279. package/packages/web/dist/assets/q-pXgVlZs6.js +1 -0
  280. package/packages/web/dist/assets/r-DUYO_cvP.js +1 -0
  281. package/packages/web/dist/assets/rpm-CTu-6PCP.js +1 -0
  282. package/packages/web/dist/assets/ruby-B2Rjki9n.js +1 -0
  283. package/packages/web/dist/assets/sas-B4kiWyti.js +1 -0
  284. package/packages/web/dist/assets/scheme-C41bIUwD.js +1 -0
  285. package/packages/web/dist/assets/sessions-D681M81k.js +1 -0
  286. package/packages/web/dist/assets/settings-D0evez2V.js +1 -0
  287. package/packages/web/dist/assets/shell-CjFT_Tl9.js +1 -0
  288. package/packages/web/dist/assets/sieve-C3Gn_uJK.js +1 -0
  289. package/packages/web/dist/assets/simple-mode-GW_nhZxv.js +1 -0
  290. package/packages/web/dist/assets/smalltalk-CnHTOXQT.js +1 -0
  291. package/packages/web/dist/assets/solr-DehyRSwq.js +1 -0
  292. package/packages/web/dist/assets/sparql-DkYu6x3z.js +1 -0
  293. package/packages/web/dist/assets/spreadsheet-BCZA_wO0.js +1 -0
  294. package/packages/web/dist/assets/sql-D0XecflT.js +1 -0
  295. package/packages/web/dist/assets/stex-C3f8Ysf7.js +1 -0
  296. package/packages/web/dist/assets/style-BTin-zR_.css +1 -0
  297. package/packages/web/dist/assets/stylus-B533Al4x.js +1 -0
  298. package/packages/web/dist/assets/swift-BzpIVaGY.js +1 -0
  299. package/packages/web/dist/assets/tcl-DVfN8rqt.js +1 -0
  300. package/packages/web/dist/assets/textile-CnDTJFAw.js +1 -0
  301. package/packages/web/dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  302. package/packages/web/dist/assets/tiki-DGYXhP31.js +1 -0
  303. package/packages/web/dist/assets/toml-Bm5Em-hy.js +1 -0
  304. package/packages/web/dist/assets/troff-wAsdV37c.js +1 -0
  305. package/packages/web/dist/assets/ttcn-CfJYG6tj.js +1 -0
  306. package/packages/web/dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  307. package/packages/web/dist/assets/turtle-B1tBg_DP.js +1 -0
  308. package/packages/web/dist/assets/vb-CmGdzxic.js +1 -0
  309. package/packages/web/dist/assets/vbscript-BuJXcnF6.js +1 -0
  310. package/packages/web/dist/assets/velocity-D8B20fx6.js +1 -0
  311. package/packages/web/dist/assets/verilog-C6RDOZhf.js +1 -0
  312. package/packages/web/dist/assets/vhdl-lSbBsy5d.js +1 -0
  313. package/packages/web/dist/assets/webidl-ZXfAyPTL.js +1 -0
  314. package/packages/web/dist/assets/xquery-CQfU5ijd.js +1 -0
  315. package/packages/web/dist/assets/yacas-BJ4BC0dw.js +1 -0
  316. package/packages/web/dist/assets/z80-Hz9HOZM7.js +1 -0
  317. package/packages/web/dist/favicon.png +0 -0
  318. package/packages/web/dist/index.html +17 -0
  319. package/packages/web/dist/logo.png +0 -0
@@ -0,0 +1,129 @@
1
+ import { Router } from 'express';
2
+ import { projects, quickResponses } from '../database.js';
3
+ import {
4
+ CreateQuickResponseRequest,
5
+ UpdateQuickResponseRequest,
6
+ ReorderQuickResponsesRequest,
7
+ } from '../../../shared/src/contracts/quickResponses.js';
8
+
9
+ const router = Router();
10
+
11
+ // GET /api/projects/:projectId/quick-responses - List quick responses for a project
12
+ router.get('/projects/:projectId/quick-responses', (req, res) => {
13
+ const { projectId } = req.params;
14
+
15
+ const project = projects.getById(projectId);
16
+ if (!project) {
17
+ return res.status(404).json({ error: 'Project not found' });
18
+ }
19
+
20
+ const responses = quickResponses.getAvailableForProject(projectId);
21
+ res.json(responses);
22
+ });
23
+
24
+ // POST /api/projects/:projectId/quick-responses - Create a quick response
25
+ router.post('/projects/:projectId/quick-responses', (req, res) => {
26
+ const { projectId } = req.params;
27
+
28
+ const project = projects.getById(projectId);
29
+ if (!project) {
30
+ return res.status(404).json({ error: 'Project not found' });
31
+ }
32
+
33
+ const result = CreateQuickResponseRequest.safeParse(req.body);
34
+ if (!result.success) {
35
+ return res.status(400).json({ error: result.error.issues[0].message });
36
+ }
37
+
38
+ const { label, content, autoSubmit, category, sortOrder, isGlobal } = result.data;
39
+
40
+ const response = quickResponses.create({
41
+ projectId: isGlobal ? null : projectId,
42
+ label: label.trim(),
43
+ content,
44
+ autoSubmit,
45
+ category,
46
+ sortOrder,
47
+ });
48
+
49
+ res.status(201).json(response);
50
+ });
51
+
52
+ // GET /api/quick-responses/global - List global quick responses only
53
+ router.get('/quick-responses/global', (_req, res) => {
54
+ const responses = quickResponses.getGlobal();
55
+ res.json(responses);
56
+ });
57
+
58
+ // PATCH /api/quick-responses/:id - Update a quick response
59
+ router.patch('/quick-responses/:id', (req, res) => {
60
+ const { id } = req.params;
61
+
62
+ const existing = quickResponses.getById(id);
63
+ if (!existing) {
64
+ return res.status(404).json({ error: 'Quick response not found' });
65
+ }
66
+
67
+ const result = UpdateQuickResponseRequest.safeParse(req.body);
68
+ if (!result.success) {
69
+ return res.status(400).json({ error: result.error.issues[0].message });
70
+ }
71
+
72
+ const updates = { ...result.data };
73
+ if (updates.label) {
74
+ updates.label = updates.label.trim();
75
+ }
76
+
77
+ const updated = quickResponses.update(id, updates);
78
+ res.json(updated);
79
+ });
80
+
81
+ // DELETE /api/quick-responses/:id - Delete a quick response
82
+ router.delete('/quick-responses/:id', (req, res) => {
83
+ const { id } = req.params;
84
+
85
+ const existing = quickResponses.getById(id);
86
+ if (!existing) {
87
+ return res.status(404).json({ error: 'Quick response not found' });
88
+ }
89
+
90
+ quickResponses.deleteById(id);
91
+ res.status(204).send();
92
+ });
93
+
94
+ // POST /api/projects/:projectId/quick-responses/reorder - Reorder quick responses
95
+ router.post('/projects/:projectId/quick-responses/reorder', (req, res) => {
96
+ const { projectId } = req.params;
97
+
98
+ const project = projects.getById(projectId);
99
+ if (!project) {
100
+ return res.status(404).json({ error: 'Project not found' });
101
+ }
102
+
103
+ const result = ReorderQuickResponsesRequest.safeParse(req.body);
104
+ if (!result.success) {
105
+ return res.status(400).json({ error: result.error.issues[0].message });
106
+ }
107
+
108
+ quickResponses.updateSortOrder(result.data);
109
+
110
+ // Return the updated responses
111
+ const responses = quickResponses.getAvailableForProject(projectId);
112
+ res.json(responses);
113
+ });
114
+
115
+ // POST /api/quick-responses/global/reorder - Reorder global quick responses
116
+ router.post('/quick-responses/global/reorder', (req, res) => {
117
+ const result = ReorderQuickResponsesRequest.safeParse(req.body);
118
+ if (!result.success) {
119
+ return res.status(400).json({ error: result.error.issues[0].message });
120
+ }
121
+
122
+ quickResponses.updateSortOrder(result.data);
123
+
124
+ // Return the updated global responses
125
+ const responses = quickResponses.getGlobal();
126
+ res.json(responses);
127
+ });
128
+
129
+ export default router;
@@ -0,0 +1,69 @@
1
+ import { Router } from 'express';
2
+ import { sessions } from '../database.js';
3
+ import { broadcastToProject } from '../websocket.js';
4
+ import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
5
+ import { requireSession, requireSessionAndProject } from '../middleware/sessionLookup.js';
6
+ import { executeHookAsync } from '../services/hookService.js';
7
+
8
+ const router = Router();
9
+
10
+ // POST /api/sessions/:id/archive - Archive a session
11
+ router.post('/:id/archive', requireSessionAndProject, async (req, res) => {
12
+ // Only allow archiving stopped/waiting/error sessions (not active sessions like starting/running)
13
+ if (!['stopped', 'waiting', 'error'].includes(req.session_.status)) {
14
+ return res.status(400).json({ error: 'Can only archive stopped, waiting, or error sessions' });
15
+ }
16
+
17
+ const { cleanup } = req.body || {};
18
+
19
+ const updated = sessions.update(req.params.id, { archived: true });
20
+
21
+ // Execute project cleanup command if cleanup requested and project has one configured
22
+ // Skip for child sessions - they share parent's resources and shouldn't trigger teardown
23
+ if (cleanup && req.project?.onSessionDeleted && !req.session_.parentSessionId) {
24
+ executeHookAsync(req.project.onSessionDeleted, req.workingDirectory, {
25
+ sessionId: req.session_.id,
26
+ projectId: req.project.id,
27
+ sessionName: req.session_.name,
28
+ });
29
+ }
30
+
31
+ // Broadcast update to project subscribers
32
+ broadcastToProject(req.session_.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
33
+ projectId: req.session_.projectId,
34
+ sessionId: req.params.id,
35
+ session: updated,
36
+ });
37
+
38
+ res.json(updated);
39
+ });
40
+
41
+ // POST /api/sessions/:id/unarchive - Unarchive a session
42
+ router.post('/:id/unarchive', requireSession, (req, res) => {
43
+ const updated = sessions.update(req.params.id, { archived: false });
44
+
45
+ // Broadcast update to project subscribers
46
+ broadcastToProject(req.session_.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
47
+ projectId: req.session_.projectId,
48
+ sessionId: req.params.id,
49
+ session: updated,
50
+ });
51
+
52
+ res.json(updated);
53
+ });
54
+
55
+ // POST /api/sessions/:id/star - Toggle star status for a session
56
+ router.post('/:id/star', requireSession, (req, res) => {
57
+ const updated = sessions.update(req.params.id, { starred: !req.session_.starred });
58
+
59
+ // Broadcast update to project subscribers
60
+ broadcastToProject(req.session_.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
61
+ projectId: req.session_.projectId,
62
+ sessionId: req.params.id,
63
+ session: updated,
64
+ });
65
+
66
+ res.json(updated);
67
+ });
68
+
69
+ export default router;
@@ -0,0 +1,220 @@
1
+ import { Router } from 'express';
2
+ import { commandButtons, commandRuns } from '../database.js';
3
+ import { broadcastToSession, broadcastToProject } from '../websocket.js';
4
+ import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
5
+ import { requireSession, requireSessionAndProject } from '../middleware/sessionLookup.js';
6
+ import { commandRunner } from '../services/commandRunner.js';
7
+ import { databaseManager } from '../db/DatabaseManager.js';
8
+
9
+ const router = Router();
10
+
11
+ /**
12
+ * Broadcast command output to session and project subscribers.
13
+ * @param {{ sessionId: string, projectId: string, runId: string, buttonId: string }} ctx - Context
14
+ * @param {string} output - Output text
15
+ */
16
+ function broadcastCommandOutput(ctx, output) {
17
+ const { sessionId, projectId, runId, buttonId } = ctx;
18
+ broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_OUTPUT, { sessionId, runId, buttonId, output });
19
+ broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_OUTPUT, { projectId, sessionId, runId, buttonId, output });
20
+ }
21
+
22
+ /**
23
+ * Broadcast command completion to session and project subscribers.
24
+ * @param {{ sessionId: string, projectId: string, runId: string, buttonId: string }} ctx - Context
25
+ * @param {{ exitCode: number, output: string }} result - Completion result
26
+ */
27
+ function broadcastCommandComplete(ctx, result) {
28
+ const { sessionId, projectId, runId, buttonId } = ctx;
29
+ const { exitCode, output } = result;
30
+ const status = exitCode === 0 ? 'success' : 'error';
31
+ console.log(`[RUN] Command completed for runId: ${runId}, exitCode: ${exitCode}, status: ${status}`);
32
+ broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_COMPLETE, { sessionId, runId, buttonId, status, exitCode, output });
33
+ console.log(`[RUN] Broadcasting COMMAND_RUN_COMPLETE to project ${projectId}`);
34
+ broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_COMPLETE, { projectId, sessionId, runId, buttonId, status, exitCode, output });
35
+ }
36
+
37
+ /**
38
+ * Broadcast command error to session and project subscribers.
39
+ * @param {{ sessionId: string, projectId: string, runId: string, buttonId: string }} ctx - Context
40
+ * @param {string} errorMessage - Error message
41
+ */
42
+ function broadcastCommandError(ctx, errorMessage) {
43
+ const { sessionId, projectId, runId, buttonId } = ctx;
44
+ console.log(`[RUN] Error for runId: ${runId}: ${errorMessage}`);
45
+ broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_ERROR, { sessionId, runId, buttonId, error: errorMessage });
46
+ broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_ERROR, { projectId, sessionId, runId, buttonId, error: errorMessage });
47
+ }
48
+
49
+ // POST /api/sessions/:id/command-buttons/:buttonId/run - Execute button command
50
+ router.post('/:id/command-buttons/:buttonId/run', requireSessionAndProject, (req, res) => {
51
+ const sessionId = req.params.id;
52
+ const buttonId = req.params.buttonId;
53
+
54
+ console.log(`[RUN] Starting command for buttonId: ${buttonId}, sessionId: ${sessionId}`);
55
+
56
+ const button = commandButtons.getById(buttonId);
57
+ if (!button) {
58
+ return res.status(404).json({ error: 'Command button not found' });
59
+ }
60
+
61
+ // Generate run ID
62
+ const runId = databaseManager.generateId();
63
+
64
+ console.log(`[RUN] Generated runId: ${runId} for command: ${button.command}`);
65
+
66
+ // Return immediately with runId
67
+ res.json({ runId, buttonId, status: 'running', output: '' });
68
+
69
+ // Capture middleware values for use in async callbacks
70
+ const projectId = req.session_.projectId;
71
+ const workingDirectory = req.workingDirectory;
72
+ const ctx = { sessionId, projectId, runId, buttonId };
73
+
74
+ // Broadcast initial "running" status immediately so session list can show the running indicator
75
+ broadcastCommandOutput(ctx, '');
76
+
77
+ // Execute command asynchronously
78
+ (async () => {
79
+ try {
80
+ console.log(`[RUN] Starting async execution for runId: ${runId}`);
81
+ await commandRunner.run(
82
+ { runId, command: button.command, workingDirectory },
83
+ {
84
+ onOutput: (text) => {
85
+ console.log(`[RUN] Output received for runId: ${runId}`);
86
+ broadcastCommandOutput(ctx, text);
87
+ },
88
+ onComplete: (exitCode, output) => broadcastCommandComplete(ctx, { exitCode, output }),
89
+ onError: (message) => broadcastCommandError(ctx, message),
90
+ },
91
+ { sessionId, buttonId }
92
+ );
93
+ } catch (error) {
94
+ console.error(`Error running command button ${buttonId}:`, error);
95
+ broadcastCommandError(ctx, error.message);
96
+ }
97
+ })();
98
+ });
99
+
100
+ // GET /api/sessions/:id/command-buttons/runs - Get active runs for session
101
+ router.get('/:id/command-buttons/runs', requireSession, (req, res) => {
102
+ const sessionId = req.params.id;
103
+
104
+ const activeRuns = commandRunner.getRunsBySession(sessionId);
105
+ res.json(activeRuns);
106
+ });
107
+
108
+ // GET /api/sessions/:id/command-buttons/runs/:runId - Get single run by ID
109
+ router.get('/:id/command-buttons/runs/:runId', requireSession, (req, res) => {
110
+ const { id: sessionId, runId } = req.params;
111
+
112
+ // Check if run is currently running (in memory)
113
+ if (commandRunner.isRunning(runId)) {
114
+ const activeRuns = commandRunner.getRunsBySession(sessionId);
115
+ const run = activeRuns.find((r) => r.runId === runId);
116
+ if (run) {
117
+ return res.json(run);
118
+ }
119
+ }
120
+
121
+ // Otherwise check database
122
+ const run = commandRuns.getById(runId);
123
+ if (!run || run.sessionId !== sessionId) {
124
+ return res.status(404).json({ error: 'Run not found' });
125
+ }
126
+
127
+ res.json({
128
+ runId: run.id,
129
+ buttonId: run.buttonId,
130
+ status: run.status,
131
+ output: run.output,
132
+ exitCode: run.exitCode,
133
+ startedAt: run.startedAt,
134
+ completedAt: run.completedAt,
135
+ });
136
+ });
137
+
138
+ // DELETE /api/sessions/:id/command-buttons/runs/:runId - Delete a command run record
139
+ router.delete('/:id/command-buttons/runs/:runId', requireSessionAndProject, (req, res) => {
140
+ const sessionId = req.params.id;
141
+ const { runId } = req.params;
142
+
143
+ const run = commandRuns.getById(runId);
144
+ if (!run || run.sessionId !== sessionId) {
145
+ return res.status(404).json({ error: 'Run not found' });
146
+ }
147
+
148
+ if (commandRunner.isRunning(runId)) {
149
+ return res.status(409).json({ error: 'Cannot delete a running command. Kill it first.' });
150
+ }
151
+
152
+ commandRuns.deleteById(runId);
153
+
154
+ const projectId = req.session_.projectId;
155
+
156
+ // Broadcast deletion to session and project subscribers
157
+ broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
158
+ runId,
159
+ buttonId: run.buttonId,
160
+ sessionId,
161
+ });
162
+ broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
163
+ runId,
164
+ buttonId: run.buttonId,
165
+ sessionId,
166
+ projectId,
167
+ });
168
+
169
+ res.status(204).send();
170
+ });
171
+
172
+ // DELETE /api/sessions/:id/command-buttons/:buttonId/runs/all - Delete all runs for a button in a session
173
+ router.delete('/:id/command-buttons/:buttonId/runs/all', requireSessionAndProject, (req, res) => {
174
+ const sessionId = req.params.id;
175
+ const { buttonId } = req.params;
176
+
177
+ const button = commandButtons.getById(buttonId);
178
+ if (!button) {
179
+ return res.status(404).json({ error: 'Command button not found' });
180
+ }
181
+
182
+ const { deletedRuns } = commandRuns.deleteByButtonAndSession(buttonId, sessionId);
183
+
184
+ const projectId = req.session_.projectId;
185
+
186
+ // Broadcast individual COMMAND_RUN_DELETED events for each deleted run
187
+ for (const run of deletedRuns) {
188
+ broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
189
+ runId: run.id,
190
+ buttonId: run.buttonId,
191
+ sessionId,
192
+ });
193
+ broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
194
+ runId: run.id,
195
+ buttonId: run.buttonId,
196
+ sessionId,
197
+ projectId,
198
+ });
199
+ }
200
+
201
+ res.status(204).send();
202
+ });
203
+
204
+ // POST /api/sessions/:id/command-buttons/runs/:runId/kill - Kill running command
205
+ router.post('/:id/command-buttons/runs/:runId/kill', requireSession, (req, res) => {
206
+ const sessionId = req.params.id;
207
+ const runId = req.params.runId;
208
+
209
+ console.log(`[KILL] Kill request for runId: ${runId}, sessionId: ${sessionId}`);
210
+
211
+ const killed = commandRunner.kill(runId);
212
+ console.log(`[KILL] Kill result: ${killed} for runId: ${runId}`);
213
+ if (!killed) {
214
+ return res.status(404).json({ error: 'Run not found or already completed' });
215
+ }
216
+
217
+ res.json({ success: true, runId });
218
+ });
219
+
220
+ export default router;
@@ -0,0 +1,168 @@
1
+ import { Router } from 'express';
2
+ import { messages, conversations, projects } from '../database.js';
3
+ import { continueSessionWithExistingMessage } from '../services/sessionManager.js';
4
+ import { broadcastToSession } from '../websocket.js';
5
+ import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
6
+ import { requireSession } from '../middleware/sessionLookup.js';
7
+
8
+ const router = Router();
9
+
10
+ // GET /api/sessions/:id/conversations - List all conversations for a session
11
+ router.get('/:id/conversations', requireSession, (req, res) => {
12
+ // Use getBySessionIdWithBranchInfo to include branching metadata
13
+ const sessionConversations = conversations.getBySessionIdWithBranchInfo(req.params.id);
14
+ res.json(sessionConversations);
15
+ });
16
+
17
+ // POST /api/sessions/:id/conversations - Create new conversation
18
+ router.post('/:id/conversations', requireSession, async (req, res) => {
19
+ // Block creating new conversation while session is running
20
+ if (req.session_.status === 'running') {
21
+ return res.status(400).json({ error: 'Cannot create new conversation while session is running' });
22
+ }
23
+
24
+ const { name } = req.body;
25
+
26
+ const conversation = conversations.create(req.params.id, name || null, true);
27
+
28
+ // Broadcast conversation created event
29
+ broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_CREATED, {
30
+ sessionId: req.params.id,
31
+ conversation,
32
+ });
33
+
34
+ res.status(201).json(conversation);
35
+ });
36
+
37
+ // GET /api/sessions/:id/conversations/:convId - Get specific conversation
38
+ router.get('/:id/conversations/:convId', (req, res) => {
39
+ const conversation = conversations.getById(req.params.convId);
40
+ if (!conversation || conversation.sessionId !== req.params.id) {
41
+ return res.status(404).json({ error: 'Conversation not found' });
42
+ }
43
+
44
+ // Include message count
45
+ const messageCount = messages.getCountByConversationId(req.params.convId);
46
+ res.json({ ...conversation, messageCount });
47
+ });
48
+
49
+ // PATCH /api/sessions/:id/conversations/:convId - Update conversation
50
+ router.patch('/:id/conversations/:convId', requireSession, async (req, res) => {
51
+ const conversation = conversations.getById(req.params.convId);
52
+ if (!conversation || conversation.sessionId !== req.params.id) {
53
+ return res.status(404).json({ error: 'Conversation not found' });
54
+ }
55
+
56
+ const { name, isActive } = req.body;
57
+
58
+ // Block switching conversation while session is running
59
+ if (isActive && req.session_.status === 'running') {
60
+ return res.status(400).json({ error: 'Cannot switch conversation while session is running' });
61
+ }
62
+
63
+ const updateData = {};
64
+ if (name !== undefined) updateData.name = name;
65
+ if (isActive !== undefined) updateData.isActive = isActive;
66
+
67
+ const updated = conversations.update(req.params.convId, updateData);
68
+
69
+ // Broadcast conversation updated event
70
+ broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_UPDATED, {
71
+ sessionId: req.params.id,
72
+ conversation: updated,
73
+ });
74
+
75
+ res.json(updated);
76
+ });
77
+
78
+ // DELETE /api/sessions/:id/conversations/:convId - Delete conversation
79
+ router.delete('/:id/conversations/:convId', requireSession, (req, res) => {
80
+ // Block deleting conversation while session is running
81
+ if (req.session_.status === 'running') {
82
+ return res.status(400).json({ error: 'Cannot delete conversation while session is running' });
83
+ }
84
+
85
+ const conversation = conversations.getById(req.params.convId);
86
+ if (!conversation || conversation.sessionId !== req.params.id) {
87
+ return res.status(404).json({ error: 'Conversation not found' });
88
+ }
89
+
90
+ // Delete and handle active conversation logic
91
+ const newActive = conversations.deleteAndHandleActive(req.params.convId);
92
+
93
+ // Broadcast conversation deleted event
94
+ broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_DELETED, {
95
+ sessionId: req.params.id,
96
+ conversationId: req.params.convId,
97
+ newActiveConversation: newActive,
98
+ });
99
+
100
+ res.status(204).send();
101
+ });
102
+
103
+ // POST /api/sessions/:id/conversations/:convId/branch - Create a branch from a conversation
104
+ router.post('/:id/conversations/:convId/branch', requireSession, async (req, res) => {
105
+ // Block branching while session is running
106
+ if (req.session_.status === 'running') {
107
+ return res.status(400).json({ error: 'Cannot branch conversation while session is running' });
108
+ }
109
+
110
+ const conversation = conversations.getById(req.params.convId);
111
+ if (!conversation || conversation.sessionId !== req.params.id) {
112
+ return res.status(404).json({ error: 'Conversation not found' });
113
+ }
114
+
115
+ const { messageId, prompt } = req.body;
116
+
117
+ if (!messageId) {
118
+ return res.status(400).json({ error: 'messageId is required' });
119
+ }
120
+
121
+ if (!prompt || !prompt.trim()) {
122
+ return res.status(400).json({ error: 'prompt is required' });
123
+ }
124
+
125
+ try {
126
+ // Create the branch
127
+ // Note: name is auto-generated from the prompt in ConversationRepository.branch()
128
+ const branchConversation = conversations.branch(
129
+ req.params.convId,
130
+ messageId,
131
+ null, // name is auto-generated from prompt
132
+ prompt
133
+ );
134
+
135
+ // Broadcast the new conversation to session subscribers
136
+ broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_CREATED, {
137
+ sessionId: req.params.id,
138
+ conversation: branchConversation,
139
+ });
140
+
141
+ // Auto-submit to Claude: Start the session with the new prompt
142
+ // The branch already has the user message, so we use continueSessionWithExistingMessage
143
+ // which triggers Claude's response WITHOUT creating a duplicate user message
144
+ try {
145
+ const project = projects.getById(req.session_.projectId);
146
+ const workingDirectory = req.session_.gitWorktree || project?.workingDirectory;
147
+ if (workingDirectory) {
148
+ await continueSessionWithExistingMessage(
149
+ req.params.id,
150
+ branchConversation.id,
151
+ workingDirectory,
152
+ { systemPrompt: project?.systemPrompt }
153
+ );
154
+ }
155
+ } catch (err) {
156
+ console.error('Failed to auto-start branched conversation:', err);
157
+ // Don't fail the whole request if auto-start fails
158
+ // User can manually trigger from the UI
159
+ }
160
+
161
+ res.status(201).json(branchConversation);
162
+ } catch (error) {
163
+ console.error('Branch conversation error:', error);
164
+ res.status(400).json({ error: error.message });
165
+ }
166
+ });
167
+
168
+ export default router;
@@ -0,0 +1,72 @@
1
+ import { Router } from 'express';
2
+ import { messages } from '../database.js';
3
+ import { broadcastToSession } from '../websocket.js';
4
+ import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
5
+ import { requireSession } from '../middleware/sessionLookup.js';
6
+ import { validateDraftSession, startDraft, DraftSessionError } from '../services/draftSessionService.js';
7
+
8
+ const router = Router();
9
+
10
+ // PUT /api/sessions/:id/initial-prompt - Update the initial prompt for a draft session
11
+ router.put('/:id/initial-prompt', requireSession, (req, res) => {
12
+ const validation = validateDraftSession(req.session_);
13
+ if (!validation.valid) {
14
+ return res.status(400).json({ error: validation.error });
15
+ }
16
+
17
+ // Get the request body
18
+ const { prompt } = req.body;
19
+
20
+ // Validate prompt is provided and non-empty
21
+ if (!prompt || typeof prompt !== 'string' || prompt.trim() === '') {
22
+ return res.status(400).json({ error: 'Prompt must be a non-empty string' });
23
+ }
24
+
25
+ try {
26
+ const allMessages = messages.getBySessionId(req.session_.id);
27
+ // Find the first user message and update it
28
+ const userMessages = allMessages.filter(msg => msg.role === 'user');
29
+ if (userMessages.length === 0) {
30
+ return res.status(400).json({ error: 'No initial prompt found' });
31
+ }
32
+
33
+ const initialMessage = userMessages[0];
34
+ const updatedMessage = messages.updateContent(initialMessage.id, prompt);
35
+
36
+ // Broadcast the update to session subscribers
37
+ broadcastToSession(req.session_.id, WS_MESSAGE_TYPES.MESSAGE_UPDATED, {
38
+ sessionId: req.session_.id,
39
+ message: updatedMessage,
40
+ });
41
+
42
+ res.json({ success: true, message: updatedMessage });
43
+ } catch (error) {
44
+ console.error('Update initial prompt error:', error);
45
+ res.status(500).json({ error: error.message });
46
+ }
47
+ });
48
+
49
+ // POST /api/sessions/:id/start - Start a draft session (waiting status with no assistant messages)
50
+ router.post('/:id/start', requireSession, async (req, res) => {
51
+ const validation = validateDraftSession(req.session_);
52
+ if (!validation.valid) {
53
+ return res.status(400).json({ error: validation.error });
54
+ }
55
+
56
+ try {
57
+ const updatedSession = await startDraft(req.session_, {
58
+ prompt: req.body.prompt,
59
+ model: req.body.model,
60
+ });
61
+
62
+ res.json({ success: true, session: updatedSession });
63
+ } catch (error) {
64
+ if (error instanceof DraftSessionError) {
65
+ return res.status(error.statusCode).json({ error: error.message });
66
+ }
67
+ console.error('Start session error:', error);
68
+ res.status(500).json({ error: error.message });
69
+ }
70
+ });
71
+
72
+ export default router;