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
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { sessions, conversations } from '../database.js';
|
|
2
|
+
import { broadcastToSession, broadcastToProject } from '../websocket.js';
|
|
3
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
4
|
+
import { updateTurnUsage, currentTurnUsage, estimatedOutputTokens, estimateTokens } from './usageTracker.js';
|
|
5
|
+
import { activeConversationIds, currentModels } from './streamEventHandler.js';
|
|
6
|
+
|
|
7
|
+
// ── Stream usage helpers ────────────────────────────────────────────────────
|
|
8
|
+
// Extracted from streamEventHandler.js to keep it under the max-lines limit.
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Handle stream_event > message_start — initial usage (input tokens)
|
|
12
|
+
* @param {string} sessionId
|
|
13
|
+
* @param {Object} event
|
|
14
|
+
*/
|
|
15
|
+
export function handleMessageStart(sessionId, event) {
|
|
16
|
+
const usage = event.event?.message?.usage;
|
|
17
|
+
if (usage) {
|
|
18
|
+
const conversationId = activeConversationIds.get(sessionId);
|
|
19
|
+
const turnUsage = updateTurnUsage(conversationId, usage, 'message_start');
|
|
20
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_USAGE_UPDATE, {
|
|
21
|
+
sessionId,
|
|
22
|
+
conversationId,
|
|
23
|
+
usage: turnUsage,
|
|
24
|
+
isFinal: false,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handle stream_event > message_delta — streaming output tokens
|
|
31
|
+
* @param {string} sessionId
|
|
32
|
+
* @param {Object} event
|
|
33
|
+
*/
|
|
34
|
+
export function handleMessageDelta(sessionId, event) {
|
|
35
|
+
const usage = event.event?.usage;
|
|
36
|
+
if (usage) {
|
|
37
|
+
const conversationId = activeConversationIds.get(sessionId);
|
|
38
|
+
const turnUsage = updateTurnUsage(conversationId, usage, 'message_delta');
|
|
39
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_USAGE_UPDATE, {
|
|
40
|
+
sessionId,
|
|
41
|
+
conversationId,
|
|
42
|
+
usage: turnUsage,
|
|
43
|
+
isFinal: false,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handle text_delta within content_block_delta — accumulate text and estimate tokens
|
|
50
|
+
* @param {string} sessionId
|
|
51
|
+
* @param {Object} delta
|
|
52
|
+
* @param {Map} textAccumulators
|
|
53
|
+
*/
|
|
54
|
+
export function handleTextDelta(sessionId, delta, textAccumulators) {
|
|
55
|
+
// Accumulate text content
|
|
56
|
+
const current = textAccumulators.get(sessionId) || '';
|
|
57
|
+
const accumulated = current + delta.text;
|
|
58
|
+
textAccumulators.set(sessionId, accumulated);
|
|
59
|
+
|
|
60
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_PARTIAL, {
|
|
61
|
+
sessionId,
|
|
62
|
+
text: accumulated,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Estimate tokens from streamed content for real-time output token updates
|
|
66
|
+
const conversationId = activeConversationIds.get(sessionId);
|
|
67
|
+
if (!conversationId) return;
|
|
68
|
+
|
|
69
|
+
const currentEstimate = estimatedOutputTokens.get(conversationId) || 0;
|
|
70
|
+
const newEstimate = currentEstimate + estimateTokens(delta.text);
|
|
71
|
+
estimatedOutputTokens.set(conversationId, newEstimate);
|
|
72
|
+
|
|
73
|
+
// Get current turn usage and add estimated output
|
|
74
|
+
const turnData = currentTurnUsage.get(conversationId) || {
|
|
75
|
+
inputTokens: 0,
|
|
76
|
+
outputTokens: 0,
|
|
77
|
+
lastMessageOutput: 0,
|
|
78
|
+
cacheReadInputTokens: 0,
|
|
79
|
+
cacheCreationInputTokens: 0,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Broadcast usage update with estimated tokens
|
|
83
|
+
const broadcastUsage = {
|
|
84
|
+
inputTokens: turnData.inputTokens,
|
|
85
|
+
outputTokens: turnData.outputTokens + Math.max(turnData.lastMessageOutput, newEstimate),
|
|
86
|
+
cacheReadInputTokens: turnData.cacheReadInputTokens,
|
|
87
|
+
cacheCreationInputTokens: turnData.cacheCreationInputTokens,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_USAGE_UPDATE, {
|
|
91
|
+
sessionId,
|
|
92
|
+
conversationId,
|
|
93
|
+
usage: broadcastUsage,
|
|
94
|
+
isFinal: false,
|
|
95
|
+
isEstimate: true, // Flag so UI can show "~" prefix if desired
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Resolve a token field from modelUsageEntry (camelCase) or event.usage (snake_case).
|
|
101
|
+
*/
|
|
102
|
+
function resolveTokenField(modelUsageEntry, eventUsage, keys) {
|
|
103
|
+
return modelUsageEntry?.[keys.camel] || eventUsage?.[keys.snake] || 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Extract turn usage from result event's modelUsage or usage fields
|
|
108
|
+
* @param {string} sessionId
|
|
109
|
+
* @param {Object} event
|
|
110
|
+
* @returns {Object} turnUsage
|
|
111
|
+
*/
|
|
112
|
+
export function extractTurnUsage(sessionId, event) {
|
|
113
|
+
// Extract from modelUsage if available (has more detail)
|
|
114
|
+
const modelUsageEntry = event.modelUsage
|
|
115
|
+
? Object.values(event.modelUsage)[0]
|
|
116
|
+
: null;
|
|
117
|
+
|
|
118
|
+
// Use the model from system.init (stored in currentModels) rather than modelUsage keys
|
|
119
|
+
// because modelUsage can contain multiple models when sub-agents are used (e.g., Opus using Haiku)
|
|
120
|
+
// and Object.keys()[0] would pick the wrong model
|
|
121
|
+
const primaryModel = currentModels.get(sessionId) || Object.keys(event.modelUsage || {})[0] || null;
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
inputTokens: resolveTokenField(modelUsageEntry, event.usage, { camel: 'inputTokens', snake: 'input_tokens' }),
|
|
125
|
+
outputTokens: resolveTokenField(modelUsageEntry, event.usage, { camel: 'outputTokens', snake: 'output_tokens' }),
|
|
126
|
+
cacheReadInputTokens: resolveTokenField(modelUsageEntry, event.usage, { camel: 'cacheReadInputTokens', snake: 'cache_read_input_tokens' }),
|
|
127
|
+
cacheCreationInputTokens: resolveTokenField(modelUsageEntry, event.usage, { camel: 'cacheCreationInputTokens', snake: 'cache_creation_input_tokens' }),
|
|
128
|
+
webSearchRequests: modelUsageEntry?.webSearchRequests || 0,
|
|
129
|
+
contextWindow: modelUsageEntry?.contextWindow || 200000,
|
|
130
|
+
model: primaryModel,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Build cumulative session-level usage by adding turn usage to existing session usage
|
|
136
|
+
* @param {string} sessionId
|
|
137
|
+
* @param {Object} turnUsage
|
|
138
|
+
* @returns {Object} cumulativeSessionUsage
|
|
139
|
+
*/
|
|
140
|
+
export function buildCumulativeSessionUsage(sessionId, turnUsage) {
|
|
141
|
+
const currentSession = sessions.getById(sessionId);
|
|
142
|
+
return {
|
|
143
|
+
inputTokens: (currentSession.inputTokens || 0) + turnUsage.inputTokens,
|
|
144
|
+
outputTokens: (currentSession.outputTokens || 0) + turnUsage.outputTokens,
|
|
145
|
+
cacheReadInputTokens: (currentSession.cacheReadInputTokens || 0) + turnUsage.cacheReadInputTokens,
|
|
146
|
+
cacheCreationInputTokens: (currentSession.cacheCreationInputTokens || 0) + turnUsage.cacheCreationInputTokens,
|
|
147
|
+
webSearchRequests: (currentSession.webSearchRequests || 0) + turnUsage.webSearchRequests,
|
|
148
|
+
contextWindow: turnUsage.contextWindow,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Update conversation with cumulative usage from turn
|
|
154
|
+
* @param {string|undefined} conversationId
|
|
155
|
+
* @param {Object|null} currentConversation
|
|
156
|
+
* @param {Object} turnUsage
|
|
157
|
+
* @returns {Object|null} updatedConversation
|
|
158
|
+
*/
|
|
159
|
+
export function updateConversationUsage(conversationId, currentConversation, turnUsage) {
|
|
160
|
+
if (!currentConversation) return null;
|
|
161
|
+
|
|
162
|
+
const cumulativeConversationUsage = {
|
|
163
|
+
inputTokens: (currentConversation.inputTokens || 0) + turnUsage.inputTokens,
|
|
164
|
+
outputTokens: (currentConversation.outputTokens || 0) + turnUsage.outputTokens,
|
|
165
|
+
cacheReadInputTokens: (currentConversation.cacheReadInputTokens || 0) + turnUsage.cacheReadInputTokens,
|
|
166
|
+
cacheCreationInputTokens: (currentConversation.cacheCreationInputTokens || 0) + turnUsage.cacheCreationInputTokens,
|
|
167
|
+
webSearchRequests: (currentConversation.webSearchRequests || 0) + turnUsage.webSearchRequests,
|
|
168
|
+
contextWindow: turnUsage.contextWindow,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return conversations.updateUsage(conversationId, cumulativeConversationUsage);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Handle final usage stats from result event — update conversation and session usage
|
|
176
|
+
* @param {string} sessionId
|
|
177
|
+
* @param {Object} event
|
|
178
|
+
*/
|
|
179
|
+
export function handleResultUsage(sessionId, event) {
|
|
180
|
+
const turnUsage = extractTurnUsage(sessionId, event);
|
|
181
|
+
|
|
182
|
+
// Get the conversation ID for this session's current turn
|
|
183
|
+
const conversationId = activeConversationIds.get(sessionId);
|
|
184
|
+
const currentConversation = conversationId ? conversations.getById(conversationId) : null;
|
|
185
|
+
|
|
186
|
+
// Update conversation with cumulative usage (add to existing)
|
|
187
|
+
const updatedConversation = updateConversationUsage(conversationId, currentConversation, turnUsage);
|
|
188
|
+
|
|
189
|
+
// Also update session-level usage (aggregate of all conversations) for backward compatibility
|
|
190
|
+
const cumulativeSessionUsage = buildCumulativeSessionUsage(sessionId, turnUsage);
|
|
191
|
+
const updatedSession = sessions.updateUsage(sessionId, cumulativeSessionUsage);
|
|
192
|
+
|
|
193
|
+
// Broadcast final usage update with conversationId
|
|
194
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_USAGE_UPDATE, {
|
|
195
|
+
sessionId,
|
|
196
|
+
conversationId,
|
|
197
|
+
usage: updatedConversation ? {
|
|
198
|
+
inputTokens: updatedConversation.inputTokens,
|
|
199
|
+
outputTokens: updatedConversation.outputTokens,
|
|
200
|
+
cacheReadInputTokens: updatedConversation.cacheReadInputTokens,
|
|
201
|
+
cacheCreationInputTokens: updatedConversation.cacheCreationInputTokens,
|
|
202
|
+
webSearchRequests: updatedConversation.webSearchRequests,
|
|
203
|
+
contextWindow: updatedConversation.contextWindow,
|
|
204
|
+
} : cumulativeSessionUsage,
|
|
205
|
+
turnUsage,
|
|
206
|
+
isFinal: true,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Also broadcast session update for session list
|
|
210
|
+
broadcastToProject(updatedSession.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
|
|
211
|
+
projectId: updatedSession.projectId,
|
|
212
|
+
sessionId,
|
|
213
|
+
session: updatedSession,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Broadcast conversation update for real-time UI updates
|
|
217
|
+
if (updatedConversation) {
|
|
218
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.CONVERSATION_UPDATED, {
|
|
219
|
+
sessionId,
|
|
220
|
+
conversation: updatedConversation,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Clean up turn usage and estimated tokens
|
|
225
|
+
currentTurnUsage.delete(conversationId);
|
|
226
|
+
estimatedOutputTokens.delete(conversationId);
|
|
227
|
+
activeConversationIds.delete(sessionId);
|
|
228
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Broadcast helpers for summary-related WebSocket events.
|
|
3
|
+
* Consolidates repeated broadcast patterns from summaryService.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { broadcastToSession, broadcastToProject } from '../websocket.js';
|
|
7
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Broadcast summary update to session and project subscribers
|
|
11
|
+
* @param {string} sessionId - The session ID
|
|
12
|
+
* @param {string|null} projectId - The project ID (null skips project broadcast)
|
|
13
|
+
* @param {Object} summary - The summary object
|
|
14
|
+
*/
|
|
15
|
+
export function broadcastSummaryUpdate(sessionId, projectId, summary) {
|
|
16
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_SUMMARY_UPDATED, {
|
|
17
|
+
sessionId,
|
|
18
|
+
summary,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (projectId) {
|
|
22
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.SESSION_SUMMARY_UPDATED, {
|
|
23
|
+
projectId,
|
|
24
|
+
sessionId,
|
|
25
|
+
summary,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Broadcast generating status to session subscribers
|
|
32
|
+
* @param {string} sessionId - The session ID
|
|
33
|
+
* @param {boolean} generating - Whether generation is in progress
|
|
34
|
+
*/
|
|
35
|
+
export function broadcastGeneratingStatus(sessionId, generating) {
|
|
36
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_SUMMARY_GENERATING, {
|
|
37
|
+
sessionId,
|
|
38
|
+
generating,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Broadcast session update to session and project subscribers
|
|
44
|
+
* @param {string} sessionId - The session ID
|
|
45
|
+
* @param {string|null} projectId - The project ID (null skips project broadcast)
|
|
46
|
+
* @param {Object} session - The session object
|
|
47
|
+
*/
|
|
48
|
+
export function broadcastSessionUpdate(sessionId, projectId, session) {
|
|
49
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
|
|
50
|
+
sessionId,
|
|
51
|
+
session,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (projectId) {
|
|
55
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
|
|
56
|
+
projectId,
|
|
57
|
+
sessionId,
|
|
58
|
+
session,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
import { sessions } from '../database.js';
|
|
3
|
+
import { agentCallLogger } from './agentCallLogger.js';
|
|
4
|
+
import { createVCRQueryFn } from '../agents/vcr/VCRSummaryWrapper.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default JSON schema for session summary structured output
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Process a content block from Claude's response
|
|
11
|
+
* @param {Object} block - Content block
|
|
12
|
+
* @param {Object} stateInput - Mutable state object { responseText, structuredOutput }
|
|
13
|
+
*/
|
|
14
|
+
function processContentBlock(block, stateInput) {
|
|
15
|
+
const state = stateInput;
|
|
16
|
+
if (block.type === 'tool_use' && block.name === 'StructuredOutput') {
|
|
17
|
+
state.structuredOutput = block.input;
|
|
18
|
+
} else if (block.type === 'text') {
|
|
19
|
+
state.responseText += block.text;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Log usage metrics from a result event
|
|
25
|
+
* @param {string} callId - The call ID for logging
|
|
26
|
+
* @param {Object} event - The result event
|
|
27
|
+
*/
|
|
28
|
+
function logResultUsage(callId, event) {
|
|
29
|
+
const modelUsageEntry = event.modelUsage
|
|
30
|
+
? Object.values(event.modelUsage)[0]
|
|
31
|
+
: null;
|
|
32
|
+
if (!modelUsageEntry && !event.usage) return;
|
|
33
|
+
|
|
34
|
+
agentCallLogger.updateUsage(callId, {
|
|
35
|
+
inputTokens: modelUsageEntry?.inputTokens || event.usage?.input_tokens || 0,
|
|
36
|
+
outputTokens: modelUsageEntry?.outputTokens || event.usage?.output_tokens || 0,
|
|
37
|
+
thinkingTokens: 0,
|
|
38
|
+
cacheReadInputTokens:
|
|
39
|
+
modelUsageEntry?.cacheReadInputTokens ||
|
|
40
|
+
event.usage?.cache_read_input_tokens ||
|
|
41
|
+
0,
|
|
42
|
+
cacheCreationInputTokens:
|
|
43
|
+
modelUsageEntry?.cacheCreationInputTokens ||
|
|
44
|
+
event.usage?.cache_creation_input_tokens ||
|
|
45
|
+
0,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Build the query parameters for the Claude SDK call.
|
|
51
|
+
* @param {string} prompt - The prompt to send
|
|
52
|
+
* @param {{ systemPrompt?: string, jsonSchema?: Object }} options
|
|
53
|
+
* @returns {Object} queryParams ready for the SDK query function
|
|
54
|
+
*/
|
|
55
|
+
function buildClaudeRequest(prompt, options) {
|
|
56
|
+
const { systemPrompt = null, jsonSchema = null } = options || {};
|
|
57
|
+
const schema = jsonSchema || SESSION_SUMMARY_SCHEMA;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
prompt,
|
|
61
|
+
options: {
|
|
62
|
+
cwd: process.cwd(),
|
|
63
|
+
permissionMode: 'bypassPermissions',
|
|
64
|
+
maxTurns: 1,
|
|
65
|
+
model: 'claude-haiku-4-5-20251001',
|
|
66
|
+
...(systemPrompt && { systemPrompt }),
|
|
67
|
+
outputFormat: {
|
|
68
|
+
type: 'json_schema',
|
|
69
|
+
schema,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Process the Claude SDK event stream and extract the response.
|
|
77
|
+
* @param {AsyncIterable} eventStream - The async iterable from the SDK query
|
|
78
|
+
* @param {string|null} callId - The agent call logger ID (null if not logging)
|
|
79
|
+
* @returns {Promise<string>} The text response (JSON string)
|
|
80
|
+
*/
|
|
81
|
+
async function handleClaudeResponse(eventStream, callId) {
|
|
82
|
+
const state = { responseText: '', structuredOutput: null };
|
|
83
|
+
|
|
84
|
+
for await (const event of eventStream) {
|
|
85
|
+
switch (event.type) {
|
|
86
|
+
case 'assistant': {
|
|
87
|
+
const content = event.message?.content || [];
|
|
88
|
+
for (const block of content) {
|
|
89
|
+
processContentBlock(block, state);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case 'result': {
|
|
94
|
+
if (event.subtype === 'error') {
|
|
95
|
+
throw new Error(event.error || 'Claude SDK query failed');
|
|
96
|
+
}
|
|
97
|
+
if (callId) {
|
|
98
|
+
logResultUsage(callId, event);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (state.structuredOutput) {
|
|
106
|
+
return JSON.stringify(state.structuredOutput);
|
|
107
|
+
}
|
|
108
|
+
return state.responseText;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const SESSION_SUMMARY_SCHEMA = {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
short_summary: { type: 'string', description: '1-2 sentence preview for list view (max 150 characters)' },
|
|
115
|
+
full_summary: { type: 'string', description: 'Detailed summary with key accomplishments and current state (max 500 characters)' },
|
|
116
|
+
key_actions: { type: 'array', items: { type: 'string' }, description: 'List of key actions taken' },
|
|
117
|
+
files_modified: { type: 'array', items: { type: 'string' }, description: 'List of files that were modified' },
|
|
118
|
+
outcome: { type: 'string', enum: ['completed', 'partial', 'failed', 'ongoing'], description: 'Session outcome status' },
|
|
119
|
+
pr_url: { type: ['string', 'null'], description: 'GitHub PR URL if one was created' },
|
|
120
|
+
session_title: { type: ['string', 'null'], description: 'Concise title for this session (max 60 characters)' },
|
|
121
|
+
},
|
|
122
|
+
required: ['short_summary', 'full_summary', 'key_actions', 'files_modified', 'outcome'],
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Call Claude via SDK and extract text response.
|
|
127
|
+
* Unified function that handles both default session summary schema
|
|
128
|
+
* and custom schemas (e.g., combined session+conversation summary).
|
|
129
|
+
*
|
|
130
|
+
* @param {string} prompt - The prompt to send
|
|
131
|
+
* @param {Array} recentMessages - Messages (for mock mode context)
|
|
132
|
+
* @param {string} sessionStatus - Session status (for mock mode context)
|
|
133
|
+
* @param {{ logMeta?: Object, systemPrompt?: string, jsonSchema?: Object }} options - Optional parameters
|
|
134
|
+
* @returns {Promise<string>} The text response (JSON string)
|
|
135
|
+
*/
|
|
136
|
+
export async function callClaude(prompt, recentMessages, sessionStatus, options = {}) {
|
|
137
|
+
const { logMeta = null } = options || {};
|
|
138
|
+
// Build stable key for VCR cassette (session prompts are hardcoded strings in E2E tests)
|
|
139
|
+
let keyHint = null;
|
|
140
|
+
if (process.env.VCR_MODE && logMeta?.sessionId) {
|
|
141
|
+
const session = sessions.getById(logMeta.sessionId);
|
|
142
|
+
keyHint = session ? `${logMeta.callType}:${session.prompt}` : null;
|
|
143
|
+
}
|
|
144
|
+
// Use VCR wrapper if in VCR mode, otherwise use real SDK query
|
|
145
|
+
const queryFn = process.env.VCR_MODE
|
|
146
|
+
? createVCRQueryFn(query, 'tests/e2e/cassettes/summaries', keyHint)
|
|
147
|
+
: query;
|
|
148
|
+
|
|
149
|
+
const queryParams = buildClaudeRequest(prompt, options);
|
|
150
|
+
|
|
151
|
+
// Start logging if metadata provided
|
|
152
|
+
let callId = null;
|
|
153
|
+
if (logMeta) {
|
|
154
|
+
callId = agentCallLogger.startCall({
|
|
155
|
+
sessionId: logMeta.sessionId,
|
|
156
|
+
conversationId: logMeta.conversationId || null,
|
|
157
|
+
agentType: 'summary',
|
|
158
|
+
model: 'claude-haiku-4-5-20251001',
|
|
159
|
+
callType: logMeta.callType,
|
|
160
|
+
promptLength: prompt.length,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const result = await handleClaudeResponse(queryFn(queryParams), callId);
|
|
166
|
+
|
|
167
|
+
// Complete the logged call on success
|
|
168
|
+
if (callId) {
|
|
169
|
+
agentCallLogger.completeCall(callId, { success: true });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return result;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// Complete the logged call on error
|
|
175
|
+
if (callId) {
|
|
176
|
+
agentCallLogger.completeCall(callId, { success: false, error });
|
|
177
|
+
}
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt templates and message formatting for summary generation.
|
|
3
|
+
* Pure functions with no side effects.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DEFAULT_SESSION_TITLE_PROMPT } from '../../../shared/src/index.js';
|
|
7
|
+
|
|
8
|
+
// Maximum retry attempts for failed parsing
|
|
9
|
+
export const MAX_RETRIES = 2;
|
|
10
|
+
|
|
11
|
+
// Minimum number of messages before generating a summary (skip trivial sessions)
|
|
12
|
+
export const MIN_MESSAGES_FOR_SUMMARY = 3;
|
|
13
|
+
|
|
14
|
+
// Maximum number of recent messages to include in generation (optimized for token efficiency)
|
|
15
|
+
export const MAX_MESSAGES = 10;
|
|
16
|
+
|
|
17
|
+
// Re-export from shared for backward compatibility
|
|
18
|
+
export { DEFAULT_SESSION_TITLE_PROMPT };
|
|
19
|
+
|
|
20
|
+
// System prompt for summary generation (static instructions that benefit from prompt caching)
|
|
21
|
+
export const SUMMARY_SYSTEM_PROMPT = `You are updating a session summary for a Claude Code session.
|
|
22
|
+
|
|
23
|
+
Generate an updated summary that:
|
|
24
|
+
1. Preserves important context from the existing summary
|
|
25
|
+
2. Incorporates new actions and progress from recent messages
|
|
26
|
+
3. Updates the outcome status if changed
|
|
27
|
+
4. Maintains a coherent narrative of the full session
|
|
28
|
+
|
|
29
|
+
Outcome guidelines:
|
|
30
|
+
- "completed": Task was fully accomplished
|
|
31
|
+
- "partial": Some progress made but task incomplete
|
|
32
|
+
- "failed": Task encountered errors and couldn't proceed
|
|
33
|
+
- "ongoing": Session is still active/waiting for user input`;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Format messages for the prompt
|
|
37
|
+
* @param {Array} messageList - List of messages
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
export function formatMessages(messageList) {
|
|
41
|
+
return messageList
|
|
42
|
+
.map((msg) => {
|
|
43
|
+
const role = msg.role === 'user' ? 'User' : 'Assistant';
|
|
44
|
+
let content = msg.content;
|
|
45
|
+
|
|
46
|
+
// Truncate very long messages (optimized for token efficiency)
|
|
47
|
+
if (content.length > 500) {
|
|
48
|
+
content = `${content.substring(0, 500) }... [truncated]`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Add tool use info if present
|
|
52
|
+
if (msg.toolUse && msg.toolUse.length > 0) {
|
|
53
|
+
const tools = msg.toolUse.map((t) => t.name).join(', ');
|
|
54
|
+
content += `\n[Tools used: ${tools}]`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `${role}: ${content}`;
|
|
58
|
+
})
|
|
59
|
+
.join('\n\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build the prompt for incremental summary generation
|
|
64
|
+
* @param {Object|null} existingSummary - Existing summary if any
|
|
65
|
+
* @param {Array} recentMessages - Recent messages to summarize
|
|
66
|
+
* @param {string} sessionStatus - Current session status
|
|
67
|
+
* @param {{ projectTitlePrompt?: string|null, childContext?: string }} options - Optional parameters
|
|
68
|
+
* @returns {string}
|
|
69
|
+
*/
|
|
70
|
+
export function buildIncrementalPrompt(existingSummary, recentMessages, sessionStatus, options = {}) {
|
|
71
|
+
const { projectTitlePrompt = null, childContext = '' } = options || {};
|
|
72
|
+
const existingContext = existingSummary
|
|
73
|
+
? `EXISTING SUMMARY:
|
|
74
|
+
${existingSummary.fullSummary}
|
|
75
|
+
|
|
76
|
+
Key actions so far: ${JSON.stringify(existingSummary.keyActions || [])}
|
|
77
|
+
Files modified: ${JSON.stringify(existingSummary.filesModified || [])}
|
|
78
|
+
Previous outcome: ${existingSummary.outcome}
|
|
79
|
+
Previous title: ${existingSummary.sessionTitle || 'Not set'}`
|
|
80
|
+
: 'EXISTING SUMMARY:\nNo previous summary - this is the first generation.';
|
|
81
|
+
|
|
82
|
+
const formattedMessages = formatMessages(recentMessages);
|
|
83
|
+
|
|
84
|
+
// Use custom prompt if provided, otherwise use default
|
|
85
|
+
const sessionTitlePrompt = projectTitlePrompt || DEFAULT_SESSION_TITLE_PROMPT;
|
|
86
|
+
|
|
87
|
+
// Return only dynamic content - static instructions are in SUMMARY_SYSTEM_PROMPT
|
|
88
|
+
return `Current session status: ${sessionStatus}
|
|
89
|
+
|
|
90
|
+
${existingContext}
|
|
91
|
+
${childContext}
|
|
92
|
+
RECENT CONVERSATION:
|
|
93
|
+
${formattedMessages}
|
|
94
|
+
|
|
95
|
+
Session title guidelines:
|
|
96
|
+
${sessionTitlePrompt}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Strip markdown code block wrapping (```json ... ```) from response text
|
|
101
|
+
* @param {string} text - Raw response text
|
|
102
|
+
* @returns {string} Text with code block wrapper removed if present
|
|
103
|
+
*/
|
|
104
|
+
export function stripMarkdownCodeBlock(text) {
|
|
105
|
+
let cleaned = text.trim();
|
|
106
|
+
if (cleaned.startsWith('```')) {
|
|
107
|
+
const codeBlockMatch = cleaned.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```$/);
|
|
108
|
+
if (codeBlockMatch) {
|
|
109
|
+
cleaned = codeBlockMatch[1].trim();
|
|
110
|
+
console.log('[SummaryPrompts] Stripped markdown code block from response');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return cleaned;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Add message count and last message ID to summary data for staleness tracking
|
|
118
|
+
* @param {Object} summaryDataInput - Summary data to augment
|
|
119
|
+
* @param {Array} allMessages - All messages in session
|
|
120
|
+
*/
|
|
121
|
+
export function trackMessageMetadata(summaryDataInput, allMessages) {
|
|
122
|
+
const summaryData = summaryDataInput;
|
|
123
|
+
summaryData.messageCount = allMessages.length;
|
|
124
|
+
const lastMessage = allMessages.length > 0 ? allMessages[allMessages.length - 1] : null;
|
|
125
|
+
summaryData.lastSummarizedMessageId = lastMessage ? lastMessage.id : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Parse the Claude API response into a summary object
|
|
130
|
+
* Handles markdown code block wrapping (```json ... ```) that Claude sometimes returns
|
|
131
|
+
* @param {string} responseText
|
|
132
|
+
* @returns {Object}
|
|
133
|
+
*/
|
|
134
|
+
export function parseSummaryResponse(responseText) {
|
|
135
|
+
const textToParse = stripMarkdownCodeBlock(responseText);
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(textToParse);
|
|
139
|
+
return {
|
|
140
|
+
shortSummary: parsed.short_summary || 'Summary generation failed',
|
|
141
|
+
fullSummary: parsed.full_summary || 'Unable to generate summary',
|
|
142
|
+
keyActions: Array.isArray(parsed.key_actions) ? parsed.key_actions :
|
|
143
|
+
(typeof parsed.key_actions === 'string' ? [parsed.key_actions] : []),
|
|
144
|
+
filesModified: Array.isArray(parsed.files_modified) ? parsed.files_modified :
|
|
145
|
+
(typeof parsed.files_modified === 'string' ? [parsed.files_modified] : []),
|
|
146
|
+
outcome: parsed.outcome || 'ongoing',
|
|
147
|
+
prUrl: parsed.pr_url || null,
|
|
148
|
+
sessionTitle: parsed.session_title || null,
|
|
149
|
+
_parseFailed: false,
|
|
150
|
+
};
|
|
151
|
+
} catch {
|
|
152
|
+
// If JSON parsing fails, return fallback with flag for retry logic
|
|
153
|
+
console.warn('[SummaryPrompts] Failed to parse summary response as JSON, using fallback');
|
|
154
|
+
return {
|
|
155
|
+
shortSummary: responseText.substring(0, 150),
|
|
156
|
+
fullSummary: responseText.substring(0, 500),
|
|
157
|
+
keyActions: [],
|
|
158
|
+
filesModified: [],
|
|
159
|
+
outcome: 'ongoing',
|
|
160
|
+
prUrl: null,
|
|
161
|
+
sessionTitle: null,
|
|
162
|
+
_parseFailed: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Backward-compatible aliases for internal/test usage
|
|
168
|
+
export { stripMarkdownCodeBlock as _stripMarkdownCodeBlock };
|
|
169
|
+
export { trackMessageMetadata as _trackMessageMetadata };
|