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.
- package/package.json +33 -0
- package/packages/server/bin/cli.js +4 -0
- package/packages/server/src/agents/AgentGateway.js +64 -0
- package/packages/server/src/agents/BaseAgent.js +41 -0
- package/packages/server/src/agents/LoggingAgentWrapper.js +73 -0
- package/packages/server/src/agents/adapters/ClaudeCodeAdapter.js +33 -0
- package/packages/server/src/agents/adapters/CodexAdapter.js +26 -0
- package/packages/server/src/agents/types.js +43 -0
- package/packages/server/src/agents/vcr/CassetteStore.js +111 -0
- package/packages/server/src/agents/vcr/VCRAgentAdapter.js +126 -0
- package/packages/server/src/agents/vcr/VCRSummaryWrapper.js +71 -0
- package/packages/server/src/api/canvas-helpers.js +249 -0
- package/packages/server/src/api/canvas-trash-routes.js +205 -0
- package/packages/server/src/api/canvas.js +331 -0
- package/packages/server/src/api/commandButtons.js +312 -0
- package/packages/server/src/api/commands.js +169 -0
- package/packages/server/src/api/filesystem.js +62 -0
- package/packages/server/src/api/git.js +85 -0
- package/packages/server/src/api/index.js +44 -0
- package/packages/server/src/api/kanban.js +342 -0
- package/packages/server/src/api/metrics.js +194 -0
- package/packages/server/src/api/projects-helpers.js +43 -0
- package/packages/server/src/api/projects-session-helpers.js +295 -0
- package/packages/server/src/api/projects.js +384 -0
- package/packages/server/src/api/providers.js +249 -0
- package/packages/server/src/api/quickResponses.js +129 -0
- package/packages/server/src/api/sessions-archive.js +69 -0
- package/packages/server/src/api/sessions-commands.js +220 -0
- package/packages/server/src/api/sessions-conversations.js +168 -0
- package/packages/server/src/api/sessions-draft.js +72 -0
- package/packages/server/src/api/sessions-lifecycle.js +190 -0
- package/packages/server/src/api/sessions-messages.js +141 -0
- package/packages/server/src/api/sessions-notes.js +51 -0
- package/packages/server/src/api/sessions-patch.js +252 -0
- package/packages/server/src/api/sessions-streaming.js +86 -0
- package/packages/server/src/api/sessions.js +269 -0
- package/packages/server/src/api/settings.js +194 -0
- package/packages/server/src/api/templates.js +63 -0
- package/packages/server/src/app.js +51 -0
- package/packages/server/src/database.js +58 -0
- package/packages/server/src/db/AgentCallLogRepository.js +322 -0
- package/packages/server/src/db/AttachmentRepository.js +191 -0
- package/packages/server/src/db/BaseRepository.js +39 -0
- package/packages/server/src/db/CanvasItemRepository.js +315 -0
- package/packages/server/src/db/CommandButtonRepository.js +75 -0
- package/packages/server/src/db/CommandRunRepository.js +219 -0
- package/packages/server/src/db/ConversationRepository.js +379 -0
- package/packages/server/src/db/DatabaseManager.js +91 -0
- package/packages/server/src/db/KanbanBoardRepository.js +92 -0
- package/packages/server/src/db/KanbanCardRepository.js +286 -0
- package/packages/server/src/db/KanbanLaneRepository.js +279 -0
- package/packages/server/src/db/MessageRepository.js +156 -0
- package/packages/server/src/db/ProjectDefaultsRepository.js +173 -0
- package/packages/server/src/db/ProjectRepository.js +110 -0
- package/packages/server/src/db/ProviderRepository.js +307 -0
- package/packages/server/src/db/QuickResponseRepository.js +186 -0
- package/packages/server/src/db/SessionNoteRepository.js +60 -0
- package/packages/server/src/db/SessionRepository.js +314 -0
- package/packages/server/src/db/SessionSummaryRepository.js +200 -0
- package/packages/server/src/db/SessionTemplateRepository.js +171 -0
- package/packages/server/src/db/SettingsRepository.js +211 -0
- package/packages/server/src/db/TodoRepository.js +132 -0
- package/packages/server/src/db/WorkLogRepository.js +122 -0
- package/packages/server/src/db/conversation-helpers.js +119 -0
- package/packages/server/src/db/index.js +100 -0
- package/packages/server/src/db/migrations/canvasItemsMigrations.js +109 -0
- package/packages/server/src/db/migrations/conversationsMigrations.js +183 -0
- package/packages/server/src/db/migrations/index.js +199 -0
- package/packages/server/src/db/migrations/kanbanMigrations.js +99 -0
- package/packages/server/src/db/migrations/migrationUtils.js +55 -0
- package/packages/server/src/db/migrations/miscMigrations.js +242 -0
- package/packages/server/src/db/migrations/projectsMigrations.js +95 -0
- package/packages/server/src/db/migrations/sessionsMigrations.js +282 -0
- package/packages/server/src/db/session-helpers.js +150 -0
- package/packages/server/src/index.js +106 -0
- package/packages/server/src/logger.js +22 -0
- package/packages/server/src/middleware/sessionLookup.js +57 -0
- package/packages/server/src/middleware/upload.js +94 -0
- package/packages/server/src/schema.sql +363 -0
- package/packages/server/src/services/agentCallLogger.js +116 -0
- package/packages/server/src/services/canvasStore.js +56 -0
- package/packages/server/src/services/childSessionContext.js +61 -0
- package/packages/server/src/services/commandRunner.js +422 -0
- package/packages/server/src/services/conversationContext.js +72 -0
- package/packages/server/src/services/diffService.js +172 -0
- package/packages/server/src/services/draftSessionService.js +181 -0
- package/packages/server/src/services/encryption.js +134 -0
- package/packages/server/src/services/ghService.js +169 -0
- package/packages/server/src/services/gitService.js +520 -0
- package/packages/server/src/services/gitSessionSetup.js +48 -0
- package/packages/server/src/services/hookService.js +60 -0
- package/packages/server/src/services/kanbanService.js +262 -0
- package/packages/server/src/services/kanbanTriggers.js +273 -0
- package/packages/server/src/services/nodeSpawnHelper.js +63 -0
- package/packages/server/src/services/prStatusService.js +204 -0
- package/packages/server/src/services/prUrlService.js +224 -0
- package/packages/server/src/services/providerTestService.js +81 -0
- package/packages/server/src/services/scheduleService.js +110 -0
- package/packages/server/src/services/schedulerService.js +281 -0
- package/packages/server/src/services/sessionDuplicator.js +63 -0
- package/packages/server/src/services/sessionErrors.js +173 -0
- package/packages/server/src/services/sessionExecution.js +378 -0
- package/packages/server/src/services/sessionManager.js +356 -0
- package/packages/server/src/services/sessionPrompts.js +427 -0
- package/packages/server/src/services/sessionProvider.js +107 -0
- package/packages/server/src/services/slashCommandDiscovery.js +258 -0
- package/packages/server/src/services/slashCommandPluginDiscovery.js +216 -0
- package/packages/server/src/services/slashCommandService.js +306 -0
- package/packages/server/src/services/streamEventCallbacks.js +170 -0
- package/packages/server/src/services/streamEventHandler.js +488 -0
- package/packages/server/src/services/streamUsageHandler.js +228 -0
- package/packages/server/src/services/summaryBroadcast.js +61 -0
- package/packages/server/src/services/summaryClaudeClient.js +180 -0
- package/packages/server/src/services/summaryPrompts.js +169 -0
- package/packages/server/src/services/summaryService.js +552 -0
- package/packages/server/src/services/summaryStaleCheck.js +35 -0
- package/packages/server/src/services/systemMonitor.js +281 -0
- package/packages/server/src/services/templateTriggerService.js +197 -0
- package/packages/server/src/services/terminalOutput.js +160 -0
- package/packages/server/src/services/todoStore.js +58 -0
- package/packages/server/src/services/usageTracker.js +69 -0
- package/packages/server/src/services/withConcurrencyGuard.js +110 -0
- package/packages/server/src/websocket.js +10 -0
- package/packages/server/src/ws/WebSocketManager.js +240 -0
- package/packages/server/src/ws/index.js +50 -0
- package/packages/shared/package.json +27 -0
- package/packages/shared/src/constants.js +44 -0
- package/packages/shared/src/contracts/canvas.js +25 -0
- package/packages/shared/src/contracts/commandButtons.js +36 -0
- package/packages/shared/src/contracts/kanban.js +142 -0
- package/packages/shared/src/contracts/projects.js +63 -0
- package/packages/shared/src/contracts/providers.js +81 -0
- package/packages/shared/src/contracts/quickResponses.js +44 -0
- package/packages/shared/src/contracts/sessions.js +112 -0
- package/packages/shared/src/contracts/templates.js +51 -0
- package/packages/shared/src/index.js +5 -0
- package/packages/shared/src/protocol.js +76 -0
- package/packages/shared/src/routeParams.js +36 -0
- package/packages/shared/src/types.js +167 -0
- package/packages/shared/src/utils.js +101 -0
- package/packages/web/dist/assets/ActiveSessionsView-BQc76Jc8.js +1 -0
- package/packages/web/dist/assets/ActiveSessionsView-ofSvx-K1.css +1 -0
- package/packages/web/dist/assets/AgentLogsView-CTCjHjsu.js +2 -0
- package/packages/web/dist/assets/AgentLogsView-D90PnQVk.css +1 -0
- package/packages/web/dist/assets/ApiClient-Dbs1H78V.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-CCxSZ52u.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-CQZeuYBz.css +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-CF_-LXpU.js +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-DBm3rzhw.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-BQaQmU2d.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-DPofLvm-.js +1 -0
- package/packages/web/dist/assets/GeneralSettingsView-BCf53fpC.css +1 -0
- package/packages/web/dist/assets/GeneralSettingsView-BY1G-Kv8.js +1 -0
- package/packages/web/dist/assets/InterpolationHelp-CgdbNcJB.js +1 -0
- package/packages/web/dist/assets/InterpolationHelp-iNxTxmhs.css +1 -0
- package/packages/web/dist/assets/MarkdownEditor-CqT1U8lo.js +2 -0
- package/packages/web/dist/assets/MarkdownEditor-enuH2yvP.css +1 -0
- package/packages/web/dist/assets/ModelSelector-BBn_Ve0D.js +1 -0
- package/packages/web/dist/assets/ModelSelector-DPPD-92R.css +1 -0
- package/packages/web/dist/assets/NewSessionView-Bo5l49nu.js +3 -0
- package/packages/web/dist/assets/NewSessionView-Byoi1XdQ.css +1 -0
- package/packages/web/dist/assets/PathChooser-BoMGzeg2.css +1 -0
- package/packages/web/dist/assets/PathChooser-Cx9gQ-Qt.js +1 -0
- package/packages/web/dist/assets/ProjectEditView-BFuscj-V.js +1 -0
- package/packages/web/dist/assets/ProjectEditView-DNwBUNRk.css +1 -0
- package/packages/web/dist/assets/ProjectListView-C55H1JHQ.css +1 -0
- package/packages/web/dist/assets/ProjectListView-Dj0jBZ46.js +1 -0
- package/packages/web/dist/assets/ProjectNewView-Brdp-xUu.js +1 -0
- package/packages/web/dist/assets/ProjectNewView-CpgE4R-l.css +1 -0
- package/packages/web/dist/assets/ProvidersView-B_QQF3RM.css +1 -0
- package/packages/web/dist/assets/ProvidersView-Cxc-1skq.js +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-B2eVAtHW.js +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-B8188A1D.css +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-DIBQFj0W.css +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-lU8pW2B0.js +1 -0
- package/packages/web/dist/assets/ResizableTextarea-B5nAA0RV.css +1 -0
- package/packages/web/dist/assets/ResizableTextarea-DSy1mWGY.js +1 -0
- package/packages/web/dist/assets/SessionCard-BvjLwVYg.js +1 -0
- package/packages/web/dist/assets/SessionCard-D20G3bX8.css +1 -0
- package/packages/web/dist/assets/SessionDetailView-BQbPg-RJ.js +36 -0
- package/packages/web/dist/assets/SessionDetailView-BrMG4p2-.css +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BgqFR-5f.js +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BuLlDF-7.css +1 -0
- package/packages/web/dist/assets/SessionListView-BAIBtJF7.css +1 -0
- package/packages/web/dist/assets/SessionListView-CYIHI8qF.js +1 -0
- package/packages/web/dist/assets/SessionLogStream-B-FwUMJQ.js +18 -0
- package/packages/web/dist/assets/SessionLogStream-zPUTiGbe.css +1 -0
- package/packages/web/dist/assets/SettingsView-DC8-hTQ-.css +1 -0
- package/packages/web/dist/assets/SettingsView-fZxpiGp7.js +1 -0
- package/packages/web/dist/assets/SlashCommandWizard-BB30cSvo.css +1 -0
- package/packages/web/dist/assets/SlashCommandWizard-BgaOw9W3.js +1 -0
- package/packages/web/dist/assets/SummarySettingsView-DcsmSVJI.css +1 -0
- package/packages/web/dist/assets/SummarySettingsView-eeu1Xq86.js +1 -0
- package/packages/web/dist/assets/TemplateDetailView-DEPKSwDo.js +1 -0
- package/packages/web/dist/assets/TemplateDetailView-DT2m06W7.css +1 -0
- package/packages/web/dist/assets/apl-B4CMkyY2.js +1 -0
- package/packages/web/dist/assets/asciiarmor-Df11BRmG.js +1 -0
- package/packages/web/dist/assets/asn1-EdZsLKOL.js +1 -0
- package/packages/web/dist/assets/asterisk-B-8jnY81.js +1 -0
- package/packages/web/dist/assets/brainfuck-C4LP7Hcl.js +1 -0
- package/packages/web/dist/assets/clike-B9uivgTg.js +1 -0
- package/packages/web/dist/assets/clojure-BMjYHr_A.js +1 -0
- package/packages/web/dist/assets/cmake-BQqOBYOt.js +1 -0
- package/packages/web/dist/assets/cobol-CWcv1MsR.js +1 -0
- package/packages/web/dist/assets/coffeescript-S37ZYGWr.js +1 -0
- package/packages/web/dist/assets/commandButtons-DNSHH8IA.js +4 -0
- package/packages/web/dist/assets/commonlisp-DBKNyK5s.js +1 -0
- package/packages/web/dist/assets/crystal-SjHAIU92.js +1 -0
- package/packages/web/dist/assets/css-BnMrqG3P.js +1 -0
- package/packages/web/dist/assets/cypher-C_CwsFkJ.js +1 -0
- package/packages/web/dist/assets/d-pRatUO7H.js +1 -0
- package/packages/web/dist/assets/diff-DbItnlRl.js +1 -0
- package/packages/web/dist/assets/dockerfile-BKs6k2Af.js +1 -0
- package/packages/web/dist/assets/dtd-DF_7sFjM.js +1 -0
- package/packages/web/dist/assets/dylan-DwRh75JA.js +1 -0
- package/packages/web/dist/assets/ebnf-CDyGwa7X.js +1 -0
- package/packages/web/dist/assets/ecl-Cabwm37j.js +1 -0
- package/packages/web/dist/assets/eiffel-CnydiIhH.js +1 -0
- package/packages/web/dist/assets/elm-vLlmbW-K.js +1 -0
- package/packages/web/dist/assets/erlang-BNw1qcRV.js +1 -0
- package/packages/web/dist/assets/factor-kuTfRLto.js +1 -0
- package/packages/web/dist/assets/fcl-Kvtd6kyn.js +1 -0
- package/packages/web/dist/assets/forth-Ffai-XNe.js +1 -0
- package/packages/web/dist/assets/fortran-DYz_wnZ1.js +1 -0
- package/packages/web/dist/assets/gas-Bneqetm1.js +1 -0
- package/packages/web/dist/assets/gherkin-heZmZLOM.js +1 -0
- package/packages/web/dist/assets/groovy-D9Dt4D0W.js +1 -0
- package/packages/web/dist/assets/haskell-BWDZoCOh.js +1 -0
- package/packages/web/dist/assets/haxe-H-WmDvRZ.js +1 -0
- package/packages/web/dist/assets/http-DBlCnlav.js +1 -0
- package/packages/web/dist/assets/idl-BEugSyMb.js +1 -0
- package/packages/web/dist/assets/index-BZlHgDSz.js +1 -0
- package/packages/web/dist/assets/index-BhWX8AfE.js +2 -0
- package/packages/web/dist/assets/index-Bi3XvF_f.js +1 -0
- package/packages/web/dist/assets/index-BqXoPf_D.js +1 -0
- package/packages/web/dist/assets/index-CAuTOZSD.js +1 -0
- package/packages/web/dist/assets/index-CKYk-fkb.js +1 -0
- package/packages/web/dist/assets/index-CTumW_tV.js +318 -0
- package/packages/web/dist/assets/index-CVOJVSsC.js +82 -0
- package/packages/web/dist/assets/index-CXK2Z3_z.js +1 -0
- package/packages/web/dist/assets/index-CYllQ3Vd.js +1 -0
- package/packages/web/dist/assets/index-CpsfI08O.js +1 -0
- package/packages/web/dist/assets/index-DQkhDeTA.js +3 -0
- package/packages/web/dist/assets/index-DWP8iCBp.js +1 -0
- package/packages/web/dist/assets/index-DkVb9W_J.js +1 -0
- package/packages/web/dist/assets/index-DmKHPbIa.js +1 -0
- package/packages/web/dist/assets/index-DrlQi03X.js +1 -0
- package/packages/web/dist/assets/index-gmCCsCQ1.css +1 -0
- package/packages/web/dist/assets/index-prTEzzgO.js +1 -0
- package/packages/web/dist/assets/index-wqgejMCM.js +1 -0
- package/packages/web/dist/assets/index-yh0ZHIWw.js +7 -0
- package/packages/web/dist/assets/javascript-qCveANmP.js +1 -0
- package/packages/web/dist/assets/julia-DuME0IfC.js +1 -0
- package/packages/web/dist/assets/livescript-BwQOo05w.js +1 -0
- package/packages/web/dist/assets/lua-BgMRiT3U.js +1 -0
- package/packages/web/dist/assets/mathematica-DTrFuWx2.js +1 -0
- package/packages/web/dist/assets/mbox-CNhZ1qSd.js +1 -0
- package/packages/web/dist/assets/mirc-CjQqDB4T.js +1 -0
- package/packages/web/dist/assets/mllike-CXdrOF99.js +1 -0
- package/packages/web/dist/assets/modelica-Dc1JOy9r.js +1 -0
- package/packages/web/dist/assets/mscgen-BA5vi2Kp.js +1 -0
- package/packages/web/dist/assets/mumps-BT43cFF4.js +1 -0
- package/packages/web/dist/assets/nginx-DdIZxoE0.js +1 -0
- package/packages/web/dist/assets/nsis-LdVXkNf5.js +1 -0
- package/packages/web/dist/assets/ntriples-BfvgReVJ.js +1 -0
- package/packages/web/dist/assets/octave-Ck1zUtKM.js +1 -0
- package/packages/web/dist/assets/oz-BzwKVEFT.js +1 -0
- package/packages/web/dist/assets/pascal--L3eBynH.js +1 -0
- package/packages/web/dist/assets/perl-CdXCOZ3F.js +1 -0
- package/packages/web/dist/assets/pig-CevX1Tat.js +1 -0
- package/packages/web/dist/assets/powershell-CFHJl5sT.js +1 -0
- package/packages/web/dist/assets/projects-DbBQQH-V.js +1 -0
- package/packages/web/dist/assets/properties-C78fOPTZ.js +1 -0
- package/packages/web/dist/assets/protobuf-ChK-085T.js +1 -0
- package/packages/web/dist/assets/providers-ceCc4xRU.js +1 -0
- package/packages/web/dist/assets/pug-DukmZTjD.js +1 -0
- package/packages/web/dist/assets/puppet-DMA9R1ak.js +1 -0
- package/packages/web/dist/assets/python-BuPzkPfP.js +1 -0
- package/packages/web/dist/assets/q-pXgVlZs6.js +1 -0
- package/packages/web/dist/assets/r-DUYO_cvP.js +1 -0
- package/packages/web/dist/assets/rpm-CTu-6PCP.js +1 -0
- package/packages/web/dist/assets/ruby-B2Rjki9n.js +1 -0
- package/packages/web/dist/assets/sas-B4kiWyti.js +1 -0
- package/packages/web/dist/assets/scheme-C41bIUwD.js +1 -0
- package/packages/web/dist/assets/sessions-D681M81k.js +1 -0
- package/packages/web/dist/assets/settings-D0evez2V.js +1 -0
- package/packages/web/dist/assets/shell-CjFT_Tl9.js +1 -0
- package/packages/web/dist/assets/sieve-C3Gn_uJK.js +1 -0
- package/packages/web/dist/assets/simple-mode-GW_nhZxv.js +1 -0
- package/packages/web/dist/assets/smalltalk-CnHTOXQT.js +1 -0
- package/packages/web/dist/assets/solr-DehyRSwq.js +1 -0
- package/packages/web/dist/assets/sparql-DkYu6x3z.js +1 -0
- package/packages/web/dist/assets/spreadsheet-BCZA_wO0.js +1 -0
- package/packages/web/dist/assets/sql-D0XecflT.js +1 -0
- package/packages/web/dist/assets/stex-C3f8Ysf7.js +1 -0
- package/packages/web/dist/assets/style-BTin-zR_.css +1 -0
- package/packages/web/dist/assets/stylus-B533Al4x.js +1 -0
- package/packages/web/dist/assets/swift-BzpIVaGY.js +1 -0
- package/packages/web/dist/assets/tcl-DVfN8rqt.js +1 -0
- package/packages/web/dist/assets/textile-CnDTJFAw.js +1 -0
- package/packages/web/dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
- package/packages/web/dist/assets/tiki-DGYXhP31.js +1 -0
- package/packages/web/dist/assets/toml-Bm5Em-hy.js +1 -0
- package/packages/web/dist/assets/troff-wAsdV37c.js +1 -0
- package/packages/web/dist/assets/ttcn-CfJYG6tj.js +1 -0
- package/packages/web/dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
- package/packages/web/dist/assets/turtle-B1tBg_DP.js +1 -0
- package/packages/web/dist/assets/vb-CmGdzxic.js +1 -0
- package/packages/web/dist/assets/vbscript-BuJXcnF6.js +1 -0
- package/packages/web/dist/assets/velocity-D8B20fx6.js +1 -0
- package/packages/web/dist/assets/verilog-C6RDOZhf.js +1 -0
- package/packages/web/dist/assets/vhdl-lSbBsy5d.js +1 -0
- package/packages/web/dist/assets/webidl-ZXfAyPTL.js +1 -0
- package/packages/web/dist/assets/xquery-CQfU5ijd.js +1 -0
- package/packages/web/dist/assets/yacas-BJ4BC0dw.js +1 -0
- package/packages/web/dist/assets/z80-Hz9HOZM7.js +1 -0
- package/packages/web/dist/favicon.png +0 -0
- package/packages/web/dist/index.html +17 -0
- 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,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
|
+
}
|