@xopcai/xopc 0.0.121 → 0.0.122
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/README.md +23 -3
- package/README.zh-CN.md +23 -3
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-4AW4JgSU.js → agent-avatar-dicebear-CK3s5cJo.js} +1 -1
- package/dist/gateway/static/root/assets/agents-SsQrj9B-.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CE6GkGU2.js → app-management-settings-panel-CWsNHtTs.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BmaUjghw.js → appearance-settings-C4b8y6iZ.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-CYb6fame.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CX4_KWut.js → archive-plugin-DAtNW3Ia.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-jf-PIIUU.js → attachment-load-Dm-RY54y.js} +1 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-BKHewSVT.js +1 -0
- package/dist/gateway/static/root/assets/automations-page-DpOOq1nk.js +1 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DG9XQikd.js → binary-plugins-BG6Xnk71.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Up_VIxLJ.js → block-editor-DfndiO3h.js} +28 -28
- package/dist/gateway/static/root/assets/browser-settings-page-9nXxgh4u.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-CGM4OyMw.js → capability-presets-api-Cx93GLdw.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-CzWt0LBb.js → capability-presets-settings-panel-D528_YFy.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-BEByL4_o.js → channel-recipient-api-DdPTtEs8.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-Ck-3h5lI.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-DL-fAqE_.js +2 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-D835EHJd.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-CCa1F7Fg.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-DZxa_j7-.js → extension-debug-page-W2ITCPSA.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CseziW53.js → extension-page-DrvUytvn.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-B_9J-xyo.js → extension-settings-page-qM8aLT5u.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-Cs4ck8zA.js → fetch-ovkvM8KQ.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-Dl223a7r.js → gateway-config-api-BB3biuD0.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-mmmnIH27.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-IvePwjFE.js → gateway-startup-retry-CanQ8UcR.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-CNqNdEkQ.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-Ceu5zrN6.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-CUCP6yCl.js → goals-config-api-BBOX6mUl.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BP1kfvWv.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-Dj87qPxv.js → goals-settings-Bh3A7OZV.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-YoYgKlfX.js +1 -0
- package/dist/gateway/static/root/assets/index-DdqSO9R2.js +191 -0
- package/dist/gateway/static/root/assets/index-jGB-2nI-.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DJgYw0nB.js → keyboard-shortcuts-settings-B4TKJHla.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-BgVk5DvZ.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-gnQ_EiGo.js → markdown-split-DcFtdLci.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-CQV-mgJn.js → media-plugins-B6SmeLVB.js} +1 -1
- package/dist/gateway/static/root/assets/messages-B5O4OIEo.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BU0x8PGP.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-D6JYVP4I.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-mqRJYnJS.js +2 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BPMXC-2u.js → preview-open-alternatives-C29O19s7.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-DOTgzoeh.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DGUIb4y5.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DVKKUj3u.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BEHqwZjz.js → save-bar-controls-B4ad9GfB.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CMY25m97.js → schedule-field-DdYncfLR.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CxjxsFfg.js → schema-form-BMTrDE-7.js} +1 -1
- package/dist/gateway/static/root/assets/segmented-styles-BL8oLBEp.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-BVbzUWxW.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-DrNNoSqE.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-s-kiLAxP.js → settings-collapsible-section-MwTW2eK4.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-CIxIEYxU.js → settings-form-section-Dg6LBbcr.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-CMDqgeMR.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-layout-BX4T-OLF.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-Ce1pcFtR.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-DWE-dUFh.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-aKSraR8l.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CS-FU_B-.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-BaF3u0LA.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-Czjf0kLJ.js → system-settings-panel-Dd6Y35Cm.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-Cmt8SYdI.js → typed-models-lib-Cx6XV5A2.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-DyM-ckEQ.js → use-channel-catalog-D4sMbmy7.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-BygLv8vm.js → user-profile-settings-panel-Bx-hjMQc.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-CloW8coB.js → utils-D7b_vZnv.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-CkhbRt1q.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel--TdohF5N.js +3 -0
- package/dist/gateway/static/root/assets/workflow-start-dialog-B0lavx_o.js +1 -0
- package/dist/gateway/static/root/assets/workflows-page-CZR05Bu_.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +2 -0
- package/dist/src/agent/agent-manager.js +47 -7
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/context/project-context.d.ts +19 -0
- package/dist/src/agent/context/project-context.js +90 -0
- package/dist/src/agent/context/project-context.js.map +1 -0
- package/dist/src/agent/goals/goal-locale.js +2 -2
- package/dist/src/agent/memory/types.d.ts +1 -0
- package/dist/src/agent/prompt/service-prompt-builder.d.ts +1 -0
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -0
- package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
- package/dist/src/agent/prompt/system-prompt-params.d.ts +2 -0
- package/dist/src/agent/prompt/system-prompt-params.js +1 -0
- package/dist/src/agent/prompt/system-prompt-params.js.map +1 -1
- package/dist/src/agent/prompt/system-prompt.d.ts +1 -0
- package/dist/src/agent/prompt/system-prompt.js +2 -1
- package/dist/src/agent/prompt/system-prompt.js.map +1 -1
- package/dist/src/agent/service.js +4 -3
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/session/session-config-service.js +6 -1
- package/dist/src/agent/session/session-config-service.js.map +1 -1
- package/dist/src/agent/session/session-hydrator.js +7 -3
- package/dist/src/agent/session/session-hydrator.js.map +1 -1
- package/dist/src/agent/session/session-inspector.js +9 -5
- package/dist/src/agent/session/session-inspector.js.map +1 -1
- package/dist/src/agent/starter-agents.js +5 -34
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/workflow/index.js +2 -2
- package/dist/src/agent/workflow/runtime.js +1 -1
- package/dist/src/automations/api/routes.js +13 -2
- package/dist/src/automations/api/routes.js.map +1 -1
- package/dist/src/automations/domain/types.d.ts +2 -0
- package/dist/src/automations/domain/validation.d.ts +3 -0
- package/dist/src/automations/domain/validation.js +1 -0
- package/dist/src/automations/domain/validation.js.map +1 -1
- package/dist/src/automations/draft/automation-draft-validator.d.ts +1 -0
- package/dist/src/automations/service/action-executor.js +3 -1
- package/dist/src/automations/service/action-executor.js.map +1 -1
- package/dist/src/automations/service/automation-service.d.ts +4 -1
- package/dist/src/automations/service/automation-service.js +2 -2
- package/dist/src/automations/service/automation-service.js.map +1 -1
- package/dist/src/automations/storage/automation-repository.d.ts +3 -1
- package/dist/src/automations/storage/automation-repository.js +7 -5
- package/dist/src/automations/storage/automation-repository.js.map +1 -1
- package/dist/src/automations/storage/automation-run-repository.d.ts +1 -0
- package/dist/src/automations/storage/automation-run-repository.js +12 -1
- package/dist/src/automations/storage/automation-run-repository.js.map +1 -1
- package/dist/src/chat-commands/context.js +2 -1
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +4 -0
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/goal.js +27 -4
- package/dist/src/cli/commands/goal.js.map +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/profile.js +1 -1
- package/dist/src/cli/commands/project.d.ts +4 -0
- package/dist/src/cli/commands/project.js +239 -0
- package/dist/src/cli/commands/project.js.map +1 -0
- package/dist/src/cli/commands/tunnel.js +1 -1
- package/dist/src/cli/commands/update.js +1 -1
- package/dist/src/cli/extension-cli-register.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/index.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/hono/routes/goals.js +51 -4
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +5 -1
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/notes.js +5 -0
- package/dist/src/gateway/hono/routes/notes.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.d.ts +3 -0
- package/dist/src/gateway/hono/routes/projects.js +578 -0
- package/dist/src/gateway/hono/routes/projects.js.map +1 -0
- package/dist/src/gateway/hono/routes/sessions.js +47 -7
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +27 -4
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service.d.ts +3 -0
- package/dist/src/gateway/service.js +10 -3
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-service.d.ts +1 -1
- package/dist/src/goals/goal-service.js +2 -1
- package/dist/src/goals/goal-service.js.map +1 -1
- package/dist/src/goals/goal-store.d.ts +1 -1
- package/dist/src/goals/goal-store.js +12 -6
- package/dist/src/goals/goal-store.js.map +1 -1
- package/dist/src/goals/goal-workflow-judge.js +1 -1
- package/dist/src/goals/types.d.ts +3 -0
- package/dist/src/i18n/goals-bundle.js +1 -1
- package/dist/src/i18n/index.js +1 -1
- package/dist/src/i18n/share-tool-bundle.js +2 -2
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/notes/agent-context.js +2 -2
- package/dist/src/projects/automation-bind.d.ts +1 -0
- package/dist/src/projects/automation-bind.js +16 -0
- package/dist/src/projects/automation-bind.js.map +1 -0
- package/dist/src/projects/goal-bind.d.ts +4 -0
- package/dist/src/projects/goal-bind.js +40 -0
- package/dist/src/projects/goal-bind.js.map +1 -0
- package/dist/src/projects/index.d.ts +11 -0
- package/dist/src/projects/index.js +11 -0
- package/dist/src/projects/memory-bind.d.ts +1 -0
- package/dist/src/projects/memory-bind.js +16 -0
- package/dist/src/projects/memory-bind.js.map +1 -0
- package/dist/src/projects/project-agent.d.ts +10 -0
- package/dist/src/projects/project-agent.js +27 -0
- package/dist/src/projects/project-agent.js.map +1 -0
- package/dist/src/projects/project-loop.d.ts +58 -0
- package/dist/src/projects/project-loop.js +118 -0
- package/dist/src/projects/project-loop.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +26 -0
- package/dist/src/projects/project-service.js +94 -0
- package/dist/src/projects/project-service.js.map +1 -0
- package/dist/src/projects/project-store.d.ts +31 -0
- package/dist/src/projects/project-store.js +193 -0
- package/dist/src/projects/project-store.js.map +1 -0
- package/dist/src/projects/session-bind.d.ts +5 -0
- package/dist/src/projects/session-bind.js +46 -0
- package/dist/src/projects/session-bind.js.map +1 -0
- package/dist/src/projects/types.d.ts +60 -0
- package/dist/src/projects/types.js +1 -0
- package/dist/src/projects/workflow-bind.d.ts +1 -0
- package/dist/src/projects/workflow-bind.js +16 -0
- package/dist/src/projects/workflow-bind.js.map +1 -0
- package/dist/src/projects/workspace.d.ts +5 -0
- package/dist/src/projects/workspace.js +25 -0
- package/dist/src/projects/workspace.js.map +1 -0
- package/dist/src/session/index.d.ts +1 -1
- package/dist/src/session/index.js +3 -3
- package/dist/src/session/session-workspace.d.ts +4 -2
- package/dist/src/session/session-workspace.js +10 -3
- package/dist/src/session/session-workspace.js.map +1 -1
- package/dist/src/session/types.d.ts +3 -0
- package/dist/src/session/types.js.map +1 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +3 -0
- package/dist/src/storage/sqlite/memory-records-repository.js +16 -5
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/019_projects.sql +40 -0
- package/dist/src/storage/sqlite/migrations/020_project_default_agent.sql +1 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/notes-repository.js +20 -4
- package/dist/src/storage/sqlite/notes-repository.js.map +1 -1
- package/dist/src/storage/sqlite/row-mappers.d.ts +2 -0
- package/dist/src/storage/sqlite/row-mappers.js +2 -0
- package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/session-repository.js +10 -5
- package/dist/src/storage/sqlite/session-repository.js.map +1 -1
- package/dist/src/storage/sqlite/transcript-repository.js +8 -0
- package/dist/src/storage/sqlite/transcript-repository.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/tui/tui-commands.js +145 -0
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/workflows/domain/run.d.ts +1 -0
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/index.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
- package/dist/src/workflows/service/workflow-run-service.js +18 -4
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +2 -0
- package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +3 -0
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
- package/dist/src/workflows/store/run-index-store.d.ts +1 -0
- package/dist/src/workflows/store/run-index-store.js +13 -3
- package/dist/src/workflows/store/run-index-store.js.map +1 -1
- package/dist/src/workflows/store/run-store.d.ts +3 -1
- package/dist/src/workflows/store/run-store.js +5 -2
- package/dist/src/workflows/store/run-store.js.map +1 -1
- package/package.json +10 -1
- package/dist/gateway/static/root/assets/agents-2SVUUPlp.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-BWeQ1I49.js +0 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-BHk5rtJz.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-QHotl53D.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-Cmwi6IIQ.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-BOMCUe2J.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-C-Dexk_F.js +0 -2
- package/dist/gateway/static/root/assets/dreaming-settings-WPEj8pir.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-DdzbtW9R.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-Dxldogms.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-DU5Nd6Xp.js +0 -2
- package/dist/gateway/static/root/assets/heartbeat-settings-Cgm5JmwH.js +0 -1
- package/dist/gateway/static/root/assets/index-BU1pvxmS.js +0 -191
- package/dist/gateway/static/root/assets/index-l54WfSqe.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-C10aS4XO.js +0 -2
- package/dist/gateway/static/root/assets/messages-C6Kc4Jhf.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-B35CAfg6.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-Brlwse8x.js +0 -2
- package/dist/gateway/static/root/assets/note-image-lightbox-BthwKjLA.js +0 -1
- package/dist/gateway/static/root/assets/note-time-hm6JO_Wi.js +0 -1
- package/dist/gateway/static/root/assets/notes-page-CoAwRc7L.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-B36RQ05h.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-73THChnj.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CR9VsY-R.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-BJ1AhfKI.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-layout--6qT7ZJa.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-C1TtS1vJ.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-BZADSbS1.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-gqC8Rmhy.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-C9P-WYvS.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CEpSvOhF.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-BsoqJba6.js +0 -1
- package/dist/gateway/static/root/assets/voice-player-store-DduWlJYV.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-CcQeX6l5.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-CeA8A9Ef.js +0 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript-repository.js","names":[],"sources":["../../../../src/storage/sqlite/transcript-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CompactionCheckpointDetail, CompactionCheckpointSummary } from '../../session/types.js';\nimport type { TranscriptCompactionRecord } from '../../session/transcript-format.js';\nimport {\n buildSessionContextForLlm,\n type TranscriptStoredRow,\n} from '../../session/session-context-for-llm.js';\nimport {\n classifyStoredRow,\n estimateTokensFromMessages,\n extractFtsContent,\n transcriptEntryRowToStoredRow,\n type TranscriptEntryRow,\n} from './row-mappers.js';\nimport { getCurrentSessionId, readCurrentSessionId } from './session-repository.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst MAX_CHECKPOINTS_PER_TRANSCRIPT = 15;\n\nfunction nextSeq(db: DatabaseSync, sessionId: string): number {\n const row = db\n .prepare(`SELECT COALESCE(MAX(seq), 0) AS max_seq FROM transcript_entries WHERE session_id = ?`)\n .get(sessionId) as { max_seq: number };\n return (row.max_seq ?? 0) + 1;\n}\n\nfunction insertEntry(\n db: DatabaseSync,\n params: {\n sessionId: string;\n sessionKey: string;\n row: TranscriptStoredRow;\n entryId?: string;\n createdAt?: number;\n },\n): TranscriptEntryRow {\n const { entryKind, role } = classifyStoredRow(params.row);\n const entryId = params.entryId ?? randomUUID();\n const seq = nextSeq(db, params.sessionId);\n const createdAt = params.createdAt ?? Date.now();\n const payloadJson = JSON.stringify(params.row);\n\n db.prepare(\n `INSERT INTO transcript_entries (entry_id, session_id, seq, entry_kind, role, payload_json, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(entryId, params.sessionId, seq, entryKind, role, payloadJson, createdAt);\n\n const content = extractFtsContent(params.row);\n if (content.trim()) {\n db.prepare(\n `INSERT INTO transcript_fts (content, session_key, session_id, entry_id)\n VALUES (?, ?, ?, ?)`,\n ).run(content, params.sessionKey, params.sessionId, entryId);\n }\n\n return {\n entry_id: entryId,\n session_id: params.sessionId,\n seq,\n entry_kind: entryKind,\n role,\n payload_json: payloadJson,\n created_at: createdAt,\n };\n}\n\nexport function appendTranscriptEntry(\n sessionKey: string,\n row: TranscriptStoredRow,\n opts?: { sessionId?: string; tokenDelta?: number },\n): TranscriptEntryRow {\n return runSqliteWriteTransaction((db) => {\n const sessionId = opts?.sessionId ?? readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n const inserted = insertEntry(db, { sessionId, sessionKey, row });\n if (classifyStoredRow(row).entryKind === 'message') {\n const tokenDelta = opts?.tokenDelta ?? 0;\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n }\n return inserted;\n });\n}\n\nexport function loadTranscriptRowsForSession(sessionKey: string): TranscriptStoredRow[] {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return [];\n }\n return loadTranscriptRows(sessionId);\n}\n\nexport function loadTranscriptRows(sessionId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ?\n ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n return rows.map(transcriptEntryRowToStoredRow);\n}\n\nexport function loadLlmMessagesForSession(sessionKey: string): AgentMessage[] {\n const rows = loadTranscriptRowsForSession(sessionKey);\n return buildSessionContextForLlm(rows);\n}\n\nexport function replaceTranscriptRows(\n sessionKey: string,\n rows: TranscriptStoredRow[],\n opts?: { appendCompaction?: TranscriptCompactionRecord },\n): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n const toWrite = [...rows];\n if (opts?.appendCompaction) {\n toWrite.push({\n type: 'compaction',\n ...opts.appendCompaction,\n } as unknown as TranscriptStoredRow);\n }\n\n for (const row of toWrite) {\n insertEntry(db, { sessionId, sessionKey, row });\n }\n\n const llm = buildSessionContextForLlm(toWrite);\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n\nexport function loadCheckpointRows(sessionKey: string, checkpointId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n return [];\n }\n const entries = db\n .prepare(\n `SELECT payload_json FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{ payload_json: string }>;\n return entries.map((entry) => JSON.parse(entry.payload_json) as TranscriptStoredRow);\n}\n\nexport function paginateTranscriptMessages(\n sessionKey: string,\n options: {\n offset?: number;\n limit?: number;\n beforeSeq?: number;\n /** 0-based exclusive end index (legacy cursor from gateway UI). */\n beforeEndIndex?: number;\n includeContext?: boolean;\n } = {},\n): {\n rows: TranscriptStoredRow[];\n messages: AgentMessage[];\n total: number;\n startSeq: number;\n endSeq: number;\n} {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return { rows: [], messages: [], total: 0, startSeq: 0, endSeq: 0 };\n }\n\n const db = getSqliteDatabase();\n const kinds = options.includeContext ? ['message', 'context'] : ['message'];\n const kindPlaceholders = kinds.map(() => '?').join(', ');\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) AS total FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})`,\n )\n .get(sessionId, ...kinds) as { total: number };\n const total = countRow.total;\n\n const limit = Math.min(200, Math.max(1, Math.trunc(options.limit ?? 50)));\n const offset = Math.max(0, Math.trunc(options.offset ?? 0));\n\n let rows: TranscriptEntryRow[];\n if (options.beforeEndIndex !== undefined && Number.isFinite(options.beforeEndIndex)) {\n const endExclusive = Math.min(total, Math.max(0, Math.trunc(options.beforeEndIndex)));\n const startInclusive = Math.max(0, endExclusive - limit);\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM (\n SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at,\n ROW_NUMBER() OVER (ORDER BY seq ASC) - 1 AS idx\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n )\n WHERE idx >= ? AND idx < ?\n ORDER BY seq ASC`,\n )\n .all(sessionId, ...kinds, startInclusive, endExclusive) as TranscriptEntryRow[];\n } else if (options.beforeSeq !== undefined && Number.isFinite(options.beforeSeq)) {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders}) AND seq < ?\n ORDER BY seq DESC\n LIMIT ?`,\n )\n .all(sessionId, ...kinds, options.beforeSeq, limit) as TranscriptEntryRow[];\n rows.reverse();\n } else {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n ORDER BY seq DESC\n LIMIT ? OFFSET ?`,\n )\n .all(sessionId, ...kinds, limit, offset) as TranscriptEntryRow[];\n rows.reverse();\n }\n\n const storedRows = rows.map(transcriptEntryRowToStoredRow);\n const messages = buildSessionContextForLlm(storedRows);\n const startSeq = rows[0]?.seq ?? 0;\n const endSeq = rows[rows.length - 1]?.seq ?? 0;\n return { rows: storedRows, messages, total, startSeq, endSeq };\n}\n\nexport function captureCompactionCheckpoint(sessionKey: string): string | null {\n return runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n return null;\n }\n\n const entries = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries WHERE session_id = ? ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n if (entries.length === 0) {\n return null;\n }\n\n const checkpointId = randomUUID();\n const now = Date.now();\n const messageCount = entries.filter((e) => e.entry_kind === 'message').length;\n const sizeBytes = entries.reduce((sum, e) => sum + e.payload_json.length, 0);\n\n db.prepare(\n `INSERT INTO compaction_checkpoints\n (checkpoint_id, session_id, session_key, created_at, message_count, size_bytes)\n VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(checkpointId, sessionId, sessionKey, now, messageCount, sizeBytes);\n\n const insertCheckpointEntry = db.prepare(\n `INSERT INTO checkpoint_entries (checkpoint_id, seq, entry_kind, role, payload_json)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const entry of entries) {\n insertCheckpointEntry.run(\n checkpointId,\n entry.seq,\n entry.entry_kind,\n entry.role,\n entry.payload_json,\n );\n }\n\n pruneCompactionCheckpoints(db, sessionId);\n return checkpointId;\n });\n}\n\nfunction pruneCompactionCheckpoints(db: DatabaseSync, sessionId: string): void {\n const rows = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_id = ?\n ORDER BY created_at ASC`,\n )\n .all(sessionId) as Array<{ checkpoint_id: string }>;\n if (rows.length <= MAX_CHECKPOINTS_PER_TRANSCRIPT) {\n return;\n }\n const toDelete = rows.slice(0, rows.length - MAX_CHECKPOINTS_PER_TRANSCRIPT);\n const del = db.prepare(`DELETE FROM compaction_checkpoints WHERE checkpoint_id = ?`);\n for (const row of toDelete) {\n del.run(row.checkpoint_id);\n }\n}\n\nexport function listCompactionCheckpoints(sessionKey: string): CompactionCheckpointSummary[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ?\n ORDER BY created_at DESC`,\n )\n .all(sessionKey) as Array<{\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }>;\n\n return rows.map((row) => ({\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n }));\n}\n\nexport function getCompactionCheckpointDetail(\n sessionKey: string,\n checkpointId: string,\n): CompactionCheckpointDetail | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as\n | {\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }\n | undefined;\n if (!row) {\n return null;\n }\n return {\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n messageCount: row.message_count,\n };\n}\n\nexport function restoreCompactionCheckpoint(sessionKey: string, checkpointId: string): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n throw new Error(`Checkpoint not found: ${checkpointId}`);\n }\n\n const entries = db\n .prepare(\n `SELECT seq, entry_kind, role, payload_json\n FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n }>;\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n for (const entry of entries) {\n const payload = JSON.parse(entry.payload_json) as TranscriptStoredRow;\n insertEntry(db, {\n sessionId,\n sessionKey,\n row: payload,\n createdAt: Date.now(),\n });\n }\n\n const llm = buildSessionContextForLlm(entries.map((e) => JSON.parse(e.payload_json) as TranscriptStoredRow));\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transcript-repository.js","names":[],"sources":["../../../../src/storage/sqlite/transcript-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CompactionCheckpointDetail, CompactionCheckpointSummary } from '../../session/types.js';\nimport type { TranscriptCompactionRecord } from '../../session/transcript-format.js';\nimport {\n buildSessionContextForLlm,\n type TranscriptStoredRow,\n} from '../../session/session-context-for-llm.js';\nimport {\n classifyStoredRow,\n estimateTokensFromMessages,\n extractFtsContent,\n transcriptEntryRowToStoredRow,\n type TranscriptEntryRow,\n} from './row-mappers.js';\nimport { getCurrentSessionId, readCurrentSessionId } from './session-repository.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst MAX_CHECKPOINTS_PER_TRANSCRIPT = 15;\n\nfunction nextSeq(db: DatabaseSync, sessionId: string): number {\n const row = db\n .prepare(`SELECT COALESCE(MAX(seq), 0) AS max_seq FROM transcript_entries WHERE session_id = ?`)\n .get(sessionId) as { max_seq: number };\n return (row.max_seq ?? 0) + 1;\n}\n\nfunction insertEntry(\n db: DatabaseSync,\n params: {\n sessionId: string;\n sessionKey: string;\n row: TranscriptStoredRow;\n entryId?: string;\n createdAt?: number;\n },\n): TranscriptEntryRow {\n const { entryKind, role } = classifyStoredRow(params.row);\n const entryId = params.entryId ?? randomUUID();\n const seq = nextSeq(db, params.sessionId);\n const createdAt = params.createdAt ?? Date.now();\n const payloadJson = JSON.stringify(params.row);\n\n db.prepare(\n `INSERT INTO transcript_entries (entry_id, session_id, seq, entry_kind, role, payload_json, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(entryId, params.sessionId, seq, entryKind, role, payloadJson, createdAt);\n\n const content = extractFtsContent(params.row);\n if (content.trim()) {\n db.prepare(\n `INSERT INTO transcript_fts (content, session_key, session_id, entry_id)\n VALUES (?, ?, ?, ?)`,\n ).run(content, params.sessionKey, params.sessionId, entryId);\n }\n\n return {\n entry_id: entryId,\n session_id: params.sessionId,\n seq,\n entry_kind: entryKind,\n role,\n payload_json: payloadJson,\n created_at: createdAt,\n };\n}\n\nfunction isUserMessageRow(row: TranscriptStoredRow): boolean {\n const classified = classifyStoredRow(row);\n return classified.entryKind === 'message' && classified.role === 'user';\n}\n\nexport function appendTranscriptEntry(\n sessionKey: string,\n row: TranscriptStoredRow,\n opts?: { sessionId?: string; tokenDelta?: number },\n): TranscriptEntryRow {\n return runSqliteWriteTransaction((db) => {\n const sessionId = opts?.sessionId ?? readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n const inserted = insertEntry(db, { sessionId, sessionKey, row });\n if (classifyStoredRow(row).entryKind === 'message') {\n const tokenDelta = opts?.tokenDelta ?? 0;\n const now = Date.now();\n const hiddenUpdate = isUserMessageRow(row) ? `hidden_from_session_list = 0,` : '';\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n ${hiddenUpdate}\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n }\n return inserted;\n });\n}\n\nexport function loadTranscriptRowsForSession(sessionKey: string): TranscriptStoredRow[] {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return [];\n }\n return loadTranscriptRows(sessionId);\n}\n\nexport function loadTranscriptRows(sessionId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ?\n ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n return rows.map(transcriptEntryRowToStoredRow);\n}\n\nexport function loadLlmMessagesForSession(sessionKey: string): AgentMessage[] {\n const rows = loadTranscriptRowsForSession(sessionKey);\n return buildSessionContextForLlm(rows);\n}\n\nexport function replaceTranscriptRows(\n sessionKey: string,\n rows: TranscriptStoredRow[],\n opts?: { appendCompaction?: TranscriptCompactionRecord },\n): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n const toWrite = [...rows];\n if (opts?.appendCompaction) {\n toWrite.push({\n type: 'compaction',\n ...opts.appendCompaction,\n } as unknown as TranscriptStoredRow);\n }\n\n for (const row of toWrite) {\n insertEntry(db, { sessionId, sessionKey, row });\n }\n\n const llm = buildSessionContextForLlm(toWrite);\n const now = Date.now();\n const hasUserMessage = llm.some((message) => message.role === 'user');\n const hiddenUpdate = hasUserMessage ? `hidden_from_session_list = 0,` : '';\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n ${hiddenUpdate}\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n\nexport function loadCheckpointRows(sessionKey: string, checkpointId: string): TranscriptStoredRow[] {\n const db = getSqliteDatabase();\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n return [];\n }\n const entries = db\n .prepare(\n `SELECT payload_json FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{ payload_json: string }>;\n return entries.map((entry) => JSON.parse(entry.payload_json) as TranscriptStoredRow);\n}\n\nexport function paginateTranscriptMessages(\n sessionKey: string,\n options: {\n offset?: number;\n limit?: number;\n beforeSeq?: number;\n /** 0-based exclusive end index (legacy cursor from gateway UI). */\n beforeEndIndex?: number;\n includeContext?: boolean;\n } = {},\n): {\n rows: TranscriptStoredRow[];\n messages: AgentMessage[];\n total: number;\n startSeq: number;\n endSeq: number;\n} {\n const sessionId = getCurrentSessionId(sessionKey);\n if (!sessionId) {\n return { rows: [], messages: [], total: 0, startSeq: 0, endSeq: 0 };\n }\n\n const db = getSqliteDatabase();\n const kinds = options.includeContext ? ['message', 'context'] : ['message'];\n const kindPlaceholders = kinds.map(() => '?').join(', ');\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) AS total FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})`,\n )\n .get(sessionId, ...kinds) as { total: number };\n const total = countRow.total;\n\n const limit = Math.min(200, Math.max(1, Math.trunc(options.limit ?? 50)));\n const offset = Math.max(0, Math.trunc(options.offset ?? 0));\n\n let rows: TranscriptEntryRow[];\n if (options.beforeEndIndex !== undefined && Number.isFinite(options.beforeEndIndex)) {\n const endExclusive = Math.min(total, Math.max(0, Math.trunc(options.beforeEndIndex)));\n const startInclusive = Math.max(0, endExclusive - limit);\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM (\n SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at,\n ROW_NUMBER() OVER (ORDER BY seq ASC) - 1 AS idx\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n )\n WHERE idx >= ? AND idx < ?\n ORDER BY seq ASC`,\n )\n .all(sessionId, ...kinds, startInclusive, endExclusive) as TranscriptEntryRow[];\n } else if (options.beforeSeq !== undefined && Number.isFinite(options.beforeSeq)) {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders}) AND seq < ?\n ORDER BY seq DESC\n LIMIT ?`,\n )\n .all(sessionId, ...kinds, options.beforeSeq, limit) as TranscriptEntryRow[];\n rows.reverse();\n } else {\n rows = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries\n WHERE session_id = ? AND entry_kind IN (${kindPlaceholders})\n ORDER BY seq DESC\n LIMIT ? OFFSET ?`,\n )\n .all(sessionId, ...kinds, limit, offset) as TranscriptEntryRow[];\n rows.reverse();\n }\n\n const storedRows = rows.map(transcriptEntryRowToStoredRow);\n const messages = buildSessionContextForLlm(storedRows);\n const startSeq = rows[0]?.seq ?? 0;\n const endSeq = rows[rows.length - 1]?.seq ?? 0;\n return { rows: storedRows, messages, total, startSeq, endSeq };\n}\n\nexport function captureCompactionCheckpoint(sessionKey: string): string | null {\n return runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n return null;\n }\n\n const entries = db\n .prepare(\n `SELECT entry_id, session_id, seq, entry_kind, role, payload_json, created_at\n FROM transcript_entries WHERE session_id = ? ORDER BY seq ASC`,\n )\n .all(sessionId) as TranscriptEntryRow[];\n if (entries.length === 0) {\n return null;\n }\n\n const checkpointId = randomUUID();\n const now = Date.now();\n const messageCount = entries.filter((e) => e.entry_kind === 'message').length;\n const sizeBytes = entries.reduce((sum, e) => sum + e.payload_json.length, 0);\n\n db.prepare(\n `INSERT INTO compaction_checkpoints\n (checkpoint_id, session_id, session_key, created_at, message_count, size_bytes)\n VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(checkpointId, sessionId, sessionKey, now, messageCount, sizeBytes);\n\n const insertCheckpointEntry = db.prepare(\n `INSERT INTO checkpoint_entries (checkpoint_id, seq, entry_kind, role, payload_json)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const entry of entries) {\n insertCheckpointEntry.run(\n checkpointId,\n entry.seq,\n entry.entry_kind,\n entry.role,\n entry.payload_json,\n );\n }\n\n pruneCompactionCheckpoints(db, sessionId);\n return checkpointId;\n });\n}\n\nfunction pruneCompactionCheckpoints(db: DatabaseSync, sessionId: string): void {\n const rows = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_id = ?\n ORDER BY created_at ASC`,\n )\n .all(sessionId) as Array<{ checkpoint_id: string }>;\n if (rows.length <= MAX_CHECKPOINTS_PER_TRANSCRIPT) {\n return;\n }\n const toDelete = rows.slice(0, rows.length - MAX_CHECKPOINTS_PER_TRANSCRIPT);\n const del = db.prepare(`DELETE FROM compaction_checkpoints WHERE checkpoint_id = ?`);\n for (const row of toDelete) {\n del.run(row.checkpoint_id);\n }\n}\n\nexport function listCompactionCheckpoints(sessionKey: string): CompactionCheckpointSummary[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ?\n ORDER BY created_at DESC`,\n )\n .all(sessionKey) as Array<{\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }>;\n\n return rows.map((row) => ({\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n }));\n}\n\nexport function getCompactionCheckpointDetail(\n sessionKey: string,\n checkpointId: string,\n): CompactionCheckpointDetail | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(\n `SELECT checkpoint_id, created_at, message_count, size_bytes\n FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as\n | {\n checkpoint_id: string;\n created_at: number;\n message_count: number;\n size_bytes: number;\n }\n | undefined;\n if (!row) {\n return null;\n }\n return {\n id: row.checkpoint_id,\n sizeBytes: row.size_bytes,\n modifiedAt: new Date(row.created_at).toISOString(),\n messageCount: row.message_count,\n };\n}\n\nexport function restoreCompactionCheckpoint(sessionKey: string, checkpointId: string): void {\n runSqliteWriteTransaction((db) => {\n const sessionId = readCurrentSessionId(db, sessionKey);\n if (!sessionId) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const checkpoint = db\n .prepare(\n `SELECT checkpoint_id FROM compaction_checkpoints\n WHERE session_key = ? AND checkpoint_id = ?`,\n )\n .get(sessionKey, checkpointId) as { checkpoint_id: string } | undefined;\n if (!checkpoint) {\n throw new Error(`Checkpoint not found: ${checkpointId}`);\n }\n\n const entries = db\n .prepare(\n `SELECT seq, entry_kind, role, payload_json\n FROM checkpoint_entries\n WHERE checkpoint_id = ?\n ORDER BY seq ASC`,\n )\n .all(checkpointId) as Array<{\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n }>;\n\n db.prepare(`DELETE FROM transcript_fts WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM transcript_entries WHERE session_id = ?`).run(sessionId);\n\n for (const entry of entries) {\n const payload = JSON.parse(entry.payload_json) as TranscriptStoredRow;\n insertEntry(db, {\n sessionId,\n sessionKey,\n row: payload,\n createdAt: Date.now(),\n });\n }\n\n const llm = buildSessionContextForLlm(entries.map((e) => JSON.parse(e.payload_json) as TranscriptStoredRow));\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(llm.length, estimateTokensFromMessages(llm), now, now, now, sessionKey);\n });\n}\n"],"mappings":"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"}
|
|
@@ -4,9 +4,9 @@ import { init_agent_session_key, resolveAgentIdFromSessionKey } from "../../rout
|
|
|
4
4
|
import { createLogger } from "../../utils/logger/index.js";
|
|
5
5
|
import { init_logger } from "../../utils/logger.js";
|
|
6
6
|
import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
7
|
-
import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
|
|
8
7
|
import { openXopcDatabase } from "../../storage/sqlite/connection.js";
|
|
9
8
|
import "../../storage/sqlite/index.js";
|
|
9
|
+
import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
|
|
10
10
|
import { evictEmbeddedSessionRunner } from "../../agent/embedded/session-runner.js";
|
|
11
11
|
import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
|
|
12
12
|
import { SessionIndex } from "../../session/manager.js";
|
|
@@ -3,10 +3,18 @@ import { init_agent_session_key, isValidAgentId, normalizeAgentId } from "../rou
|
|
|
3
3
|
import { resolveConfigPath } from "../config/paths.js";
|
|
4
4
|
import { loadConfig } from "../config/loader.js";
|
|
5
5
|
import { init_providers, providerSupportsOAuth } from "../providers/index.js";
|
|
6
|
+
import { closeXopcDatabase, isXopcDatabaseOpen, openXopcDatabase } from "../storage/sqlite/connection.js";
|
|
7
|
+
import { GoalService } from "../goals/goal-service.js";
|
|
8
|
+
import "../goals/index.js";
|
|
9
|
+
import { getSessionMetadata } from "../storage/sqlite/session-repository.js";
|
|
10
|
+
import "../storage/sqlite/index.js";
|
|
6
11
|
import { createWorkflowCatalog } from "../agent/workflow/catalog.js";
|
|
7
12
|
import { normalizeReasoningLevel, normalizeThinkLevel, normalizeVerboseLevel } from "../agent/transcript/thinking-types.js";
|
|
8
13
|
import { provenanceTracker } from "../extensions/security.js";
|
|
9
14
|
import "../config/index.js";
|
|
15
|
+
import { ProjectService } from "../projects/project-service.js";
|
|
16
|
+
import { isValidProjectAgentId, normalizeProjectAgentId } from "../projects/project-agent.js";
|
|
17
|
+
import "../projects/index.js";
|
|
10
18
|
import { getTuiKeybindingsPath } from "./tui-keybindings-file.js";
|
|
11
19
|
import { formatKeyIds, formatXopcTuiHotkeys } from "./format-tui-hotkeys.js";
|
|
12
20
|
import { formatTuiConfigInfo, formatTuiContextInfo, formatTuiLoginInfo, formatTuiModelsInfo, formatTuiSessionInfo, formatTuiSessionListInfo, formatTuiSessionTreeInfo, formatTuiStoredAuthProfilesInfo, formatTuiTranscriptTreeInfo, formatTuiTrustInfo, formatTuiUsageInfo, formatTuiWorkflowDetail, formatTuiWorkflowsInfo, loadTuiChangelogInfo, nextVerboseLevel, parseTuiExportRequest, parseTuiImportRequest, parseTuiShareRequest, writeTuiDebugInfo } from "./tui-command-formatters.js";
|
|
@@ -91,6 +99,10 @@ function getSlashCommands(_isLocal, keybindings) {
|
|
|
91
99
|
name: "session",
|
|
92
100
|
description: "Show current session info"
|
|
93
101
|
},
|
|
102
|
+
{
|
|
103
|
+
name: "project",
|
|
104
|
+
description: "Manage current project context"
|
|
105
|
+
},
|
|
94
106
|
{
|
|
95
107
|
name: "agent",
|
|
96
108
|
description: "Show or switch agent for this TUI session"
|
|
@@ -346,6 +358,130 @@ function formatCurrentAgent(state) {
|
|
|
346
358
|
if (model) lines.push(`Model: ${provider ? `${provider}/${model}` : model}`);
|
|
347
359
|
return lines.join("\n");
|
|
348
360
|
}
|
|
361
|
+
function withTuiProjects(fn) {
|
|
362
|
+
const wasOpen = isXopcDatabaseOpen();
|
|
363
|
+
if (!wasOpen) openXopcDatabase();
|
|
364
|
+
try {
|
|
365
|
+
return fn(new ProjectService());
|
|
366
|
+
} finally {
|
|
367
|
+
if (!wasOpen) closeXopcDatabase();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function resolveTuiProject(projects, ref) {
|
|
371
|
+
return projects.get(ref) ?? projects.getBySlug(ref);
|
|
372
|
+
}
|
|
373
|
+
function parseTuiProjectStatus(raw) {
|
|
374
|
+
return raw === "active" || raw === "paused" || raw === "archived" ? raw : void 0;
|
|
375
|
+
}
|
|
376
|
+
function formatTuiProject(project) {
|
|
377
|
+
return [
|
|
378
|
+
`${project.name} [${project.status}]`,
|
|
379
|
+
`ID: ${project.id}`,
|
|
380
|
+
`Slug: ${project.slug}`,
|
|
381
|
+
`Default agent: ${project.defaultAgentId ?? "global default"}`,
|
|
382
|
+
project.workspaceRoot ? `Workspace: ${project.workspaceRoot}` : void 0,
|
|
383
|
+
project.sessionCount != null ? `Sessions: ${project.sessionCount}` : void 0,
|
|
384
|
+
project.goalCount != null ? `Goals: ${project.goalCount} (${project.activeGoalCount ?? 0} active)` : void 0,
|
|
385
|
+
project.brief ? `Brief: ${project.brief}` : void 0
|
|
386
|
+
].filter(Boolean).join("\n");
|
|
387
|
+
}
|
|
388
|
+
function runTuiProjectCommand(state, args) {
|
|
389
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
390
|
+
const subcommand = parts[0]?.toLowerCase();
|
|
391
|
+
return withTuiProjects((projects) => {
|
|
392
|
+
if (!subcommand || subcommand === "status") {
|
|
393
|
+
const projectId = getSessionMetadata(state.currentSessionKey)?.projectId;
|
|
394
|
+
if (!projectId) return `No project selected.\nSession: ${state.currentSessionKey}`;
|
|
395
|
+
const project = projects.getWithDetails(projectId);
|
|
396
|
+
return project ? formatTuiProject(project) : `Current project not found: ${projectId}`;
|
|
397
|
+
}
|
|
398
|
+
if (subcommand === "list") {
|
|
399
|
+
const status = parseTuiProjectStatus(parts[1]);
|
|
400
|
+
const result = projects.list({
|
|
401
|
+
...status ? { status } : {},
|
|
402
|
+
limit: 20
|
|
403
|
+
});
|
|
404
|
+
if (!result.items.length) return "No projects.";
|
|
405
|
+
return ["Projects:", ...result.items.map((project) => `- ${project.slug} [${project.status}] ${project.name}`)].join("\n");
|
|
406
|
+
}
|
|
407
|
+
if (subcommand === "new") {
|
|
408
|
+
const name = parts.slice(1).join(" ").trim();
|
|
409
|
+
if (!name) return "Usage: /project new <name>";
|
|
410
|
+
const project = projects.create({ name });
|
|
411
|
+
projects.attachSession(state.currentSessionKey, project.id);
|
|
412
|
+
return `Created and attached project:\n${formatTuiProject(project)}`;
|
|
413
|
+
}
|
|
414
|
+
if (subcommand === "switch" || subcommand === "attach") {
|
|
415
|
+
const ref = parts[1];
|
|
416
|
+
if (!ref) return `Usage: /project ${subcommand} <id-or-slug>`;
|
|
417
|
+
const project = resolveTuiProject(projects, ref);
|
|
418
|
+
if (!project) return `Project not found: ${ref}`;
|
|
419
|
+
projects.attachSession(state.currentSessionKey, project.id);
|
|
420
|
+
return `Attached current session to project: ${project.name}`;
|
|
421
|
+
}
|
|
422
|
+
if (subcommand === "detach") {
|
|
423
|
+
projects.detachSession(state.currentSessionKey);
|
|
424
|
+
return "Detached current session from project.";
|
|
425
|
+
}
|
|
426
|
+
if (subcommand === "archive") {
|
|
427
|
+
const ref = parts[1];
|
|
428
|
+
if (!ref) return "Usage: /project archive <id-or-slug>";
|
|
429
|
+
const project = resolveTuiProject(projects, ref);
|
|
430
|
+
if (!project) return `Project not found: ${ref}`;
|
|
431
|
+
projects.update(project.id, { status: "archived" });
|
|
432
|
+
return `Archived project: ${project.name}`;
|
|
433
|
+
}
|
|
434
|
+
if (subcommand === "set-agent") {
|
|
435
|
+
const agentId = normalizeProjectAgentId(parts[1]);
|
|
436
|
+
if (!agentId) return "Usage: /project set-agent <agent-id>";
|
|
437
|
+
const currentProjectId = getSessionMetadata(state.currentSessionKey)?.projectId;
|
|
438
|
+
if (!currentProjectId) return "No current project.";
|
|
439
|
+
if (!isValidProjectAgentId(loadConfig(resolveConfigPath()), agentId)) return `Agent not found: ${parts[1]}`;
|
|
440
|
+
return `Project default agent set to ${projects.update(currentProjectId, { defaultAgentId: agentId }).defaultAgentId}.`;
|
|
441
|
+
}
|
|
442
|
+
if (subcommand === "clear-agent") {
|
|
443
|
+
const currentProjectId = getSessionMetadata(state.currentSessionKey)?.projectId;
|
|
444
|
+
if (!currentProjectId) return "No current project.";
|
|
445
|
+
projects.update(currentProjectId, { defaultAgentId: null });
|
|
446
|
+
return "Project default agent cleared.";
|
|
447
|
+
}
|
|
448
|
+
if (subcommand === "sessions") {
|
|
449
|
+
const ref = parts[1];
|
|
450
|
+
const currentProjectId = getSessionMetadata(state.currentSessionKey)?.projectId;
|
|
451
|
+
const project = ref ? resolveTuiProject(projects, ref) : currentProjectId ? projects.get(currentProjectId) : null;
|
|
452
|
+
if (!project) return ref ? `Project not found: ${ref}` : "No current project.";
|
|
453
|
+
const keys = projects.listSessionKeys(project.id, 20);
|
|
454
|
+
if (!keys.length) return `No sessions in ${project.name}.`;
|
|
455
|
+
return [`Sessions in ${project.name}:`, ...keys.map((key) => `- ${key}`)].join("\n");
|
|
456
|
+
}
|
|
457
|
+
if (subcommand === "goals") {
|
|
458
|
+
const ref = parts[1];
|
|
459
|
+
const currentProjectId = getSessionMetadata(state.currentSessionKey)?.projectId;
|
|
460
|
+
const project = ref ? resolveTuiProject(projects, ref) : currentProjectId ? projects.get(currentProjectId) : null;
|
|
461
|
+
if (!project) return ref ? `Project not found: ${ref}` : "No current project.";
|
|
462
|
+
const goals = new GoalService().list({
|
|
463
|
+
projectId: project.id,
|
|
464
|
+
limit: 20
|
|
465
|
+
});
|
|
466
|
+
if (!goals.length) return `No goals in ${project.name}.`;
|
|
467
|
+
return [`Goals in ${project.name}:`, ...goals.map((goal) => `- ${goal.id} [${goal.status}] ${goal.title}`)].join("\n");
|
|
468
|
+
}
|
|
469
|
+
return [
|
|
470
|
+
"Usage:",
|
|
471
|
+
" /project",
|
|
472
|
+
" /project list [active|paused|archived]",
|
|
473
|
+
" /project new <name>",
|
|
474
|
+
" /project switch <id-or-slug>",
|
|
475
|
+
" /project attach <id-or-slug>",
|
|
476
|
+
" /project detach",
|
|
477
|
+
" /project set-agent <agent-id>",
|
|
478
|
+
" /project clear-agent",
|
|
479
|
+
" /project sessions [id-or-slug]",
|
|
480
|
+
" /project goals [id-or-slug]",
|
|
481
|
+
" /project archive <id-or-slug>"
|
|
482
|
+
].join("\n");
|
|
483
|
+
});
|
|
484
|
+
}
|
|
349
485
|
function createTuiCommandHandler(deps) {
|
|
350
486
|
const { state, chatLog, tui, isLocalMode, abortActive, sendMessage, requestExit, updateFooter, keybindings, uiOverlays, skillSlashCommands = [], workflowSlashCommands = [], extensionSlashCommands = [], extensionShortcuts = [], setSession, resetSession } = deps;
|
|
351
487
|
const runReset = async () => {
|
|
@@ -403,6 +539,15 @@ function createTuiCommandHandler(deps) {
|
|
|
403
539
|
tui.requestRender();
|
|
404
540
|
});
|
|
405
541
|
return;
|
|
542
|
+
case "project":
|
|
543
|
+
try {
|
|
544
|
+
chatLog.addSystem(runTuiProjectCommand(state, commandArgs));
|
|
545
|
+
} catch (err) {
|
|
546
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
547
|
+
chatLog.addSystem(`Project command failed: ${errorMessage}`);
|
|
548
|
+
}
|
|
549
|
+
tui.requestRender();
|
|
550
|
+
return;
|
|
406
551
|
case "agents":
|
|
407
552
|
if (uiOverlays?.openAgentPicker) {
|
|
408
553
|
uiOverlays.openAgentPicker();
|