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
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "circuschief",
3
+ "version": "0.1.0",
4
+ "description": "Local-first web UI for managing Claude Code sessions",
5
+ "type": "module",
6
+ "bin": {
7
+ "circuschief": "./packages/server/bin/cli.js"
8
+ },
9
+ "files": [
10
+ "packages/server/bin/",
11
+ "packages/server/src/",
12
+ "packages/shared/src/",
13
+ "packages/shared/package.json",
14
+ "packages/web/dist/"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "dependencies": {
20
+ "zod": "^4.2.1",
21
+ "@anthropic-ai/claude-agent-sdk": "^0.1.76",
22
+ "@anthropic-ai/sdk": "^0.71.2",
23
+ "better-sqlite3": "^11.7.0",
24
+ "cors": "^2.8.5",
25
+ "express": "^4.21.2",
26
+ "http-proxy-middleware": "^3.0.5",
27
+ "liquidjs": "^10.24.0",
28
+ "multer": "^1.4.5-lts.1",
29
+ "nanoid": "^3.3.0",
30
+ "ws": "^8.18.0",
31
+ "yaml": "^2.8.2"
32
+ }
33
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+
3
+ process.env.NODE_ENV = 'production';
4
+ import '../src/index.js';
@@ -0,0 +1,64 @@
1
+ import { ClaudeCodeAdapter } from './adapters/ClaudeCodeAdapter.js';
2
+
3
+ /**
4
+ * Factory/registry for agent adapters.
5
+ * Session Manager uses this to get the appropriate adapter for a session's agent type.
6
+ */
7
+ export class AgentGateway {
8
+ constructor() {
9
+ /** @type {Map<string, typeof import('./BaseAgent.js').BaseAgent>} */
10
+ this.adapters = new Map();
11
+ this._registerDefaultAdapters();
12
+ }
13
+
14
+ _registerDefaultAdapters() {
15
+ this.registerAdapter('claude-code', ClaudeCodeAdapter);
16
+ }
17
+
18
+ /**
19
+ * Register an adapter class for a given agent type.
20
+ * @param {string} agentType
21
+ * @param {typeof import('./BaseAgent.js').BaseAgent} AdapterClass
22
+ */
23
+ registerAdapter(agentType, AdapterClass) {
24
+ this.adapters.set(agentType, AdapterClass);
25
+ }
26
+
27
+ /**
28
+ * Create an agent instance for the given type.
29
+ * @param {string} agentType - e.g., 'claude-code'
30
+ * @param {import('./types.js').AgentConfig} [config]
31
+ * @returns {import('./BaseAgent.js').BaseAgent}
32
+ */
33
+ createAgent(agentType, config = {}) {
34
+ const AdapterClass = this.adapters.get(agentType);
35
+ if (!AdapterClass) {
36
+ throw new Error(
37
+ `Unknown agent type: "${agentType}". Available: ${this.getAvailableAgents().join(', ')}`
38
+ );
39
+ }
40
+ return new AdapterClass({ ...config, agentType });
41
+ }
42
+
43
+ /**
44
+ * @returns {string[]} List of registered agent type names
45
+ */
46
+ getAvailableAgents() {
47
+ return Array.from(this.adapters.keys());
48
+ }
49
+
50
+ /**
51
+ * Get capabilities for an agent type (uses a static check, no instantiation).
52
+ * @param {string} agentType
53
+ * @returns {Object|null}
54
+ */
55
+ getAgentCapabilities(agentType) {
56
+ const AdapterClass = this.adapters.get(agentType);
57
+ if (!AdapterClass) return null;
58
+ // Capabilities are static per adapter class
59
+ return new AdapterClass({}).getCapabilities();
60
+ }
61
+ }
62
+
63
+ // Singleton instance
64
+ export const agentGateway = new AgentGateway();
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Base agent interface. All adapters must implement `execute()` which returns
3
+ * an async generator of SDK events.
4
+ */
5
+ export class BaseAgent {
6
+ constructor(config = {}) {
7
+ this.config = config;
8
+ }
9
+
10
+ /**
11
+ * Execute a query and yield events as an async generator.
12
+ * This mirrors the SDK's `query()` contract: callers consume with `for await...of`.
13
+ *
14
+ * @param {import('./types.js').AgentQueryParams} queryParams - { prompt, options? }
15
+ * @returns {AsyncGenerator<Object>} - Yields raw SDK events
16
+ */
17
+ async *execute(_queryParams) { // eslint-disable-line require-yield
18
+ throw new Error('execute() must be implemented by adapter');
19
+ }
20
+
21
+ /**
22
+ * Check if agent supports session resumption via `options.resume`
23
+ * @returns {boolean}
24
+ */
25
+ supportsResume() {
26
+ return false;
27
+ }
28
+
29
+ /**
30
+ * Get agent capabilities
31
+ * @returns {{ streaming: boolean, thinking: boolean, toolUse: boolean, resume: boolean }}
32
+ */
33
+ getCapabilities() {
34
+ return {
35
+ streaming: false,
36
+ thinking: false,
37
+ toolUse: false,
38
+ resume: false,
39
+ };
40
+ }
41
+ }
@@ -0,0 +1,73 @@
1
+ import { agentCallLogger } from '../services/agentCallLogger.js';
2
+
3
+ /**
4
+ * Extract usage data from a result event
5
+ * @param {Object} event - The result event
6
+ * @returns {Object|null} Usage data or null
7
+ */
8
+ function extractUsageFromEvent(event) {
9
+ const modelUsageEntry = event.modelUsage
10
+ ? Object.values(event.modelUsage)[0]
11
+ : null;
12
+
13
+ if (!modelUsageEntry && !event.usage) {
14
+ return null;
15
+ }
16
+
17
+ return {
18
+ inputTokens: modelUsageEntry?.inputTokens || event.usage?.input_tokens || 0,
19
+ outputTokens: modelUsageEntry?.outputTokens || event.usage?.output_tokens || 0,
20
+ thinkingTokens: 0,
21
+ cacheReadInputTokens: modelUsageEntry?.cacheReadInputTokens || event.usage?.cache_read_input_tokens || 0,
22
+ cacheCreationInputTokens: modelUsageEntry?.cacheCreationInputTokens || event.usage?.cache_creation_input_tokens || 0,
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Decorator that wraps a BaseAgent's execute() to log call start/end/errors.
28
+ * The wrapper is transparent to the consumer -- it yields the same events.
29
+ */
30
+ export class LoggingAgentWrapper {
31
+ /**
32
+ * @param {import('./BaseAgent.js').BaseAgent} agent - The agent to wrap
33
+ */
34
+ constructor(agent) {
35
+ this.agent = agent;
36
+ }
37
+
38
+ /**
39
+ * Wraps agent.execute() with logging.
40
+ * @param {import('./types.js').AgentQueryParams} queryParams
41
+ * @param {import('./types.js').AgentCallMeta} meta - Logging metadata (sessionId, callType, etc.)
42
+ * @returns {AsyncGenerator<Object>} Same events as the inner agent
43
+ */
44
+ async *execute(queryParams, meta) {
45
+ const callId = agentCallLogger.startCall(meta);
46
+
47
+ try {
48
+ for await (const event of this.agent.execute(queryParams, meta)) {
49
+ // Capture final usage from 'result' events
50
+ const usage = (event.type === 'result' && event.subtype !== 'error')
51
+ ? extractUsageFromEvent(event)
52
+ : null;
53
+ if (usage) agentCallLogger.updateUsage(callId, usage);
54
+
55
+ yield event;
56
+ }
57
+
58
+ agentCallLogger.completeCall(callId, { success: true });
59
+ } catch (error) {
60
+ agentCallLogger.completeCall(callId, { success: false, error });
61
+ throw error;
62
+ }
63
+ }
64
+
65
+ // Proxy capability methods
66
+ supportsResume() {
67
+ return this.agent.supportsResume();
68
+ }
69
+
70
+ getCapabilities() {
71
+ return this.agent.getCapabilities();
72
+ }
73
+ }
@@ -0,0 +1,33 @@
1
+ import { query } from '@anthropic-ai/claude-agent-sdk';
2
+ import { BaseAgent } from '../BaseAgent.js';
3
+
4
+ /**
5
+ * Adapter for Claude Code SDK. Wraps the SDK's `query()` function
6
+ * which returns an async generator of events.
7
+ *
8
+ * The adapter does NOT transform events -- it passes through raw SDK events.
9
+ * Event handling remains in sessionManager's handleStreamEvent().
10
+ */
11
+ export class ClaudeCodeAdapter extends BaseAgent {
12
+ /**
13
+ * Execute a query against the Claude Code SDK.
14
+ * @param {import('../types.js').AgentQueryParams} queryParams - { prompt, options? }
15
+ * @yields {Object} Raw SDK events (system, assistant, tool_result, stream_event, result)
16
+ */
17
+ async *execute(queryParams) {
18
+ yield* query(queryParams);
19
+ }
20
+
21
+ supportsResume() {
22
+ return true;
23
+ }
24
+
25
+ getCapabilities() {
26
+ return {
27
+ streaming: true,
28
+ thinking: true,
29
+ toolUse: true,
30
+ resume: true,
31
+ };
32
+ }
33
+ }
@@ -0,0 +1,26 @@
1
+ import { BaseAgent } from '../BaseAgent.js';
2
+
3
+ /**
4
+ * Stub adapter for OpenAI Codex / future agents.
5
+ * When implemented, this would:
6
+ * 1. Transform AgentQueryParams into Codex API format
7
+ * 2. Call the Codex API
8
+ * 3. Yield events normalized to the SDK event format (system, assistant, tool_result, stream_event, result)
9
+ *
10
+ * Phase 7 note: A NormalizedEvent format should be introduced at this point,
11
+ * along with refactoring handleStreamEvent to consume normalized events.
12
+ */
13
+ export class CodexAdapter extends BaseAgent {
14
+ async *execute(_queryParams) { // eslint-disable-line require-yield
15
+ throw new Error('CodexAdapter is not yet implemented');
16
+ }
17
+
18
+ getCapabilities() {
19
+ return {
20
+ streaming: true,
21
+ thinking: false, // Codex doesn't have explicit thinking
22
+ toolUse: true,
23
+ resume: false, // Codex may not support session resume
24
+ };
25
+ }
26
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Agent abstraction layer type definitions.
3
+ *
4
+ * @typedef {Object} AgentConfig
5
+ * @property {string} agentType - 'claude-code' | 'codex' | etc.
6
+ * @property {string} [model] - Model identifier
7
+ * @property {Object} [providerConfig] - Provider-specific configuration
8
+ */
9
+
10
+ /**
11
+ * @typedef {Object} AgentQueryParams
12
+ * @property {string} prompt - The prompt text (may include attachments/context)
13
+ * @property {AgentQueryOptions} [options] - SDK options (omitted in mock mode)
14
+ */
15
+
16
+ /**
17
+ * @typedef {Object} AgentQueryOptions
18
+ * @property {string} cwd - Working directory
19
+ * @property {AbortController} abortController - Abort controller
20
+ * @property {boolean} includePartialMessages - Whether to include partial messages
21
+ * @property {string} permissionMode - 'default' | 'bypassPermissions'
22
+ * @property {string[]} settingSources - e.g., ['project']
23
+ * @property {string} [resume] - Claude session ID for resumption
24
+ * @property {Object} env - Environment variables
25
+ * @property {Function} spawnClaudeCodeProcess - Process spawner function
26
+ * @property {string} [model] - Model to use
27
+ * @property {string} systemPrompt - System prompt string
28
+ */
29
+
30
+ /**
31
+ * Agent call metadata for logging purposes
32
+ * @typedef {Object} AgentCallMeta
33
+ * @property {string} sessionId
34
+ * @property {string} [conversationId]
35
+ * @property {string} callType - 'runSession' | 'continueSession' | 'continueSessionWithExistingMessage'
36
+ * @property {string} [agentType]
37
+ * @property {string} [model]
38
+ * @property {string} [effortLevel] - Effort level for the call
39
+ * @property {boolean} [isResume] - Whether this call uses SDK session resume
40
+ * @property {number} promptLength - Character length of prompt
41
+ */
42
+
43
+ export {};
@@ -0,0 +1,111 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import crypto from 'crypto';
4
+
5
+ /**
6
+ * CassetteStore handles reading/writing VCR cassette files.
7
+ * Uses atomic writes to prevent corruption from parallel test workers.
8
+ *
9
+ * Cassette format:
10
+ * {
11
+ * "key": "runSession-a1b2c3d4e5f6g7h8",
12
+ * "prompt": "<original user prompt (truncated)>",
13
+ * "model": "claude-haiku-4-5-20251001",
14
+ * "recordedAt": "2025-07-15T...",
15
+ * "events": [...]
16
+ * }
17
+ */
18
+
19
+ export class CassetteStore {
20
+ /**
21
+ * Load a cassette by key
22
+ * @param {string} cassetteDir - Directory containing cassettes
23
+ * @param {string} key - Cassette key
24
+ * @returns {object|null} Cassette object or null if not found
25
+ */
26
+ static load(cassetteDir, key) {
27
+ const filePath = path.join(cassetteDir, `${key}.json`);
28
+
29
+ if (!fs.existsSync(filePath)) {
30
+ return null;
31
+ }
32
+
33
+ try {
34
+ const content = fs.readFileSync(filePath, 'utf-8');
35
+ return JSON.parse(content);
36
+ } catch (error) {
37
+ console.error(`Failed to load cassette ${key}:`, error.message);
38
+ return null;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Save a cassette with atomic write (temp file + rename)
44
+ * @param {string} cassetteDir - Directory to save cassette
45
+ * @param {string} key - Cassette key
46
+ * @param {object} cassette - Cassette data to save
47
+ */
48
+ static save(cassetteDir, key, cassette) {
49
+ // Ensure directory exists
50
+ if (!fs.existsSync(cassetteDir)) {
51
+ fs.mkdirSync(cassetteDir, { recursive: true });
52
+ }
53
+
54
+ const targetPath = path.join(cassetteDir, `${key}.json`);
55
+ const tempPath = `${targetPath}.tmp`;
56
+
57
+ // Prepare cassette with metadata
58
+ const cassetteWithMeta = {
59
+ ...cassette,
60
+ key,
61
+ recordedAt: new Date().toISOString(),
62
+ };
63
+
64
+ try {
65
+ // Atomic write: write to temp file, then rename
66
+ fs.writeFileSync(tempPath, JSON.stringify(cassetteWithMeta, null, 2), 'utf-8');
67
+ fs.renameSync(tempPath, targetPath);
68
+ } catch (error) {
69
+ // Clean up temp file if write failed
70
+ if (fs.existsSync(tempPath)) {
71
+ fs.unlinkSync(tempPath);
72
+ }
73
+ throw new Error(`Failed to save cassette ${key}: ${error.message}`);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Build a cassette key from a call type and prompt text
79
+ * Keys on callType + hash of the original user prompt only
80
+ * (excludes system prompt, UUIDs, ports, and conversation context)
81
+ *
82
+ * @param {string} callType - Type of call (e.g., 'runSession', 'summary')
83
+ * @param {string} promptText - The raw user prompt text
84
+ * @returns {string} Cassette key
85
+ */
86
+ static buildKey(callType, promptText) {
87
+ const hash = crypto.createHash('sha256').update(promptText).digest('hex').substring(0, 16);
88
+ return `${callType}-${hash}`;
89
+ }
90
+
91
+ /**
92
+ * Deep copy an event for storage
93
+ * Handles non-cloneable objects with shallow-copy fallback
94
+ *
95
+ * @param {any} event - Event to copy
96
+ * @returns {any} Copied event
97
+ */
98
+ static deepCopyEvent(event) {
99
+ // Handle null and undefined explicitly
100
+ if (event === null || event === undefined) {
101
+ return event;
102
+ }
103
+
104
+ try {
105
+ return JSON.parse(JSON.stringify(event));
106
+ } catch {
107
+ // Fallback to shallow copy for objects with non-cloneable refs
108
+ return { ...event };
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,126 @@
1
+ import { CassetteStore } from './CassetteStore.js';
2
+
3
+ /**
4
+ * VCR (Video Cassette Recorder) Agent Adapter
5
+ *
6
+ * A decorator/wrapper around any agent that provides record/replay functionality.
7
+ * Same pattern as LoggingAgentWrapper — does NOT extend BaseAgent.
8
+ *
9
+ * Modes:
10
+ * - 'auto': Replay if cassette exists, record if not (default for E2E)
11
+ * - 'record': Always record (overwrite existing cassettes)
12
+ * - 'replay': Always replay (fail if cassette missing)
13
+ * - unset: VCR disabled — pass through to inner agent
14
+ *
15
+ * Environment variable: VCR_MODE=auto|record|replay
16
+ */
17
+ export class VCRAgentAdapter {
18
+ /**
19
+ * @param {object} innerAgent - The real agent to wrap
20
+ * @param {object} options - Configuration options
21
+ * @param {string} options.cassetteDir - Directory for cassette files
22
+ */
23
+ constructor(innerAgent, options = {}) {
24
+ this.innerAgent = innerAgent;
25
+ this.cassetteDir = options.cassetteDir || 'tests/e2e/cassettes';
26
+ // Only enable VCR if VCR_MODE is explicitly set
27
+ this.mode = process.env.VCR_MODE || undefined;
28
+ }
29
+
30
+ /**
31
+ * Execute query with record/replay behavior
32
+ * @param {object} queryParams - Query parameters
33
+ * @param {object} meta - Metadata (includes callType)
34
+ * @returns {AsyncGenerator} Generator yielding events
35
+ */
36
+ async *execute(queryParams, meta) {
37
+ const key = this.buildCassetteKey(queryParams, meta);
38
+
39
+ if (this.mode === 'record') {
40
+ yield* this.record(key, queryParams, meta);
41
+ } else if (this.mode === 'replay') {
42
+ const cassette = CassetteStore.load(this.cassetteDir, key);
43
+ if (!cassette) {
44
+ throw new Error(`VCR replay: no cassette found for "${key}"`);
45
+ }
46
+ yield* this.replay(cassette);
47
+ } else if (this.mode === 'auto') {
48
+ const cassette = CassetteStore.load(this.cassetteDir, key);
49
+ if (cassette) {
50
+ yield* this.replay(cassette);
51
+ } else {
52
+ yield* this.record(key, queryParams, meta);
53
+ }
54
+ } else {
55
+ // VCR disabled — pass through to inner agent
56
+ yield* this.innerAgent.execute(queryParams, meta);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Build cassette key from query parameters
62
+ * Uses callType + hash of original user prompt only
63
+ *
64
+ * @param {object} queryParams - Query parameters
65
+ * @param {object} meta - Metadata
66
+ * @returns {string} Cassette key
67
+ */
68
+ buildCassetteKey(queryParams, meta) {
69
+ const callType = meta?.callType || 'unknown';
70
+ const promptText = queryParams.prompt || '';
71
+ return CassetteStore.buildKey(callType, promptText);
72
+ }
73
+
74
+ /**
75
+ * Replay from a cassette
76
+ * @param {object} cassette - Cassette to replay
77
+ * @returns {AsyncGenerator} Generator yielding events
78
+ */
79
+ async *replay(cassette) {
80
+ for (const event of cassette.events) {
81
+ // Small delay to simulate streaming
82
+ await new Promise((resolve) => setTimeout(resolve, 5));
83
+ yield event;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Record to a cassette
89
+ * @param {string} key - Cassette key
90
+ * @param {object} queryParams - Query parameters
91
+ * @param {object} meta - Metadata
92
+ * @returns {AsyncGenerator} Generator yielding events
93
+ */
94
+ async *record(key, queryParams, meta) {
95
+ const events = [];
96
+
97
+ // Execute real query and collect events
98
+ for await (const event of this.innerAgent.execute(queryParams, meta)) {
99
+ events.push(CassetteStore.deepCopyEvent(event));
100
+ yield event;
101
+ }
102
+
103
+ // Save cassette
104
+ CassetteStore.save(this.cassetteDir, key, {
105
+ prompt: queryParams.prompt?.substring(0, 500),
106
+ model: queryParams.options?.model,
107
+ events,
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Proxy resume support to inner agent
113
+ * @returns {boolean}
114
+ */
115
+ supportsResume() {
116
+ return this.innerAgent.supportsResume?.() ?? false;
117
+ }
118
+
119
+ /**
120
+ * Proxy capabilities to inner agent
121
+ * @returns {object}
122
+ */
123
+ getCapabilities() {
124
+ return this.innerAgent.getCapabilities?.() ?? {};
125
+ }
126
+ }
@@ -0,0 +1,71 @@
1
+ import { CassetteStore } from './CassetteStore.js';
2
+
3
+ /**
4
+ * Build cassette key for summary calls
5
+ * Summary prompts are built from a fixed template + message content, so these are stable
6
+ *
7
+ * @param {object} queryParams - Query parameters
8
+ * @param {string|null} keyHint - Optional stable key hint to use instead of prompt text.
9
+ * When provided, the cassette key is derived from keyHint rather than the dynamic prompt,
10
+ * ensuring stable keys across test runs even when prompt content changes.
11
+ * @returns {string} Cassette key
12
+ */
13
+ function buildSummaryKey(queryParams, keyHint = null) {
14
+ const keySource = keyHint || queryParams.prompt || '';
15
+ return CassetteStore.buildKey('summary', keySource);
16
+ }
17
+
18
+ /**
19
+ * Create a VCR-wrapped query function for summary service
20
+ *
21
+ * The summary service calls the SDK query() directly rather than going through
22
+ * the agent gateway. This wrapper provides the same record/replay behavior.
23
+ *
24
+ * @param {function} realQueryFn - The real query function to wrap
25
+ * @param {string} cassetteDir - Directory for cassette files
26
+ * @param {string|null} keyHint - Optional stable key hint for cassette key generation.
27
+ * When provided, overrides the default prompt-based key, ensuring stable cassette
28
+ * keys across test runs even when prompt content is dynamic.
29
+ * @returns {function} VCR-wrapped query function
30
+ */
31
+ export function createVCRQueryFn(realQueryFn, cassetteDir, keyHint = null) {
32
+ // Only enable VCR if VCR_MODE is explicitly set
33
+ const mode = process.env.VCR_MODE || undefined;
34
+
35
+ return async function* vcrQuery(queryParams) {
36
+ const key = buildSummaryKey(queryParams, keyHint);
37
+ const cassette = CassetteStore.load(cassetteDir, key);
38
+
39
+ // VCR disabled - pass through to real query
40
+ if (!mode) {
41
+ yield* realQueryFn(queryParams);
42
+ return;
43
+ }
44
+
45
+ if (mode === 'record' || (mode === 'auto' && !cassette)) {
46
+ // Record mode
47
+ const events = [];
48
+ for await (const event of realQueryFn(queryParams)) {
49
+ events.push(CassetteStore.deepCopyEvent(event));
50
+ yield event;
51
+ }
52
+ CassetteStore.save(cassetteDir, key, {
53
+ prompt: queryParams.prompt?.substring(0, 200),
54
+ model: queryParams.options?.model,
55
+ events,
56
+ });
57
+ } else if (cassette) {
58
+ // Replay mode (cassette exists)
59
+ for (const event of cassette.events) {
60
+ // Small delay to simulate streaming
61
+ await new Promise((resolve) => setTimeout(resolve, 5));
62
+ yield event;
63
+ }
64
+ } else {
65
+ // Replay mode but no cassette
66
+ throw new Error(
67
+ `VCR replay: summary cassette not found for "${key}". Run with VCR_MODE=record to create it.`
68
+ );
69
+ }
70
+ };
71
+ }