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,314 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
+
import { messages, conversations } from './index.js';
|
|
4
|
+
import {
|
|
5
|
+
ACTIVITY_FIELDS_SQL,
|
|
6
|
+
mapTokenUsage,
|
|
7
|
+
mapScheduling,
|
|
8
|
+
parseCreateConfig,
|
|
9
|
+
buildUpdateClauses,
|
|
10
|
+
} from './session-helpers.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Session repository class
|
|
14
|
+
*/
|
|
15
|
+
export class SessionRepository extends BaseRepository {
|
|
16
|
+
constructor() {
|
|
17
|
+
super('sessions', SessionRepository.#mapSession);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static #mapSession(row) {
|
|
21
|
+
return {
|
|
22
|
+
id: row.id,
|
|
23
|
+
projectId: row.project_id,
|
|
24
|
+
name: row.name,
|
|
25
|
+
status: row.status,
|
|
26
|
+
mode: row.mode,
|
|
27
|
+
model: row.model,
|
|
28
|
+
thinkingEnabled: Boolean(row.thinking_enabled),
|
|
29
|
+
archived: Boolean(row.archived),
|
|
30
|
+
starred: Boolean(row.starred),
|
|
31
|
+
manuallyNamed: Boolean(row.manually_named),
|
|
32
|
+
gitBranch: row.git_branch,
|
|
33
|
+
gitWorktree: row.git_worktree,
|
|
34
|
+
prUrl: row.pr_url,
|
|
35
|
+
error: row.error,
|
|
36
|
+
costUsd: row.cost_usd,
|
|
37
|
+
claudeSessionId: row.claude_session_id,
|
|
38
|
+
nextTemplateId: row.next_template_id,
|
|
39
|
+
parentSessionId: row.parent_session_id,
|
|
40
|
+
pendingPrompt: row.pending_prompt || null,
|
|
41
|
+
pendingModel: row.pending_model || null,
|
|
42
|
+
effortLevel: row.effort_level || null,
|
|
43
|
+
autoSendPendingPrompt: Boolean(row.auto_send_pending_prompt),
|
|
44
|
+
slashCommands: row.slash_commands || null,
|
|
45
|
+
...mapTokenUsage(row),
|
|
46
|
+
...mapScheduling(row),
|
|
47
|
+
// Kanban fields
|
|
48
|
+
targetLaneId: row.target_lane_id || null,
|
|
49
|
+
laneTriggerDepth: row.lane_trigger_depth || 0,
|
|
50
|
+
createdAt: row.created_at,
|
|
51
|
+
updatedAt: row.updated_at,
|
|
52
|
+
lastActivityAt: row.last_activity_at || row.updated_at || row.created_at,
|
|
53
|
+
activeTimeMs: row.active_time_ms || 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Override getById to include computed last_activity_at and active_time_ms fields */
|
|
58
|
+
getById(id) {
|
|
59
|
+
const row = this.db
|
|
60
|
+
.prepare(`SELECT s.*, ${ACTIVITY_FIELDS_SQL} FROM sessions s WHERE s.id = ?`)
|
|
61
|
+
.get(id);
|
|
62
|
+
return this.map(row);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Create a new session with optional config (mode, thinkingEnabled, gitBranch, parentSessionId, status, model, effortLevel) */
|
|
66
|
+
create(projectId, name, prompt, options = {}) {
|
|
67
|
+
const config = parseCreateConfig(options, Array.prototype.slice.call(arguments, 4));
|
|
68
|
+
|
|
69
|
+
const id = databaseManager.generateId();
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
this.db
|
|
72
|
+
.prepare(
|
|
73
|
+
`INSERT INTO sessions (id, project_id, name, status, mode, thinking_enabled, git_branch, parent_session_id, model, effort_level, created_at, updated_at)
|
|
74
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
75
|
+
)
|
|
76
|
+
.run(id, projectId, name, config.status, config.mode, config.thinkingEnabled ? 1 : 0, config.gitBranch, config.parentSessionId, config.model, config.effortLevel, now, now);
|
|
77
|
+
|
|
78
|
+
// Create initial conversation
|
|
79
|
+
const conversation = conversations.create(id, 'Initial', true);
|
|
80
|
+
|
|
81
|
+
// Only create initial user message for sessions that start immediately
|
|
82
|
+
// For waiting/scheduled sessions, the message will be created when they start
|
|
83
|
+
if (config.status !== 'waiting' && config.status !== 'scheduled') {
|
|
84
|
+
messages.create(id, 'user', prompt, { toolUse: null, conversationId: conversation.id });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return this.getById(id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getByProjectId(projectId, { archived = null, starred = null, limit = null, offset = 0 } = {}) {
|
|
91
|
+
let sql = `SELECT s.*, ${ACTIVITY_FIELDS_SQL} FROM sessions s WHERE project_id = ?`;
|
|
92
|
+
const params = [projectId];
|
|
93
|
+
|
|
94
|
+
if (archived !== null) {
|
|
95
|
+
sql += ` AND archived = ?`;
|
|
96
|
+
params.push(archived ? 1 : 0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (starred !== null) {
|
|
100
|
+
sql += ` AND starred = ?`;
|
|
101
|
+
params.push(starred ? 1 : 0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
sql += ` ORDER BY
|
|
105
|
+
starred DESC,
|
|
106
|
+
updated_at DESC,
|
|
107
|
+
created_at DESC,
|
|
108
|
+
rowid DESC`;
|
|
109
|
+
|
|
110
|
+
// Add LIMIT/OFFSET for pagination
|
|
111
|
+
if (limit !== null) {
|
|
112
|
+
sql += ` LIMIT ? OFFSET ?`;
|
|
113
|
+
params.push(limit, offset);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
117
|
+
return this.mapAll(rows);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Get count of sessions for a project with optional archived/starred filters */
|
|
121
|
+
getCountByProjectId(projectId, { archived = null, starred = null } = {}) {
|
|
122
|
+
let sql = `SELECT COUNT(*) as count FROM sessions WHERE project_id = ?`;
|
|
123
|
+
const params = [projectId];
|
|
124
|
+
|
|
125
|
+
if (archived !== null) {
|
|
126
|
+
sql += ` AND archived = ?`;
|
|
127
|
+
params.push(archived ? 1 : 0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (starred !== null) {
|
|
131
|
+
sql += ` AND starred = ?`;
|
|
132
|
+
params.push(starred ? 1 : 0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return this.db.prepare(sql).get(...params).count;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getActiveAndWaiting() {
|
|
139
|
+
const rows = this.db
|
|
140
|
+
.prepare(
|
|
141
|
+
`SELECT s.*, p.name as project_name, p.working_directory as project_working_directory, ${ACTIVITY_FIELDS_SQL}
|
|
142
|
+
FROM sessions s JOIN projects p ON s.project_id = p.id
|
|
143
|
+
WHERE s.status IN ('starting', 'running', 'waiting') AND s.archived = 0
|
|
144
|
+
ORDER BY s.starred DESC, s.updated_at DESC, s.created_at DESC, s.rowid DESC`
|
|
145
|
+
)
|
|
146
|
+
.all();
|
|
147
|
+
return rows.map(row => ({
|
|
148
|
+
...SessionRepository.#mapSession(row),
|
|
149
|
+
projectName: row.project_name,
|
|
150
|
+
projectWorkingDirectory: row.project_working_directory,
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Get all child sessions of a parent session */
|
|
155
|
+
getChildSessions(parentSessionId) {
|
|
156
|
+
const rows = this.db
|
|
157
|
+
.prepare(
|
|
158
|
+
`SELECT s.*, ${ACTIVITY_FIELDS_SQL} FROM sessions s
|
|
159
|
+
WHERE parent_session_id = ?
|
|
160
|
+
ORDER BY updated_at DESC, created_at DESC, rowid DESC`
|
|
161
|
+
)
|
|
162
|
+
.all(parentSessionId);
|
|
163
|
+
return this.mapAll(rows);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Walk the parentSessionId chain upward to find the root session */
|
|
167
|
+
getRootSessionId(sessionId) {
|
|
168
|
+
let current = this.getById(sessionId);
|
|
169
|
+
const visited = new Set();
|
|
170
|
+
|
|
171
|
+
while (current?.parentSessionId) {
|
|
172
|
+
if (visited.has(current.id)) break; // cycle guard
|
|
173
|
+
visited.add(current.id);
|
|
174
|
+
current = this.getById(current.parentSessionId);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return current?.id ?? null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Collect all descendant session IDs recursively (does NOT include the starting session) */
|
|
181
|
+
getAllDescendantIds(sessionId) {
|
|
182
|
+
const stmt = this.db.prepare('SELECT id FROM sessions WHERE parent_session_id = ?');
|
|
183
|
+
const descendantIds = [];
|
|
184
|
+
const stack = [sessionId];
|
|
185
|
+
const visited = new Set();
|
|
186
|
+
|
|
187
|
+
while (stack.length > 0) {
|
|
188
|
+
const currentId = stack.pop();
|
|
189
|
+
if (visited.has(currentId)) continue;
|
|
190
|
+
visited.add(currentId);
|
|
191
|
+
|
|
192
|
+
const childRows = stmt.all(currentId);
|
|
193
|
+
for (const row of childRows) {
|
|
194
|
+
descendantIds.push(row.id);
|
|
195
|
+
stack.push(row.id);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return descendantIds;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
update(id, data) {
|
|
203
|
+
const { updates, values } = buildUpdateClauses(data);
|
|
204
|
+
|
|
205
|
+
if (updates.length === 0) return this.getById(id);
|
|
206
|
+
|
|
207
|
+
updates.push('updated_at = ?');
|
|
208
|
+
values.push(Date.now());
|
|
209
|
+
values.push(id);
|
|
210
|
+
|
|
211
|
+
this.db.prepare(`UPDATE sessions SET ${updates.join(', ')} WHERE id = ?`).run(...values);
|
|
212
|
+
|
|
213
|
+
return this.getById(id);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Duplicate a session with a new ID and reset state (does NOT handle git or conversation setup) */
|
|
217
|
+
duplicate(sourceSessionId, { name } = {}) {
|
|
218
|
+
const source = this.getById(sourceSessionId);
|
|
219
|
+
if (!source) {
|
|
220
|
+
throw new Error(`Session not found: ${sourceSessionId}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const id = databaseManager.generateId();
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
const newName = name || `${source.name} (Copy)`;
|
|
226
|
+
|
|
227
|
+
// Insert new session with same settings but new ID and status
|
|
228
|
+
this.db
|
|
229
|
+
.prepare(
|
|
230
|
+
`INSERT INTO sessions (id, project_id, name, status, mode, thinking_enabled, git_branch, model, effort_level, context_window,
|
|
231
|
+
input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens,
|
|
232
|
+
web_search_requests, cost_usd, created_at, updated_at)
|
|
233
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
234
|
+
)
|
|
235
|
+
.run(
|
|
236
|
+
id,
|
|
237
|
+
source.projectId,
|
|
238
|
+
newName,
|
|
239
|
+
'waiting', // Always reset status to draft
|
|
240
|
+
source.mode,
|
|
241
|
+
source.thinkingEnabled ? 1 : 0,
|
|
242
|
+
source.gitBranch, // Copy branch name (NOT worktree path)
|
|
243
|
+
source.model,
|
|
244
|
+
source.effortLevel,
|
|
245
|
+
source.contextWindow,
|
|
246
|
+
source.inputTokens,
|
|
247
|
+
source.outputTokens,
|
|
248
|
+
source.cacheReadInputTokens,
|
|
249
|
+
source.cacheCreationInputTokens,
|
|
250
|
+
source.webSearchRequests,
|
|
251
|
+
source.costUsd,
|
|
252
|
+
now,
|
|
253
|
+
now
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
return this.getById(id);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Get all sessions that have a PR URL set (used by prStatusService) */
|
|
260
|
+
getSessionsWithPrUrls() {
|
|
261
|
+
const rows = this.db
|
|
262
|
+
.prepare(
|
|
263
|
+
`SELECT s.*, ${ACTIVITY_FIELDS_SQL} FROM sessions s
|
|
264
|
+
WHERE pr_url IS NOT NULL ORDER BY updated_at DESC, created_at DESC, rowid DESC`
|
|
265
|
+
)
|
|
266
|
+
.all();
|
|
267
|
+
return this.mapAll(rows);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
updateUsage(id, usage) {
|
|
271
|
+
this.db
|
|
272
|
+
.prepare(
|
|
273
|
+
`UPDATE sessions SET input_tokens = ?, output_tokens = ?, cache_read_input_tokens = ?,
|
|
274
|
+
cache_creation_input_tokens = ?, web_search_requests = ?, context_window = ?, updated_at = ?
|
|
275
|
+
WHERE id = ?`
|
|
276
|
+
)
|
|
277
|
+
.run(usage.inputTokens, usage.outputTokens, usage.cacheReadInputTokens,
|
|
278
|
+
usage.cacheCreationInputTokens, usage.webSearchRequests, usage.contextWindow, Date.now(), id);
|
|
279
|
+
return this.getById(id);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Get scheduled sessions that are due to start (scheduled_at <= now) */
|
|
283
|
+
getScheduledSessionsDue(now) {
|
|
284
|
+
const rows = this.db
|
|
285
|
+
.prepare(
|
|
286
|
+
`SELECT s.*, ${ACTIVITY_FIELDS_SQL} FROM sessions s
|
|
287
|
+
WHERE status = 'scheduled' AND scheduled_at IS NOT NULL AND scheduled_at <= ? AND archived = 0
|
|
288
|
+
ORDER BY scheduled_at ASC`
|
|
289
|
+
)
|
|
290
|
+
.all(now);
|
|
291
|
+
return this.mapAll(rows);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Get all scheduled sessions, optionally filtered by project */
|
|
295
|
+
getScheduledSessions(projectId = null) {
|
|
296
|
+
let sql = `SELECT s.*, p.name as project_name, ${ACTIVITY_FIELDS_SQL}
|
|
297
|
+
FROM sessions s JOIN projects p ON s.project_id = p.id
|
|
298
|
+
WHERE s.status = 'scheduled' AND s.archived = 0`;
|
|
299
|
+
const params = [];
|
|
300
|
+
|
|
301
|
+
if (projectId) {
|
|
302
|
+
sql += ` AND s.project_id = ?`;
|
|
303
|
+
params.push(projectId);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
sql += ` ORDER BY s.scheduled_at ASC`;
|
|
307
|
+
|
|
308
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
309
|
+
return rows.map(row => ({
|
|
310
|
+
...SessionRepository.#mapSession(row),
|
|
311
|
+
projectName: row.project_name,
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Field definitions for session summary updates.
|
|
6
|
+
* Each entry defines how to transform the value for SQL.
|
|
7
|
+
*/
|
|
8
|
+
const SUMMARY_FIELDS = {
|
|
9
|
+
shortSummary: { column: 'short_summary', transform: (v) => v },
|
|
10
|
+
fullSummary: { column: 'full_summary', transform: (v) => v },
|
|
11
|
+
keyActions: { column: 'key_actions', transform: (v) => JSON.stringify(v) },
|
|
12
|
+
filesModified: { column: 'files_modified', transform: (v) => JSON.stringify(v) },
|
|
13
|
+
outcome: { column: 'outcome', transform: (v) => v },
|
|
14
|
+
messageCount: { column: 'message_count', transform: (v) => v },
|
|
15
|
+
lastSummarizedMessageId: { column: 'last_summarized_message_id', transform: (v) => v },
|
|
16
|
+
prMerged: { column: 'pr_merged', transform: (v) => (v ? 1 : 0) },
|
|
17
|
+
prState: { column: 'pr_state', transform: (v) => v },
|
|
18
|
+
hasMergeConflicts: { column: 'has_merge_conflicts', transform: (v) => (v ? 1 : 0) },
|
|
19
|
+
ciStatus: { column: 'ci_status', transform: (v) => v },
|
|
20
|
+
ciFailures: { column: 'ci_failures', transform: (v) => JSON.stringify(v) },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert an optional boolean to a DB-compatible value (null, 0, or 1).
|
|
25
|
+
*/
|
|
26
|
+
function optionalBoolToDb(value) {
|
|
27
|
+
if (value === undefined) return null;
|
|
28
|
+
return value ? 1 : 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Session summary repository class
|
|
33
|
+
*/
|
|
34
|
+
export class SessionSummaryRepository extends BaseRepository {
|
|
35
|
+
constructor() {
|
|
36
|
+
super('session_summaries', SessionSummaryRepository.#mapSummary);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static #mapSummary(row) {
|
|
40
|
+
return {
|
|
41
|
+
id: row.id,
|
|
42
|
+
sessionId: row.session_id,
|
|
43
|
+
shortSummary: row.short_summary,
|
|
44
|
+
fullSummary: row.full_summary,
|
|
45
|
+
keyActions: row.key_actions ? JSON.parse(row.key_actions) : [],
|
|
46
|
+
filesModified: row.files_modified ? JSON.parse(row.files_modified) : [],
|
|
47
|
+
outcome: row.outcome,
|
|
48
|
+
messageCount: row.message_count,
|
|
49
|
+
lastSummarizedMessageId: row.last_summarized_message_id,
|
|
50
|
+
prMerged: row.pr_merged !== null ? Boolean(row.pr_merged) : null,
|
|
51
|
+
prState: row.pr_state,
|
|
52
|
+
hasMergeConflicts: row.has_merge_conflicts !== null ? Boolean(row.has_merge_conflicts) : null,
|
|
53
|
+
ciStatus: row.ci_status,
|
|
54
|
+
ciFailures: row.ci_failures ? JSON.parse(row.ci_failures) : [],
|
|
55
|
+
generatedAt: row.generated_at,
|
|
56
|
+
createdAt: row.created_at,
|
|
57
|
+
updatedAt: row.updated_at,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get summary by session ID
|
|
63
|
+
* @param {string} sessionId
|
|
64
|
+
* @returns {Object|null}
|
|
65
|
+
*/
|
|
66
|
+
getBySessionId(sessionId) {
|
|
67
|
+
const row = this.db
|
|
68
|
+
.prepare('SELECT * FROM session_summaries WHERE session_id = ?')
|
|
69
|
+
.get(sessionId);
|
|
70
|
+
return this.map(row);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create a new session summary
|
|
75
|
+
* @param {string} sessionId
|
|
76
|
+
* @param {Object} data
|
|
77
|
+
* @returns {Object}
|
|
78
|
+
*/
|
|
79
|
+
create(sessionId, data) {
|
|
80
|
+
const id = databaseManager.generateId();
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
this.db
|
|
83
|
+
.prepare(
|
|
84
|
+
`INSERT INTO session_summaries
|
|
85
|
+
(id, session_id, short_summary, full_summary, key_actions, files_modified, outcome, message_count, last_summarized_message_id, pr_merged, pr_state, has_merge_conflicts, ci_status, ci_failures, generated_at, created_at, updated_at)
|
|
86
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
87
|
+
)
|
|
88
|
+
.run(
|
|
89
|
+
id,
|
|
90
|
+
sessionId,
|
|
91
|
+
data.shortSummary,
|
|
92
|
+
data.fullSummary,
|
|
93
|
+
data.keyActions ? JSON.stringify(data.keyActions) : null,
|
|
94
|
+
data.filesModified ? JSON.stringify(data.filesModified) : null,
|
|
95
|
+
data.outcome || 'ongoing',
|
|
96
|
+
data.messageCount || 0,
|
|
97
|
+
data.lastSummarizedMessageId || null,
|
|
98
|
+
optionalBoolToDb(data.prMerged),
|
|
99
|
+
data.prState || null,
|
|
100
|
+
optionalBoolToDb(data.hasMergeConflicts),
|
|
101
|
+
data.ciStatus || null,
|
|
102
|
+
data.ciFailures ? JSON.stringify(data.ciFailures) : null,
|
|
103
|
+
now,
|
|
104
|
+
now,
|
|
105
|
+
now
|
|
106
|
+
);
|
|
107
|
+
return this.getById(id);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update an existing session summary
|
|
112
|
+
* @param {string} id
|
|
113
|
+
* @param {Object} data
|
|
114
|
+
* @returns {Object}
|
|
115
|
+
*/
|
|
116
|
+
update(id, data) {
|
|
117
|
+
const updates = [];
|
|
118
|
+
const values = [];
|
|
119
|
+
|
|
120
|
+
// Build update clauses from field definitions
|
|
121
|
+
for (const [key, def] of Object.entries(SUMMARY_FIELDS)) {
|
|
122
|
+
if (data[key] !== undefined) {
|
|
123
|
+
updates.push(`${def.column} = ?`);
|
|
124
|
+
values.push(def.transform(data[key]));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (updates.length === 0) return this.getById(id);
|
|
129
|
+
|
|
130
|
+
// Always update generated_at and updated_at when updating
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
updates.push('generated_at = ?', 'updated_at = ?');
|
|
133
|
+
values.push(now, now, id);
|
|
134
|
+
|
|
135
|
+
this.db.prepare(`UPDATE session_summaries SET ${updates.join(', ')} WHERE id = ?`).run(...values);
|
|
136
|
+
|
|
137
|
+
return this.getById(id);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Create or update a session summary (upsert)
|
|
142
|
+
* @param {string} sessionId
|
|
143
|
+
* @param {Object} data
|
|
144
|
+
* @returns {Object}
|
|
145
|
+
*/
|
|
146
|
+
upsert(sessionId, data) {
|
|
147
|
+
const existing = this.getBySessionId(sessionId);
|
|
148
|
+
if (existing) {
|
|
149
|
+
return this.update(existing.id, data);
|
|
150
|
+
}
|
|
151
|
+
return this.create(sessionId, data);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get summaries for multiple session IDs in a single query
|
|
156
|
+
* @param {string[]} sessionIds
|
|
157
|
+
* @returns {Object[]}
|
|
158
|
+
*/
|
|
159
|
+
getBySessionIds(sessionIds) {
|
|
160
|
+
if (!sessionIds || sessionIds.length === 0) return [];
|
|
161
|
+
const placeholders = sessionIds.map(() => '?').join(',');
|
|
162
|
+
const rows = this.db
|
|
163
|
+
.prepare(`SELECT * FROM session_summaries WHERE session_id IN (${placeholders})`)
|
|
164
|
+
.all(...sessionIds);
|
|
165
|
+
return this.mapAll(rows);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Delete summary by session ID
|
|
170
|
+
* @param {string} sessionId
|
|
171
|
+
*/
|
|
172
|
+
deleteBySessionId(sessionId) {
|
|
173
|
+
this.db.prepare('DELETE FROM session_summaries WHERE session_id = ?').run(sessionId);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Duplicates the session summary from one session to another.
|
|
178
|
+
* Note: PR-related fields (prMerged, prState, hasMergeConflicts, ciStatus, ciFailures)
|
|
179
|
+
* are NOT copied because the duplicated session is a fresh start and may create
|
|
180
|
+
* its own PR. Copying prMerged=true would prevent the summary from ever being
|
|
181
|
+
* regenerated (see summaryService.generateSummary skip logic).
|
|
182
|
+
* @param {string} sourceSessionId - Source session ID
|
|
183
|
+
* @param {string} targetSessionId - Target session ID
|
|
184
|
+
*/
|
|
185
|
+
duplicateForSession(sourceSessionId, targetSessionId) {
|
|
186
|
+
const summary = this.getBySessionId(sourceSessionId);
|
|
187
|
+
|
|
188
|
+
if (summary) {
|
|
189
|
+
this.create(targetSessionId, {
|
|
190
|
+
shortSummary: summary.shortSummary,
|
|
191
|
+
fullSummary: summary.fullSummary,
|
|
192
|
+
keyActions: summary.keyActions,
|
|
193
|
+
filesModified: summary.filesModified,
|
|
194
|
+
outcome: summary.outcome,
|
|
195
|
+
messageCount: summary.messageCount,
|
|
196
|
+
// PR-related fields intentionally omitted - see note above
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Session template repository class
|
|
6
|
+
*/
|
|
7
|
+
export class SessionTemplateRepository extends BaseRepository {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('session_templates', SessionTemplateRepository.#mapTemplate);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static #mapTemplate(row) {
|
|
13
|
+
return {
|
|
14
|
+
id: row.id,
|
|
15
|
+
projectId: row.project_id,
|
|
16
|
+
name: row.name,
|
|
17
|
+
prompt: row.prompt,
|
|
18
|
+
nextTemplateId: row.next_template_id,
|
|
19
|
+
thinkingEnabled: row.thinking_enabled === null ? null : Boolean(row.thinking_enabled),
|
|
20
|
+
gitBranch: row.git_branch,
|
|
21
|
+
gitMode: row.git_mode,
|
|
22
|
+
model: row.model || null,
|
|
23
|
+
mode: row.mode || null,
|
|
24
|
+
effortLevel: row.effort_level ?? null,
|
|
25
|
+
targetLaneId: row.target_lane_id || null,
|
|
26
|
+
createdAt: row.created_at,
|
|
27
|
+
updatedAt: row.updated_at,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a new session template
|
|
33
|
+
* @param {Object} data
|
|
34
|
+
* @param {string|null} data.projectId - null for global templates
|
|
35
|
+
* @param {string} data.name
|
|
36
|
+
* @param {string} data.prompt
|
|
37
|
+
* @param {string|null} data.nextTemplateId
|
|
38
|
+
* @param {boolean|null} data.thinkingEnabled
|
|
39
|
+
* @param {string|null} data.gitBranch
|
|
40
|
+
* @param {string|null} data.gitMode
|
|
41
|
+
* @param {string|null} data.model
|
|
42
|
+
* @param {string|null} data.mode
|
|
43
|
+
* @param {string|null} data.effortLevel
|
|
44
|
+
* @returns {Object}
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* Normalize thinkingEnabled to a DB-compatible value (null, 0, or 1).
|
|
48
|
+
*/
|
|
49
|
+
static #normalizeThinkingEnabled(value) {
|
|
50
|
+
if (value === null || value === undefined) return null;
|
|
51
|
+
return value ? 1 : 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
create(data) {
|
|
55
|
+
const id = databaseManager.generateId();
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
this.db
|
|
58
|
+
.prepare(
|
|
59
|
+
`INSERT INTO session_templates (id, project_id, name, prompt, next_template_id, thinking_enabled, git_branch, git_mode, model, mode, effort_level, target_lane_id, created_at, updated_at)
|
|
60
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
61
|
+
)
|
|
62
|
+
.run(
|
|
63
|
+
id,
|
|
64
|
+
data.projectId || null,
|
|
65
|
+
data.name,
|
|
66
|
+
data.prompt,
|
|
67
|
+
data.nextTemplateId || null,
|
|
68
|
+
SessionTemplateRepository.#normalizeThinkingEnabled(data.thinkingEnabled),
|
|
69
|
+
data.gitBranch || null,
|
|
70
|
+
data.gitMode || null,
|
|
71
|
+
data.model || null,
|
|
72
|
+
data.mode !== undefined && data.mode !== null ? data.mode : null,
|
|
73
|
+
data.effortLevel ?? null,
|
|
74
|
+
data.targetLaneId || null,
|
|
75
|
+
now,
|
|
76
|
+
now
|
|
77
|
+
);
|
|
78
|
+
return this.getById(id);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Field mapping for update: property name -> { column, transform }
|
|
83
|
+
* Transform converts the JS value to the DB value.
|
|
84
|
+
*/
|
|
85
|
+
static #updateFields = {
|
|
86
|
+
name: { column: 'name', transform: (v) => v },
|
|
87
|
+
prompt: { column: 'prompt', transform: (v) => v },
|
|
88
|
+
nextTemplateId: { column: 'next_template_id', transform: (v) => v },
|
|
89
|
+
thinkingEnabled: { column: 'thinking_enabled', transform: (v) => (v === null ? null : (v ? 1 : 0)) },
|
|
90
|
+
gitBranch: { column: 'git_branch', transform: (v) => v || null }, // Match create() behavior: empty string -> null
|
|
91
|
+
gitMode: { column: 'git_mode', transform: (v) => v },
|
|
92
|
+
model: { column: 'model', transform: (v) => v },
|
|
93
|
+
mode: { column: 'mode', transform: (v) => v },
|
|
94
|
+
effortLevel: { column: 'effort_level', transform: (v) => v },
|
|
95
|
+
targetLaneId: { column: 'target_lane_id', transform: (v) => v },
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Update a session template
|
|
100
|
+
* @param {string} id
|
|
101
|
+
* @param {Object} data
|
|
102
|
+
* @returns {Object}
|
|
103
|
+
*/
|
|
104
|
+
update(id, data) {
|
|
105
|
+
const updates = [];
|
|
106
|
+
const values = [];
|
|
107
|
+
|
|
108
|
+
for (const [key, def] of Object.entries(SessionTemplateRepository.#updateFields)) {
|
|
109
|
+
if (data[key] !== undefined) {
|
|
110
|
+
updates.push(`${def.column} = ?`);
|
|
111
|
+
values.push(def.transform(data[key]));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (updates.length === 0) return this.getById(id);
|
|
116
|
+
|
|
117
|
+
updates.push('updated_at = ?');
|
|
118
|
+
values.push(Date.now());
|
|
119
|
+
values.push(id);
|
|
120
|
+
|
|
121
|
+
this.db.prepare(`UPDATE session_templates SET ${updates.join(', ')} WHERE id = ?`).run(...values);
|
|
122
|
+
|
|
123
|
+
return this.getById(id);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get all global templates (projectId is null)
|
|
128
|
+
* @returns {Array}
|
|
129
|
+
*/
|
|
130
|
+
getGlobal() {
|
|
131
|
+
const rows = this.db
|
|
132
|
+
.prepare('SELECT * FROM session_templates WHERE project_id IS NULL ORDER BY name')
|
|
133
|
+
.all();
|
|
134
|
+
return this.mapAll(rows);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get templates by project ID
|
|
139
|
+
* @param {string} projectId
|
|
140
|
+
* @returns {Array}
|
|
141
|
+
*/
|
|
142
|
+
getByProjectId(projectId) {
|
|
143
|
+
const rows = this.db
|
|
144
|
+
.prepare('SELECT * FROM session_templates WHERE project_id = ? ORDER BY name')
|
|
145
|
+
.all(projectId);
|
|
146
|
+
return this.mapAll(rows);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get all templates available for a project (project-specific + global)
|
|
151
|
+
* @param {string} projectId
|
|
152
|
+
* @returns {Object} { project: Array, global: Array }
|
|
153
|
+
*/
|
|
154
|
+
getAvailableForProject(projectId) {
|
|
155
|
+
return {
|
|
156
|
+
project: this.getByProjectId(projectId),
|
|
157
|
+
global: this.getGlobal(),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get all templates
|
|
163
|
+
* @returns {Array}
|
|
164
|
+
*/
|
|
165
|
+
getAll() {
|
|
166
|
+
const rows = this.db
|
|
167
|
+
.prepare('SELECT * FROM session_templates ORDER BY project_id NULLS FIRST, name')
|
|
168
|
+
.all();
|
|
169
|
+
return this.mapAll(rows);
|
|
170
|
+
}
|
|
171
|
+
}
|