@xopcai/xopc 0.0.121 → 0.0.123
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-Bicr3ZBg.js} +1 -1
- package/dist/gateway/static/root/assets/agents-CxDSFHGx.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CE6GkGU2.js → app-management-settings-panel-CbEy9sR1.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BmaUjghw.js → appearance-settings-CTHja6n9.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-CyZdgqvm.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CX4_KWut.js → archive-plugin-Bpy64GjU.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-jf-PIIUU.js → attachment-load-DMkp4xSb.js} +1 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-KnYaP7NT.js +1 -0
- package/dist/gateway/static/root/assets/automations-page-8VLKM4au.js +1 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DG9XQikd.js → binary-plugins-DA9C5mCw.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Up_VIxLJ.js → block-editor-CiSXk_YC.js} +6 -6
- package/dist/gateway/static/root/assets/browser-settings-page-5tPg0DcP.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-CGM4OyMw.js → capability-presets-api-CEJ44WCU.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-CzWt0LBb.js → capability-presets-settings-panel-BJW3aVST.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-BEByL4_o.js → channel-recipient-api-CDhrhfhN.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-CorUCTrs.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-VKstC3sP.js +2 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-7jKfbH0J.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-m1-wmz4o.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-DZxa_j7-.js → extension-debug-page-C9qQQrBS.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CseziW53.js → extension-page-DcBBMEGl.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-B_9J-xyo.js → extension-settings-page-BQ-jMMP9.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-Cs4ck8zA.js → fetch-Dtpbbe0s.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-Dl223a7r.js → gateway-config-api-BN6pyX2c.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-hQtgVmSR.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-IvePwjFE.js → gateway-startup-retry-BIET2x3y.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-BuIMDx6C.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-FEypCq74.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-CUCP6yCl.js → goals-config-api-CQm2r1Ty.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-C4-2_MLG.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-Dj87qPxv.js → goals-settings-DDrdRMxB.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-BJVok3Sl.js +1 -0
- package/dist/gateway/static/root/assets/index-CBDr5xjB.css +1 -0
- package/dist/gateway/static/root/assets/index-T0SaNd5a.js +191 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DJgYw0nB.js → keyboard-shortcuts-settings-C2TuQppb.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DwkwPgid.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-gnQ_EiGo.js → markdown-split-DhONQ81k.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-CQV-mgJn.js → media-plugins-DS44pJzr.js} +1 -1
- package/dist/gateway/static/root/assets/messages-avfvyZuQ.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-huNY4A1G.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-DdfGuEZ1.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-B0bT9Hqa.js +2 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BPMXC-2u.js → preview-open-alternatives-Dej6zHnr.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CjxhPorG.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-C4Z5OJMF.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CVCOPUVd.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BEHqwZjz.js → save-bar-controls-Zu1FzH6z.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CMY25m97.js → schedule-field-BW78mKc0.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CxjxsFfg.js → schema-form-Cc-IOydE.js} +1 -1
- package/dist/gateway/static/root/assets/segmented-styles-BL8oLBEp.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-B4qExXHC.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-Ce8yLTBi.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-s-kiLAxP.js → settings-collapsible-section-DIpIYht_.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-CIxIEYxU.js → settings-form-section-BFxBNxOG.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-C4fivemT.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-layout-CLx4o2PN.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-Kyc4eUf0.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-ZswWCrDu.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-Bu76CMiC.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-D-CFg86j.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-CUU4f6Ss.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-Czjf0kLJ.js → system-settings-panel-3ZNcGRhS.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-Cmt8SYdI.js → typed-models-lib-bpzZjiVF.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-DyM-ckEQ.js → use-channel-catalog-BCjZLZ3O.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-BygLv8vm.js → user-profile-settings-panel-CIXafITi.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-CloW8coB.js → utils-DXMtz5j_.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-C1MrQD8R.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-DqizIcY5.js +3 -0
- package/dist/gateway/static/root/assets/workflow-start-dialog-B4sZpTDQ.js +1 -0
- package/dist/gateway/static/root/assets/workflows-page-B3sVNPFk.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/tools/factory.js +1 -1
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/goal-tool.d.ts +5 -1
- package/dist/src/agent/tools/goal-tool.js +8 -2
- package/dist/src/agent/tools/goal-tool.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +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/builtins/goal.js +4 -1
- package/dist/src/chat-commands/builtins/goal.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/index.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 +662 -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/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 +12 -0
- package/dist/src/projects/index.js +12 -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 +40 -0
- package/dist/src/projects/project-service.js +187 -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 +62 -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-project.d.ts +22 -0
- package/dist/src/projects/workspace-project.js +143 -0
- package/dist/src/projects/workspace-project.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.d.ts +7 -1
- package/dist/src/tui/backends/embedded-backend.js +41 -2
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +7 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +22 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +16 -0
- package/dist/src/tui/tui-commands.js +145 -0
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui.js +21 -0
- package/dist/src/tui/tui.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":"memory-records-repository.js","names":[],"sources":["../../../../src/storage/sqlite/memory-records-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type {\n MemoryKind,\n MemoryRecord,\n MemorySensitivity,\n MemorySearchResult,\n MemorySignal,\n MemoryStatus,\n} from '../../agent/memory/types.js';\nimport { escapeFts5Query } from './fts.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype MemoryRecordRow = {\n record_id: string;\n provider_id: string;\n kind: string;\n agent_id: string;\n workspace_id: string | null;\n session_key: string | null;\n content: string;\n source_json: string;\n confidence: number | null;\n tags_json: string;\n status: string;\n sensitivity: string;\n evidence_json: string;\n review_after: number | null;\n expires_at: number | null;\n created_at: number;\n updated_at: number;\n};\n\ntype MemoryRecordSearchRow = MemoryRecordRow & {\n rank: number;\n};\n\nexport interface UpsertMemoryRecordInput {\n id?: string;\n providerId: string;\n kind: MemoryKind;\n agentId: string;\n workspaceId?: string;\n sessionKey?: string;\n content: string;\n source?: MemoryRecord['source'];\n confidence?: number;\n tags?: string[];\n status?: MemoryStatus;\n sensitivity?: MemorySensitivity;\n evidence?: MemoryRecord['evidence'];\n reviewAfter?: string;\n expiresAt?: string;\n nowMs?: number;\n}\n\nexport interface ListMemoryRecordsOptions {\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n kind?: MemoryKind;\n status?: MemoryStatus;\n limit?: number;\n offset?: number;\n}\n\nexport interface SearchMemoryRecordsOptions {\n query: string;\n agentId?: string;\n workspaceId?: string;\n providerId?: string;\n kinds?: MemoryKind[];\n statuses?: MemoryStatus[];\n maxResults?: number;\n minScore?: number;\n}\n\nexport interface AppendMemorySignalInput {\n signal: MemorySignal;\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n sessionKey?: string;\n nowMs?: number;\n}\n\nexport interface MemorySignalRowPayload {\n signalId: string;\n source: string;\n recordId?: string;\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n sessionKey?: string;\n score?: number;\n content?: string;\n metadata: Record<string, unknown>;\n createdAt: string;\n}\n\ntype MemorySignalRow = {\n signal_id: string;\n source: string;\n record_id: string | null;\n provider_id: string | null;\n agent_id: string | null;\n workspace_id: string | null;\n session_key: string | null;\n score: number | null;\n content: string | null;\n metadata_json: string;\n created_at: number;\n};\n\ntype MemoryTraceRow = {\n trace_id: string;\n session_key: string | null;\n turn_id: string | null;\n phase: string;\n provider_id: string;\n request_json: string;\n result_count: number | null;\n selected_record_ids_json: string;\n skipped_reason: string | null;\n error: string | null;\n feedback_json: string;\n duration_ms: number;\n created_at: number;\n};\n\nexport type MemoryTraceFeedbackOutcome = 'helpful' | 'not_helpful' | 'mixed' | 'irrelevant';\n\nexport interface MemoryTraceFeedback {\n outcome: MemoryTraceFeedbackOutcome;\n score?: number;\n reason?: string;\n source?: 'user' | 'evaluator' | 'system';\n createdAt: string;\n updatedAt?: string;\n}\n\nexport interface AppendMemoryTraceEventInput {\n sessionKey?: string;\n turnId?: string;\n phase: 'search' | 'read' | 'write' | 'update' | 'delete' | 'sync' | 'inject' | 'test';\n providerId: string;\n request?: unknown;\n resultCount?: number;\n selectedRecordIds?: string[];\n skippedReason?: string;\n error?: string;\n feedback?: MemoryTraceFeedback;\n durationMs?: number;\n nowMs?: number;\n}\n\nexport interface SetMemoryTraceFeedbackInput {\n traceId: string;\n feedback: Omit<MemoryTraceFeedback, 'createdAt' | 'updatedAt'> & {\n createdAt?: string;\n updatedAt?: string;\n };\n nowMs?: number;\n}\n\nexport interface MemoryTraceEventPayload {\n traceId: string;\n sessionKey?: string;\n turnId?: string;\n phase: string;\n providerId: string;\n request: unknown;\n resultCount?: number;\n selectedRecordIds: string[];\n skippedReason?: string;\n error?: string;\n feedback?: MemoryTraceFeedback;\n durationMs: number;\n createdAt: string;\n}\n\nexport interface MemoryRecallFeedbackSummary {\n recordId: string;\n helpful: number;\n notHelpful: number;\n mixed: number;\n irrelevant: number;\n total: number;\n averageScore: number | null;\n lastFeedbackAt?: string;\n}\n\nfunction parseStringArray(json: string): string[] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((v): v is string => typeof v === 'string') : [];\n } catch {\n return [];\n }\n}\n\nfunction parseEvidence(json: string): MemoryRecord['evidence'] {\n try {\n const parsed = JSON.parse(json) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter((item): item is NonNullable<MemoryRecord['evidence']>[number] =>\n Boolean(item) && typeof item === 'object',\n );\n } catch {\n return [];\n }\n}\n\nfunction parseMemoryTraceFeedback(json: string): MemoryTraceFeedback | undefined {\n try {\n const parsed = JSON.parse(json) as unknown;\n if (!parsed || typeof parsed !== 'object') return undefined;\n const obj = parsed as Record<string, unknown>;\n const outcome = obj.outcome;\n if (\n outcome !== 'helpful' &&\n outcome !== 'not_helpful' &&\n outcome !== 'mixed' &&\n outcome !== 'irrelevant'\n ) {\n return undefined;\n }\n return {\n outcome,\n ...(typeof obj.score === 'number' ? { score: obj.score } : {}),\n ...(typeof obj.reason === 'string' ? { reason: obj.reason } : {}),\n ...(obj.source === 'user' || obj.source === 'evaluator' || obj.source === 'system'\n ? { source: obj.source }\n : {}),\n createdAt: typeof obj.createdAt === 'string' ? obj.createdAt : new Date(0).toISOString(),\n ...(typeof obj.updatedAt === 'string' ? { updatedAt: obj.updatedAt } : {}),\n };\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeMemoryTraceFeedback(\n input: SetMemoryTraceFeedbackInput['feedback'],\n nowMs = Date.now(),\n existing?: MemoryTraceFeedback,\n): MemoryTraceFeedback {\n const score = typeof input.score === 'number'\n ? Math.max(-1, Math.min(1, input.score))\n : undefined;\n const reason = typeof input.reason === 'string' && input.reason.trim()\n ? input.reason.trim().slice(0, 1000)\n : undefined;\n const nowIso = new Date(nowMs).toISOString();\n return {\n outcome: input.outcome,\n ...(score != null ? { score } : {}),\n ...(reason ? { reason } : {}),\n ...(input.source ? { source: input.source } : {}),\n createdAt: input.createdAt ?? existing?.createdAt ?? nowIso,\n updatedAt: input.updatedAt ?? nowIso,\n };\n}\n\nfunction memoryTraceRowToPayload(row: MemoryTraceRow): MemoryTraceEventPayload {\n const feedback = parseMemoryTraceFeedback(row.feedback_json);\n return {\n traceId: row.trace_id,\n ...(row.session_key ? { sessionKey: row.session_key } : {}),\n ...(row.turn_id ? { turnId: row.turn_id } : {}),\n phase: row.phase,\n providerId: row.provider_id,\n request: parseJsonValue(row.request_json, {}),\n ...(row.result_count != null ? { resultCount: row.result_count } : {}),\n selectedRecordIds: parseStringArray(row.selected_record_ids_json),\n ...(row.skipped_reason ? { skippedReason: row.skipped_reason } : {}),\n ...(row.error ? { error: row.error } : {}),\n ...(feedback ? { feedback } : {}),\n durationMs: row.duration_ms,\n createdAt: new Date(row.created_at).toISOString(),\n };\n}\n\nfunction parseOptionalTimestamp(value: string | undefined): number | null {\n if (!value?.trim()) return null;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction timestampToIso(value: number | null): string | undefined {\n return value == null ? undefined : new Date(value).toISOString();\n}\n\nfunction parseSource(json: string): MemoryRecord['source'] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return parsed && typeof parsed === 'object' ? (parsed as MemoryRecord['source']) : {};\n } catch {\n return {};\n }\n}\n\nfunction rowToRecord(row: MemoryRecordRow): MemoryRecord {\n return {\n id: row.record_id,\n kind: row.kind as MemoryKind,\n status: row.status as MemoryStatus,\n scope: {\n agentId: row.agent_id,\n ...(row.workspace_id ? { workspaceId: row.workspace_id } : {}),\n ...(row.session_key ? { sessionKey: row.session_key } : {}),\n },\n content: row.content,\n source: parseSource(row.source_json),\n ...(row.confidence != null ? { confidence: row.confidence } : {}),\n sensitivity: row.sensitivity as MemorySensitivity,\n evidence: parseEvidence(row.evidence_json),\n ...(timestampToIso(row.review_after) ? { reviewAfter: timestampToIso(row.review_after) } : {}),\n ...(timestampToIso(row.expires_at) ? { expiresAt: timestampToIso(row.expires_at) } : {}),\n createdAt: new Date(row.created_at).toISOString(),\n updatedAt: new Date(row.updated_at).toISOString(),\n tags: parseStringArray(row.tags_json),\n };\n}\n\nfunction bm25ToScore(rank: number): number {\n return Math.max(0, Math.min(1, 1 / (1 + Math.max(0, rank))));\n}\n\nfunction upsertMemoryRecordFts(\n db: ReturnType<typeof getSqliteDatabase>,\n row: Pick<UpsertMemoryRecordInput, 'providerId' | 'kind' | 'agentId' | 'workspaceId' | 'content'> & { id: string },\n): void {\n db.prepare(`DELETE FROM memory_records_fts WHERE record_id = ?`).run(row.id);\n if (!row.content.trim()) return;\n db.prepare(\n `INSERT INTO memory_records_fts (\n content, record_id, provider_id, kind, agent_id, workspace_id\n ) VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(row.content, row.id, row.providerId, row.kind, row.agentId, row.workspaceId ?? null);\n}\n\nexport function upsertMemoryRecord(input: UpsertMemoryRecordInput): MemoryRecord {\n const id = input.id ?? randomUUID();\n const now = input.nowMs ?? Date.now();\n const status = input.status ?? 'active';\n const sensitivity = input.sensitivity ?? 'normal';\n const reviewAfter = parseOptionalTimestamp(input.reviewAfter);\n const expiresAt = parseOptionalTimestamp(input.expiresAt);\n const source = {\n ...(input.source ?? {}),\n provider: input.source?.provider ?? input.providerId,\n };\n\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_records (\n record_id, provider_id, kind, agent_id, workspace_id, session_key,\n content, source_json, confidence, tags_json, status, sensitivity,\n evidence_json, review_after, expires_at, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(record_id) DO UPDATE SET\n provider_id = excluded.provider_id,\n kind = excluded.kind,\n agent_id = excluded.agent_id,\n workspace_id = excluded.workspace_id,\n session_key = excluded.session_key,\n content = excluded.content,\n source_json = excluded.source_json,\n confidence = excluded.confidence,\n tags_json = excluded.tags_json,\n status = excluded.status,\n sensitivity = excluded.sensitivity,\n evidence_json = excluded.evidence_json,\n review_after = excluded.review_after,\n expires_at = excluded.expires_at,\n updated_at = excluded.updated_at`,\n ).run(\n id,\n input.providerId,\n input.kind,\n input.agentId,\n input.workspaceId ?? null,\n input.sessionKey ?? null,\n input.content,\n JSON.stringify(source),\n input.confidence ?? null,\n JSON.stringify(input.tags ?? []),\n status,\n sensitivity,\n JSON.stringify(input.evidence ?? []),\n reviewAfter,\n expiresAt,\n now,\n now,\n );\n upsertMemoryRecordFts(db, { ...input, id });\n });\n\n return {\n id,\n kind: input.kind,\n status,\n scope: {\n agentId: input.agentId,\n ...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),\n ...(input.sessionKey ? { sessionKey: input.sessionKey } : {}),\n },\n content: input.content,\n source,\n ...(input.confidence != null ? { confidence: input.confidence } : {}),\n sensitivity,\n evidence: input.evidence ?? [],\n ...(reviewAfter != null ? { reviewAfter: new Date(reviewAfter).toISOString() } : {}),\n ...(expiresAt != null ? { expiresAt: new Date(expiresAt).toISOString() } : {}),\n createdAt: new Date(now).toISOString(),\n updatedAt: new Date(now).toISOString(),\n tags: input.tags ?? [],\n };\n}\n\nexport function getMemoryRecord(recordId: string): MemoryRecord | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM memory_records WHERE record_id = ?`)\n .get(recordId) as MemoryRecordRow | undefined;\n return row ? rowToRecord(row) : null;\n}\n\nexport function listMemoryRecords(options: ListMemoryRecordsOptions = {}): MemoryRecord[] {\n const where: string[] = [];\n const params: Array<string | number | null> = [];\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.agentId) {\n where.push('agent_id = ?');\n params.push(options.agentId);\n }\n if (options.workspaceId) {\n where.push('workspace_id = ?');\n params.push(options.workspaceId);\n }\n if (options.kind) {\n where.push('kind = ?');\n params.push(options.kind);\n }\n if (options.status) {\n where.push('status = ?');\n params.push(options.status);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const offset = Math.max(0, options.offset ?? 0);\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_records\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY updated_at DESC\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as MemoryRecordRow[];\n return rows.map(rowToRecord);\n}\n\nexport function searchMemoryRecords(options: SearchMemoryRecordsOptions): MemorySearchResult[] {\n const query = escapeFts5Query(options.query);\n if (!query) return [];\n\n const filters: string[] = ['memory_records_fts MATCH ?'];\n const params: Array<string | number | null> = [query];\n if (options.agentId) {\n filters.push('f.agent_id = ?');\n params.push(options.agentId);\n }\n if (options.workspaceId) {\n filters.push('f.workspace_id = ?');\n params.push(options.workspaceId);\n }\n if (options.providerId) {\n filters.push('f.provider_id = ?');\n params.push(options.providerId);\n }\n if (options.kinds && options.kinds.length > 0) {\n filters.push(`f.kind IN (${options.kinds.map(() => '?').join(', ')})`);\n params.push(...options.kinds);\n }\n const statuses = options.statuses && options.statuses.length > 0 ? options.statuses : ['active'];\n filters.push(`r.status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n\n const maxResults = Math.max(1, Math.min(50, options.maxResults ?? 5));\n const minScore = options.minScore ?? 0;\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT\n r.*,\n bm25(memory_records_fts) AS rank\n FROM memory_records_fts f\n JOIN memory_records r ON r.record_id = f.record_id\n WHERE ${filters.join(' AND ')}\n ORDER BY rank\n LIMIT ?`,\n )\n .all(...params, maxResults * 3) as MemoryRecordSearchRow[];\n\n return rows\n .map((row) => {\n const record = rowToRecord(row);\n const score = bm25ToScore(row.rank);\n return {\n record,\n score,\n snippet: record.content,\n citation: {\n providerId: row.provider_id,\n recordId: row.record_id,\n path: record.source.path,\n lineStart: record.source.lineStart,\n lineEnd: record.source.lineEnd,\n createdAt: record.createdAt,\n },\n };\n })\n .filter((result) => result.score >= minScore)\n .slice(0, maxResults);\n}\n\nexport function deleteMemoryRecord(recordId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n db.prepare(`DELETE FROM memory_records_fts WHERE record_id = ?`).run(recordId);\n const result = db.prepare(`DELETE FROM memory_records WHERE record_id = ?`).run(recordId);\n return result.changes > 0;\n });\n}\n\nexport function appendMemorySignal(input: AppendMemorySignalInput): string {\n const id = randomUUID();\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_signals (\n signal_id, source, record_id, provider_id, agent_id, workspace_id,\n session_key, score, content, metadata_json, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n id,\n input.signal.source,\n input.signal.recordId ?? null,\n input.providerId ?? null,\n input.agentId ?? null,\n input.workspaceId ?? null,\n input.sessionKey ?? null,\n input.signal.score ?? null,\n input.signal.content ?? null,\n JSON.stringify(input.signal.metadata ?? {}),\n now,\n );\n if (input.signal.recordId) {\n db.prepare(\n `UPDATE memory_records\n SET recall_count = recall_count + 1, last_recalled_at = ?, updated_at = MAX(updated_at, ?)\n WHERE record_id = ?`,\n ).run(now, now, input.signal.recordId);\n }\n });\n return id;\n}\n\nexport function listMemorySignals(options: {\n recordId?: string;\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n limit?: number;\n} = {}): MemorySignalRowPayload[] {\n const where: string[] = [];\n const params: Array<string | number | null> = [];\n if (options.recordId) {\n where.push('record_id = ?');\n params.push(options.recordId);\n }\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.agentId) {\n where.push('agent_id = ?');\n params.push(options.agentId);\n }\n if (options.workspaceId) {\n where.push('workspace_id = ?');\n params.push(options.workspaceId);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_signals\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY created_at DESC\n LIMIT ?`,\n )\n .all(...params, limit) as MemorySignalRow[];\n return rows.map((row) => {\n let metadata: Record<string, unknown> = {};\n try {\n const parsed = JSON.parse(row.metadata_json) as unknown;\n if (parsed && typeof parsed === 'object') metadata = parsed as Record<string, unknown>;\n } catch {\n metadata = {};\n }\n return {\n signalId: row.signal_id,\n source: row.source,\n ...(row.record_id ? { recordId: row.record_id } : {}),\n ...(row.provider_id ? { providerId: row.provider_id } : {}),\n ...(row.agent_id ? { agentId: row.agent_id } : {}),\n ...(row.workspace_id ? { workspaceId: row.workspace_id } : {}),\n ...(row.session_key ? { sessionKey: row.session_key } : {}),\n ...(row.score != null ? { score: row.score } : {}),\n ...(row.content ? { content: row.content } : {}),\n metadata,\n createdAt: new Date(row.created_at).toISOString(),\n };\n });\n}\n\nexport function getMemoryProviderState(providerId: string, scopeKey: string): unknown | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT state_json FROM memory_provider_state WHERE provider_id = ? AND scope_key = ?`)\n .get(providerId, scopeKey) as { state_json: string } | undefined;\n if (!row) return null;\n try {\n return JSON.parse(row.state_json) as unknown;\n } catch {\n return null;\n }\n}\n\nexport function setMemoryProviderState(providerId: string, scopeKey: string, state: unknown): void {\n const now = Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_provider_state (provider_id, scope_key, state_json, updated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(provider_id, scope_key) DO UPDATE SET\n state_json = excluded.state_json,\n updated_at = excluded.updated_at`,\n ).run(providerId, scopeKey, JSON.stringify(state ?? null), now);\n });\n}\n\nexport function appendMemoryTraceEvent(input: AppendMemoryTraceEventInput): string {\n const id = randomUUID();\n const now = input.nowMs ?? Date.now();\n const feedback = input.feedback\n ? normalizeMemoryTraceFeedback(input.feedback, now)\n : undefined;\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_trace_events (\n trace_id, session_key, turn_id, phase, provider_id, request_json,\n result_count, selected_record_ids_json, skipped_reason, error, feedback_json, duration_ms, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n id,\n input.sessionKey ?? null,\n input.turnId ?? null,\n input.phase,\n input.providerId,\n JSON.stringify(input.request ?? {}),\n input.resultCount ?? null,\n JSON.stringify(input.selectedRecordIds ?? []),\n input.skippedReason ?? null,\n input.error ?? null,\n JSON.stringify(feedback ?? {}),\n Math.max(0, Math.floor(input.durationMs ?? 0)),\n now,\n );\n });\n return id;\n}\n\nexport function listMemoryTraceEvents(options: {\n providerId?: string;\n sessionKey?: string;\n phase?: string;\n limit?: number;\n} = {}): MemoryTraceEventPayload[] {\n const where: string[] = [];\n const params: Array<string | number | null> = [];\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.sessionKey) {\n where.push('session_key = ?');\n params.push(options.sessionKey);\n }\n if (options.phase) {\n where.push('phase = ?');\n params.push(options.phase);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_trace_events\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY created_at DESC\n LIMIT ?`,\n )\n .all(...params, limit) as MemoryTraceRow[];\n return rows.map(memoryTraceRowToPayload);\n}\n\nexport function setMemoryTraceFeedback(input: SetMemoryTraceFeedbackInput): MemoryTraceEventPayload | null {\n const existing = getSqliteDatabase()\n .prepare(`SELECT * FROM memory_trace_events WHERE trace_id = ?`)\n .get(input.traceId) as MemoryTraceRow | undefined;\n if (!existing) {\n return null;\n }\n const feedback = normalizeMemoryTraceFeedback(\n input.feedback,\n input.nowMs,\n parseMemoryTraceFeedback(existing.feedback_json),\n );\n runSqliteWriteTransaction((db) => {\n db.prepare(`UPDATE memory_trace_events SET feedback_json = ? WHERE trace_id = ?`)\n .run(JSON.stringify(feedback), input.traceId);\n });\n return memoryTraceRowToPayload({ ...existing, feedback_json: JSON.stringify(feedback) });\n}\n\nexport function summarizeMemoryRecallFeedback(options: {\n recordId?: string;\n providerId?: string;\n sessionKey?: string;\n limit?: number;\n} = {}): MemoryRecallFeedbackSummary[] {\n const where = ['phase IN (?, ?)'];\n const params: Array<string | number> = ['search', 'inject'];\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.sessionKey) {\n where.push('session_key = ?');\n params.push(options.sessionKey);\n }\n const limit = Math.max(1, Math.min(5000, options.limit ?? 1000));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_trace_events\n WHERE ${where.join(' AND ')}\n ORDER BY created_at DESC\n LIMIT ?`,\n )\n .all(...params, limit) as MemoryTraceRow[];\n\n const summaries = new Map<string, MemoryRecallFeedbackSummary & { scoreTotal: number; scoreCount: number }>();\n for (const row of rows) {\n const feedback = parseMemoryTraceFeedback(row.feedback_json);\n if (!feedback) continue;\n const recordIds = parseStringArray(row.selected_record_ids_json);\n for (const recordId of recordIds) {\n if (options.recordId && recordId !== options.recordId) continue;\n const current = summaries.get(recordId) ?? {\n recordId,\n helpful: 0,\n notHelpful: 0,\n mixed: 0,\n irrelevant: 0,\n total: 0,\n averageScore: null,\n scoreTotal: 0,\n scoreCount: 0,\n };\n if (feedback.outcome === 'helpful') current.helpful += 1;\n if (feedback.outcome === 'not_helpful') current.notHelpful += 1;\n if (feedback.outcome === 'mixed') current.mixed += 1;\n if (feedback.outcome === 'irrelevant') current.irrelevant += 1;\n current.total += 1;\n if (typeof feedback.score === 'number') {\n current.scoreTotal += feedback.score;\n current.scoreCount += 1;\n current.averageScore = current.scoreTotal / current.scoreCount;\n }\n current.lastFeedbackAt ??= feedback.updatedAt ?? feedback.createdAt;\n summaries.set(recordId, current);\n }\n }\n\n return [...summaries.values()]\n .map(({ scoreTotal: _scoreTotal, scoreCount: _scoreCount, ...summary }) => summary)\n .sort((a, b) => b.total - a.total);\n}\n\nfunction parseJsonValue(json: string, fallback: unknown): unknown {\n try {\n return JSON.parse(json) as unknown;\n } catch {\n return fallback;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory-records-repository.js","names":[],"sources":["../../../../src/storage/sqlite/memory-records-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type {\n MemoryKind,\n MemoryRecord,\n MemorySensitivity,\n MemorySearchResult,\n MemorySignal,\n MemoryStatus,\n} from '../../agent/memory/types.js';\nimport { escapeFts5Query } from './fts.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype MemoryRecordRow = {\n record_id: string;\n provider_id: string;\n kind: string;\n agent_id: string;\n workspace_id: string | null;\n session_key: string | null;\n project_id: string | null;\n content: string;\n source_json: string;\n confidence: number | null;\n tags_json: string;\n status: string;\n sensitivity: string;\n evidence_json: string;\n review_after: number | null;\n expires_at: number | null;\n created_at: number;\n updated_at: number;\n};\n\ntype MemoryRecordSearchRow = MemoryRecordRow & {\n rank: number;\n};\n\nexport interface UpsertMemoryRecordInput {\n id?: string;\n providerId: string;\n kind: MemoryKind;\n agentId: string;\n workspaceId?: string;\n sessionKey?: string;\n projectId?: string;\n content: string;\n source?: MemoryRecord['source'];\n confidence?: number;\n tags?: string[];\n status?: MemoryStatus;\n sensitivity?: MemorySensitivity;\n evidence?: MemoryRecord['evidence'];\n reviewAfter?: string;\n expiresAt?: string;\n nowMs?: number;\n}\n\nexport interface ListMemoryRecordsOptions {\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n projectId?: string;\n kind?: MemoryKind;\n status?: MemoryStatus;\n limit?: number;\n offset?: number;\n}\n\nexport interface SearchMemoryRecordsOptions {\n query: string;\n agentId?: string;\n workspaceId?: string;\n projectId?: string;\n providerId?: string;\n kinds?: MemoryKind[];\n statuses?: MemoryStatus[];\n maxResults?: number;\n minScore?: number;\n}\n\nexport interface AppendMemorySignalInput {\n signal: MemorySignal;\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n sessionKey?: string;\n nowMs?: number;\n}\n\nexport interface MemorySignalRowPayload {\n signalId: string;\n source: string;\n recordId?: string;\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n sessionKey?: string;\n score?: number;\n content?: string;\n metadata: Record<string, unknown>;\n createdAt: string;\n}\n\ntype MemorySignalRow = {\n signal_id: string;\n source: string;\n record_id: string | null;\n provider_id: string | null;\n agent_id: string | null;\n workspace_id: string | null;\n session_key: string | null;\n score: number | null;\n content: string | null;\n metadata_json: string;\n created_at: number;\n};\n\ntype MemoryTraceRow = {\n trace_id: string;\n session_key: string | null;\n turn_id: string | null;\n phase: string;\n provider_id: string;\n request_json: string;\n result_count: number | null;\n selected_record_ids_json: string;\n skipped_reason: string | null;\n error: string | null;\n feedback_json: string;\n duration_ms: number;\n created_at: number;\n};\n\nexport type MemoryTraceFeedbackOutcome = 'helpful' | 'not_helpful' | 'mixed' | 'irrelevant';\n\nexport interface MemoryTraceFeedback {\n outcome: MemoryTraceFeedbackOutcome;\n score?: number;\n reason?: string;\n source?: 'user' | 'evaluator' | 'system';\n createdAt: string;\n updatedAt?: string;\n}\n\nexport interface AppendMemoryTraceEventInput {\n sessionKey?: string;\n turnId?: string;\n phase: 'search' | 'read' | 'write' | 'update' | 'delete' | 'sync' | 'inject' | 'test';\n providerId: string;\n request?: unknown;\n resultCount?: number;\n selectedRecordIds?: string[];\n skippedReason?: string;\n error?: string;\n feedback?: MemoryTraceFeedback;\n durationMs?: number;\n nowMs?: number;\n}\n\nexport interface SetMemoryTraceFeedbackInput {\n traceId: string;\n feedback: Omit<MemoryTraceFeedback, 'createdAt' | 'updatedAt'> & {\n createdAt?: string;\n updatedAt?: string;\n };\n nowMs?: number;\n}\n\nexport interface MemoryTraceEventPayload {\n traceId: string;\n sessionKey?: string;\n turnId?: string;\n phase: string;\n providerId: string;\n request: unknown;\n resultCount?: number;\n selectedRecordIds: string[];\n skippedReason?: string;\n error?: string;\n feedback?: MemoryTraceFeedback;\n durationMs: number;\n createdAt: string;\n}\n\nexport interface MemoryRecallFeedbackSummary {\n recordId: string;\n helpful: number;\n notHelpful: number;\n mixed: number;\n irrelevant: number;\n total: number;\n averageScore: number | null;\n lastFeedbackAt?: string;\n}\n\nfunction parseStringArray(json: string): string[] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((v): v is string => typeof v === 'string') : [];\n } catch {\n return [];\n }\n}\n\nfunction parseEvidence(json: string): MemoryRecord['evidence'] {\n try {\n const parsed = JSON.parse(json) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter((item): item is NonNullable<MemoryRecord['evidence']>[number] =>\n Boolean(item) && typeof item === 'object',\n );\n } catch {\n return [];\n }\n}\n\nfunction parseMemoryTraceFeedback(json: string): MemoryTraceFeedback | undefined {\n try {\n const parsed = JSON.parse(json) as unknown;\n if (!parsed || typeof parsed !== 'object') return undefined;\n const obj = parsed as Record<string, unknown>;\n const outcome = obj.outcome;\n if (\n outcome !== 'helpful' &&\n outcome !== 'not_helpful' &&\n outcome !== 'mixed' &&\n outcome !== 'irrelevant'\n ) {\n return undefined;\n }\n return {\n outcome,\n ...(typeof obj.score === 'number' ? { score: obj.score } : {}),\n ...(typeof obj.reason === 'string' ? { reason: obj.reason } : {}),\n ...(obj.source === 'user' || obj.source === 'evaluator' || obj.source === 'system'\n ? { source: obj.source }\n : {}),\n createdAt: typeof obj.createdAt === 'string' ? obj.createdAt : new Date(0).toISOString(),\n ...(typeof obj.updatedAt === 'string' ? { updatedAt: obj.updatedAt } : {}),\n };\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeMemoryTraceFeedback(\n input: SetMemoryTraceFeedbackInput['feedback'],\n nowMs = Date.now(),\n existing?: MemoryTraceFeedback,\n): MemoryTraceFeedback {\n const score = typeof input.score === 'number'\n ? Math.max(-1, Math.min(1, input.score))\n : undefined;\n const reason = typeof input.reason === 'string' && input.reason.trim()\n ? input.reason.trim().slice(0, 1000)\n : undefined;\n const nowIso = new Date(nowMs).toISOString();\n return {\n outcome: input.outcome,\n ...(score != null ? { score } : {}),\n ...(reason ? { reason } : {}),\n ...(input.source ? { source: input.source } : {}),\n createdAt: input.createdAt ?? existing?.createdAt ?? nowIso,\n updatedAt: input.updatedAt ?? nowIso,\n };\n}\n\nfunction memoryTraceRowToPayload(row: MemoryTraceRow): MemoryTraceEventPayload {\n const feedback = parseMemoryTraceFeedback(row.feedback_json);\n return {\n traceId: row.trace_id,\n ...(row.session_key ? { sessionKey: row.session_key } : {}),\n ...(row.turn_id ? { turnId: row.turn_id } : {}),\n phase: row.phase,\n providerId: row.provider_id,\n request: parseJsonValue(row.request_json, {}),\n ...(row.result_count != null ? { resultCount: row.result_count } : {}),\n selectedRecordIds: parseStringArray(row.selected_record_ids_json),\n ...(row.skipped_reason ? { skippedReason: row.skipped_reason } : {}),\n ...(row.error ? { error: row.error } : {}),\n ...(feedback ? { feedback } : {}),\n durationMs: row.duration_ms,\n createdAt: new Date(row.created_at).toISOString(),\n };\n}\n\nfunction parseOptionalTimestamp(value: string | undefined): number | null {\n if (!value?.trim()) return null;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction timestampToIso(value: number | null): string | undefined {\n return value == null ? undefined : new Date(value).toISOString();\n}\n\nfunction parseSource(json: string): MemoryRecord['source'] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return parsed && typeof parsed === 'object' ? (parsed as MemoryRecord['source']) : {};\n } catch {\n return {};\n }\n}\n\nfunction rowToRecord(row: MemoryRecordRow): MemoryRecord {\n return {\n id: row.record_id,\n kind: row.kind as MemoryKind,\n status: row.status as MemoryStatus,\n scope: {\n agentId: row.agent_id,\n ...(row.workspace_id ? { workspaceId: row.workspace_id } : {}),\n ...(row.session_key ? { sessionKey: row.session_key } : {}),\n ...(row.project_id ? { projectId: row.project_id } : {}),\n },\n content: row.content,\n source: parseSource(row.source_json),\n ...(row.confidence != null ? { confidence: row.confidence } : {}),\n sensitivity: row.sensitivity as MemorySensitivity,\n evidence: parseEvidence(row.evidence_json),\n ...(timestampToIso(row.review_after) ? { reviewAfter: timestampToIso(row.review_after) } : {}),\n ...(timestampToIso(row.expires_at) ? { expiresAt: timestampToIso(row.expires_at) } : {}),\n createdAt: new Date(row.created_at).toISOString(),\n updatedAt: new Date(row.updated_at).toISOString(),\n tags: parseStringArray(row.tags_json),\n };\n}\n\nfunction bm25ToScore(rank: number): number {\n return Math.max(0, Math.min(1, 1 / (1 + Math.max(0, rank))));\n}\n\nfunction upsertMemoryRecordFts(\n db: ReturnType<typeof getSqliteDatabase>,\n row: Pick<UpsertMemoryRecordInput, 'providerId' | 'kind' | 'agentId' | 'workspaceId' | 'content'> & { id: string },\n): void {\n db.prepare(`DELETE FROM memory_records_fts WHERE record_id = ?`).run(row.id);\n if (!row.content.trim()) return;\n db.prepare(\n `INSERT INTO memory_records_fts (\n content, record_id, provider_id, kind, agent_id, workspace_id\n ) VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(row.content, row.id, row.providerId, row.kind, row.agentId, row.workspaceId ?? null);\n}\n\nexport function upsertMemoryRecord(input: UpsertMemoryRecordInput): MemoryRecord {\n const id = input.id ?? randomUUID();\n const now = input.nowMs ?? Date.now();\n const status = input.status ?? 'active';\n const sensitivity = input.sensitivity ?? 'normal';\n const reviewAfter = parseOptionalTimestamp(input.reviewAfter);\n const expiresAt = parseOptionalTimestamp(input.expiresAt);\n const source = {\n ...(input.source ?? {}),\n provider: input.source?.provider ?? input.providerId,\n };\n\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_records (\n record_id, provider_id, kind, agent_id, workspace_id, session_key, project_id,\n content, source_json, confidence, tags_json, status, sensitivity,\n evidence_json, review_after, expires_at, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(record_id) DO UPDATE SET\n provider_id = excluded.provider_id,\n kind = excluded.kind,\n agent_id = excluded.agent_id,\n workspace_id = excluded.workspace_id,\n session_key = excluded.session_key,\n project_id = excluded.project_id,\n content = excluded.content,\n source_json = excluded.source_json,\n confidence = excluded.confidence,\n tags_json = excluded.tags_json,\n status = excluded.status,\n sensitivity = excluded.sensitivity,\n evidence_json = excluded.evidence_json,\n review_after = excluded.review_after,\n expires_at = excluded.expires_at,\n updated_at = excluded.updated_at`,\n ).run(\n id,\n input.providerId,\n input.kind,\n input.agentId,\n input.workspaceId ?? null,\n input.sessionKey ?? null,\n input.projectId ?? null,\n input.content,\n JSON.stringify(source),\n input.confidence ?? null,\n JSON.stringify(input.tags ?? []),\n status,\n sensitivity,\n JSON.stringify(input.evidence ?? []),\n reviewAfter,\n expiresAt,\n now,\n now,\n );\n upsertMemoryRecordFts(db, { ...input, id });\n });\n\n return {\n id,\n kind: input.kind,\n status,\n scope: {\n agentId: input.agentId,\n ...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),\n ...(input.sessionKey ? { sessionKey: input.sessionKey } : {}),\n ...(input.projectId ? { projectId: input.projectId } : {}),\n },\n content: input.content,\n source,\n ...(input.confidence != null ? { confidence: input.confidence } : {}),\n sensitivity,\n evidence: input.evidence ?? [],\n ...(reviewAfter != null ? { reviewAfter: new Date(reviewAfter).toISOString() } : {}),\n ...(expiresAt != null ? { expiresAt: new Date(expiresAt).toISOString() } : {}),\n createdAt: new Date(now).toISOString(),\n updatedAt: new Date(now).toISOString(),\n tags: input.tags ?? [],\n };\n}\n\nexport function getMemoryRecord(recordId: string): MemoryRecord | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM memory_records WHERE record_id = ?`)\n .get(recordId) as MemoryRecordRow | undefined;\n return row ? rowToRecord(row) : null;\n}\n\nexport function listMemoryRecords(options: ListMemoryRecordsOptions = {}): MemoryRecord[] {\n const where: string[] = [];\n const params: Array<string | number | null> = [];\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.agentId) {\n where.push('agent_id = ?');\n params.push(options.agentId);\n }\n if (options.workspaceId) {\n where.push('workspace_id = ?');\n params.push(options.workspaceId);\n }\n if (options.projectId) {\n where.push('project_id = ?');\n params.push(options.projectId);\n }\n if (options.kind) {\n where.push('kind = ?');\n params.push(options.kind);\n }\n if (options.status) {\n where.push('status = ?');\n params.push(options.status);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const offset = Math.max(0, options.offset ?? 0);\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_records\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY updated_at DESC\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as MemoryRecordRow[];\n return rows.map(rowToRecord);\n}\n\nexport function searchMemoryRecords(options: SearchMemoryRecordsOptions): MemorySearchResult[] {\n const query = escapeFts5Query(options.query);\n if (!query) return [];\n\n const filters: string[] = ['memory_records_fts MATCH ?'];\n const params: Array<string | number | null> = [query];\n if (options.agentId) {\n filters.push('f.agent_id = ?');\n params.push(options.agentId);\n }\n if (options.workspaceId) {\n filters.push('f.workspace_id = ?');\n params.push(options.workspaceId);\n }\n if (options.projectId) {\n filters.push('r.project_id = ?');\n params.push(options.projectId);\n }\n if (options.providerId) {\n filters.push('f.provider_id = ?');\n params.push(options.providerId);\n }\n if (options.kinds && options.kinds.length > 0) {\n filters.push(`f.kind IN (${options.kinds.map(() => '?').join(', ')})`);\n params.push(...options.kinds);\n }\n const statuses = options.statuses && options.statuses.length > 0 ? options.statuses : ['active'];\n filters.push(`r.status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n\n const maxResults = Math.max(1, Math.min(50, options.maxResults ?? 5));\n const minScore = options.minScore ?? 0;\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT\n r.*,\n bm25(memory_records_fts) AS rank\n FROM memory_records_fts f\n JOIN memory_records r ON r.record_id = f.record_id\n WHERE ${filters.join(' AND ')}\n ORDER BY rank\n LIMIT ?`,\n )\n .all(...params, maxResults * 3) as MemoryRecordSearchRow[];\n\n return rows\n .map((row) => {\n const record = rowToRecord(row);\n const score = bm25ToScore(row.rank);\n return {\n record,\n score,\n snippet: record.content,\n citation: {\n providerId: row.provider_id,\n recordId: row.record_id,\n path: record.source.path,\n lineStart: record.source.lineStart,\n lineEnd: record.source.lineEnd,\n createdAt: record.createdAt,\n },\n };\n })\n .filter((result) => result.score >= minScore)\n .slice(0, maxResults);\n}\n\nexport function deleteMemoryRecord(recordId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n db.prepare(`DELETE FROM memory_records_fts WHERE record_id = ?`).run(recordId);\n const result = db.prepare(`DELETE FROM memory_records WHERE record_id = ?`).run(recordId);\n return result.changes > 0;\n });\n}\n\nexport function appendMemorySignal(input: AppendMemorySignalInput): string {\n const id = randomUUID();\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_signals (\n signal_id, source, record_id, provider_id, agent_id, workspace_id,\n session_key, score, content, metadata_json, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n id,\n input.signal.source,\n input.signal.recordId ?? null,\n input.providerId ?? null,\n input.agentId ?? null,\n input.workspaceId ?? null,\n input.sessionKey ?? null,\n input.signal.score ?? null,\n input.signal.content ?? null,\n JSON.stringify(input.signal.metadata ?? {}),\n now,\n );\n if (input.signal.recordId) {\n db.prepare(\n `UPDATE memory_records\n SET recall_count = recall_count + 1, last_recalled_at = ?, updated_at = MAX(updated_at, ?)\n WHERE record_id = ?`,\n ).run(now, now, input.signal.recordId);\n }\n });\n return id;\n}\n\nexport function listMemorySignals(options: {\n recordId?: string;\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n limit?: number;\n} = {}): MemorySignalRowPayload[] {\n const where: string[] = [];\n const params: Array<string | number | null> = [];\n if (options.recordId) {\n where.push('record_id = ?');\n params.push(options.recordId);\n }\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.agentId) {\n where.push('agent_id = ?');\n params.push(options.agentId);\n }\n if (options.workspaceId) {\n where.push('workspace_id = ?');\n params.push(options.workspaceId);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_signals\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY created_at DESC\n LIMIT ?`,\n )\n .all(...params, limit) as MemorySignalRow[];\n return rows.map((row) => {\n let metadata: Record<string, unknown> = {};\n try {\n const parsed = JSON.parse(row.metadata_json) as unknown;\n if (parsed && typeof parsed === 'object') metadata = parsed as Record<string, unknown>;\n } catch {\n metadata = {};\n }\n return {\n signalId: row.signal_id,\n source: row.source,\n ...(row.record_id ? { recordId: row.record_id } : {}),\n ...(row.provider_id ? { providerId: row.provider_id } : {}),\n ...(row.agent_id ? { agentId: row.agent_id } : {}),\n ...(row.workspace_id ? { workspaceId: row.workspace_id } : {}),\n ...(row.session_key ? { sessionKey: row.session_key } : {}),\n ...(row.score != null ? { score: row.score } : {}),\n ...(row.content ? { content: row.content } : {}),\n metadata,\n createdAt: new Date(row.created_at).toISOString(),\n };\n });\n}\n\nexport function getMemoryProviderState(providerId: string, scopeKey: string): unknown | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT state_json FROM memory_provider_state WHERE provider_id = ? AND scope_key = ?`)\n .get(providerId, scopeKey) as { state_json: string } | undefined;\n if (!row) return null;\n try {\n return JSON.parse(row.state_json) as unknown;\n } catch {\n return null;\n }\n}\n\nexport function setMemoryProviderState(providerId: string, scopeKey: string, state: unknown): void {\n const now = Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_provider_state (provider_id, scope_key, state_json, updated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(provider_id, scope_key) DO UPDATE SET\n state_json = excluded.state_json,\n updated_at = excluded.updated_at`,\n ).run(providerId, scopeKey, JSON.stringify(state ?? null), now);\n });\n}\n\nexport function appendMemoryTraceEvent(input: AppendMemoryTraceEventInput): string {\n const id = randomUUID();\n const now = input.nowMs ?? Date.now();\n const feedback = input.feedback\n ? normalizeMemoryTraceFeedback(input.feedback, now)\n : undefined;\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_trace_events (\n trace_id, session_key, turn_id, phase, provider_id, request_json,\n result_count, selected_record_ids_json, skipped_reason, error, feedback_json, duration_ms, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n id,\n input.sessionKey ?? null,\n input.turnId ?? null,\n input.phase,\n input.providerId,\n JSON.stringify(input.request ?? {}),\n input.resultCount ?? null,\n JSON.stringify(input.selectedRecordIds ?? []),\n input.skippedReason ?? null,\n input.error ?? null,\n JSON.stringify(feedback ?? {}),\n Math.max(0, Math.floor(input.durationMs ?? 0)),\n now,\n );\n });\n return id;\n}\n\nexport function listMemoryTraceEvents(options: {\n providerId?: string;\n sessionKey?: string;\n phase?: string;\n limit?: number;\n} = {}): MemoryTraceEventPayload[] {\n const where: string[] = [];\n const params: Array<string | number | null> = [];\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.sessionKey) {\n where.push('session_key = ?');\n params.push(options.sessionKey);\n }\n if (options.phase) {\n where.push('phase = ?');\n params.push(options.phase);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_trace_events\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY created_at DESC\n LIMIT ?`,\n )\n .all(...params, limit) as MemoryTraceRow[];\n return rows.map(memoryTraceRowToPayload);\n}\n\nexport function setMemoryTraceFeedback(input: SetMemoryTraceFeedbackInput): MemoryTraceEventPayload | null {\n const existing = getSqliteDatabase()\n .prepare(`SELECT * FROM memory_trace_events WHERE trace_id = ?`)\n .get(input.traceId) as MemoryTraceRow | undefined;\n if (!existing) {\n return null;\n }\n const feedback = normalizeMemoryTraceFeedback(\n input.feedback,\n input.nowMs,\n parseMemoryTraceFeedback(existing.feedback_json),\n );\n runSqliteWriteTransaction((db) => {\n db.prepare(`UPDATE memory_trace_events SET feedback_json = ? WHERE trace_id = ?`)\n .run(JSON.stringify(feedback), input.traceId);\n });\n return memoryTraceRowToPayload({ ...existing, feedback_json: JSON.stringify(feedback) });\n}\n\nexport function summarizeMemoryRecallFeedback(options: {\n recordId?: string;\n providerId?: string;\n sessionKey?: string;\n limit?: number;\n} = {}): MemoryRecallFeedbackSummary[] {\n const where = ['phase IN (?, ?)'];\n const params: Array<string | number> = ['search', 'inject'];\n if (options.providerId) {\n where.push('provider_id = ?');\n params.push(options.providerId);\n }\n if (options.sessionKey) {\n where.push('session_key = ?');\n params.push(options.sessionKey);\n }\n const limit = Math.max(1, Math.min(5000, options.limit ?? 1000));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_trace_events\n WHERE ${where.join(' AND ')}\n ORDER BY created_at DESC\n LIMIT ?`,\n )\n .all(...params, limit) as MemoryTraceRow[];\n\n const summaries = new Map<string, MemoryRecallFeedbackSummary & { scoreTotal: number; scoreCount: number }>();\n for (const row of rows) {\n const feedback = parseMemoryTraceFeedback(row.feedback_json);\n if (!feedback) continue;\n const recordIds = parseStringArray(row.selected_record_ids_json);\n for (const recordId of recordIds) {\n if (options.recordId && recordId !== options.recordId) continue;\n const current = summaries.get(recordId) ?? {\n recordId,\n helpful: 0,\n notHelpful: 0,\n mixed: 0,\n irrelevant: 0,\n total: 0,\n averageScore: null,\n scoreTotal: 0,\n scoreCount: 0,\n };\n if (feedback.outcome === 'helpful') current.helpful += 1;\n if (feedback.outcome === 'not_helpful') current.notHelpful += 1;\n if (feedback.outcome === 'mixed') current.mixed += 1;\n if (feedback.outcome === 'irrelevant') current.irrelevant += 1;\n current.total += 1;\n if (typeof feedback.score === 'number') {\n current.scoreTotal += feedback.score;\n current.scoreCount += 1;\n current.averageScore = current.scoreTotal / current.scoreCount;\n }\n current.lastFeedbackAt ??= feedback.updatedAt ?? feedback.createdAt;\n summaries.set(recordId, current);\n }\n }\n\n return [...summaries.values()]\n .map(({ scoreTotal: _scoreTotal, scoreCount: _scoreCount, ...summary }) => summary)\n .sort((a, b) => b.total - a.total);\n}\n\nfunction parseJsonValue(json: string, fallback: unknown): unknown {\n try {\n return JSON.parse(json) as unknown;\n } catch {\n return fallback;\n }\n}\n"],"mappings":";;;;AAoMA,SAAS,iBAAiB,MAAwB;AAChD,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,SAAO,MAAM,QAAQ,OAAO,GAAG,OAAO,QAAQ,MAAmB,OAAO,MAAM,SAAS,GAAG,EAAE;SACtF;AACN,SAAO,EAAE;;;AAIb,SAAS,cAAc,MAAwC;AAC7D,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,MAAI,CAAC,MAAM,QAAQ,OAAO,CAAE,QAAO,EAAE;AACrC,SAAO,OAAO,QAAQ,SACpB,QAAQ,KAAK,IAAI,OAAO,SAAS,SAClC;SACK;AACN,SAAO,EAAE;;;AAIb,SAAS,yBAAyB,MAA+C;AAC/E,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO,KAAA;EAClD,MAAM,MAAM;EACZ,MAAM,UAAU,IAAI;AACpB,MACE,YAAY,aACZ,YAAY,iBACZ,YAAY,WACZ,YAAY,aAEZ;AAEF,SAAO;GACL;GACA,GAAI,OAAO,IAAI,UAAU,WAAW,EAAE,OAAO,IAAI,OAAO,GAAG,EAAE;GAC7D,GAAI,OAAO,IAAI,WAAW,WAAW,EAAE,QAAQ,IAAI,QAAQ,GAAG,EAAE;GAChE,GAAI,IAAI,WAAW,UAAU,IAAI,WAAW,eAAe,IAAI,WAAW,WACtE,EAAE,QAAQ,IAAI,QAAQ,GACtB,EAAE;GACN,WAAW,OAAO,IAAI,cAAc,WAAW,IAAI,6BAAY,IAAI,KAAK,EAAE,EAAC,aAAa;GACxF,GAAI,OAAO,IAAI,cAAc,WAAW,EAAE,WAAW,IAAI,WAAW,GAAG,EAAE;GAC1E;SACK;AACN;;;AAIJ,SAAS,6BACP,OACA,QAAQ,KAAK,KAAK,EAClB,UACqB;CACrB,MAAM,QAAQ,OAAO,MAAM,UAAU,WACjC,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,MAAM,CAAC,GACtC,KAAA;CACJ,MAAM,SAAS,OAAO,MAAM,WAAW,YAAY,MAAM,OAAO,MAAM,GAClE,MAAM,OAAO,MAAM,CAAC,MAAM,GAAG,IAAK,GAClC,KAAA;CACJ,MAAM,SAAS,IAAI,KAAK,MAAM,CAAC,aAAa;AAC5C,QAAO;EACL,SAAS,MAAM;EACf,GAAI,SAAS,OAAO,EAAE,OAAO,GAAG,EAAE;EAClC,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;EAC5B,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,QAAQ,GAAG,EAAE;EAChD,WAAW,MAAM,aAAa,UAAU,aAAa;EACrD,WAAW,MAAM,aAAa;EAC/B;;AAGH,SAAS,wBAAwB,KAA8C;CAC7E,MAAM,WAAW,yBAAyB,IAAI,cAAc;AAC5D,QAAO;EACL,SAAS,IAAI;EACb,GAAI,IAAI,cAAc,EAAE,YAAY,IAAI,aAAa,GAAG,EAAE;EAC1D,GAAI,IAAI,UAAU,EAAE,QAAQ,IAAI,SAAS,GAAG,EAAE;EAC9C,OAAO,IAAI;EACX,YAAY,IAAI;EAChB,SAAS,eAAe,IAAI,cAAc,EAAE,CAAC;EAC7C,GAAI,IAAI,gBAAgB,OAAO,EAAE,aAAa,IAAI,cAAc,GAAG,EAAE;EACrE,mBAAmB,iBAAiB,IAAI,yBAAyB;EACjE,GAAI,IAAI,iBAAiB,EAAE,eAAe,IAAI,gBAAgB,GAAG,EAAE;EACnE,GAAI,IAAI,QAAQ,EAAE,OAAO,IAAI,OAAO,GAAG,EAAE;EACzC,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;EAChC,YAAY,IAAI;EAChB,WAAW,IAAI,KAAK,IAAI,WAAW,CAAC,aAAa;EAClD;;AAGH,SAAS,uBAAuB,OAA0C;AACxE,KAAI,CAAC,OAAO,MAAM,CAAE,QAAO;CAC3B,MAAM,SAAS,KAAK,MAAM,MAAM;AAChC,QAAO,OAAO,SAAS,OAAO,GAAG,SAAS;;AAG5C,SAAS,eAAe,OAA0C;AAChE,QAAO,SAAS,OAAO,KAAA,IAAY,IAAI,KAAK,MAAM,CAAC,aAAa;;AAGlE,SAAS,YAAY,MAAsC;AACzD,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,SAAO,UAAU,OAAO,WAAW,WAAY,SAAoC,EAAE;SAC/E;AACN,SAAO,EAAE;;;AAIb,SAAS,YAAY,KAAoC;AACvD,QAAO;EACL,IAAI,IAAI;EACR,MAAM,IAAI;EACV,QAAQ,IAAI;EACZ,OAAO;GACL,SAAS,IAAI;GACb,GAAI,IAAI,eAAe,EAAE,aAAa,IAAI,cAAc,GAAG,EAAE;GAC7D,GAAI,IAAI,cAAc,EAAE,YAAY,IAAI,aAAa,GAAG,EAAE;GAC1D,GAAI,IAAI,aAAa,EAAE,WAAW,IAAI,YAAY,GAAG,EAAE;GACxD;EACD,SAAS,IAAI;EACb,QAAQ,YAAY,IAAI,YAAY;EACpC,GAAI,IAAI,cAAc,OAAO,EAAE,YAAY,IAAI,YAAY,GAAG,EAAE;EAChE,aAAa,IAAI;EACjB,UAAU,cAAc,IAAI,cAAc;EAC1C,GAAI,eAAe,IAAI,aAAa,GAAG,EAAE,aAAa,eAAe,IAAI,aAAa,EAAE,GAAG,EAAE;EAC7F,GAAI,eAAe,IAAI,WAAW,GAAG,EAAE,WAAW,eAAe,IAAI,WAAW,EAAE,GAAG,EAAE;EACvF,WAAW,IAAI,KAAK,IAAI,WAAW,CAAC,aAAa;EACjD,WAAW,IAAI,KAAK,IAAI,WAAW,CAAC,aAAa;EACjD,MAAM,iBAAiB,IAAI,UAAU;EACtC;;AAGH,SAAS,YAAY,MAAsB;AACzC,QAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,EAAE,CAAC;;AAG9D,SAAS,sBACP,IACA,KACM;AACN,IAAG,QAAQ,qDAAqD,CAAC,IAAI,IAAI,GAAG;AAC5E,KAAI,CAAC,IAAI,QAAQ,MAAM,CAAE;AACzB,IAAG,QACD;;iCAGD,CAAC,IAAI,IAAI,SAAS,IAAI,IAAI,IAAI,YAAY,IAAI,MAAM,IAAI,SAAS,IAAI,eAAe,KAAK;;AAG5F,SAAgB,mBAAmB,OAA8C;CAC/E,MAAM,KAAK,MAAM,MAAM,YAAY;CACnC,MAAM,MAAM,MAAM,SAAS,KAAK,KAAK;CACrC,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,cAAc,MAAM,eAAe;CACzC,MAAM,cAAc,uBAAuB,MAAM,YAAY;CAC7D,MAAM,YAAY,uBAAuB,MAAM,UAAU;CACzD,MAAM,SAAS;EACb,GAAI,MAAM,UAAU,EAAE;EACtB,UAAU,MAAM,QAAQ,YAAY,MAAM;EAC3C;AAED,4BAA2B,OAAO;AAChC,KAAG,QACD;;;;;;;;;;;;;;;;;;;;;0CAsBD,CAAC,IACA,IACA,MAAM,YACN,MAAM,MACN,MAAM,SACN,MAAM,eAAe,MACrB,MAAM,cAAc,MACpB,MAAM,aAAa,MACnB,MAAM,SACN,KAAK,UAAU,OAAO,EACtB,MAAM,cAAc,MACpB,KAAK,UAAU,MAAM,QAAQ,EAAE,CAAC,EAChC,QACA,aACA,KAAK,UAAU,MAAM,YAAY,EAAE,CAAC,EACpC,aACA,WACA,KACA,IACD;AACD,wBAAsB,IAAI;GAAE,GAAG;GAAO;GAAI,CAAC;GAC3C;AAEF,QAAO;EACL;EACA,MAAM,MAAM;EACZ;EACA,OAAO;GACL,SAAS,MAAM;GACf,GAAI,MAAM,cAAc,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC/D,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,YAAY,GAAG,EAAE;GAC5D,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,WAAW,GAAG,EAAE;GAC1D;EACD,SAAS,MAAM;EACf;EACA,GAAI,MAAM,cAAc,OAAO,EAAE,YAAY,MAAM,YAAY,GAAG,EAAE;EACpE;EACA,UAAU,MAAM,YAAY,EAAE;EAC9B,GAAI,eAAe,OAAO,EAAE,aAAa,IAAI,KAAK,YAAY,CAAC,aAAa,EAAE,GAAG,EAAE;EACnF,GAAI,aAAa,OAAO,EAAE,WAAW,IAAI,KAAK,UAAU,CAAC,aAAa,EAAE,GAAG,EAAE;EAC7E,WAAW,IAAI,KAAK,IAAI,CAAC,aAAa;EACtC,WAAW,IAAI,KAAK,IAAI,CAAC,aAAa;EACtC,MAAM,MAAM,QAAQ,EAAE;EACvB;;AAGH,SAAgB,gBAAgB,UAAuC;CACrE,MAAM,MAAM,mBAAmB,CAC5B,QAAQ,mDAAmD,CAC3D,IAAI,SAAS;AAChB,QAAO,MAAM,YAAY,IAAI,GAAG;;AAGlC,SAAgB,kBAAkB,UAAoC,EAAE,EAAkB;CACxF,MAAM,QAAkB,EAAE;CAC1B,MAAM,SAAwC,EAAE;AAChD,KAAI,QAAQ,YAAY;AACtB,QAAM,KAAK,kBAAkB;AAC7B,SAAO,KAAK,QAAQ,WAAW;;AAEjC,KAAI,QAAQ,SAAS;AACnB,QAAM,KAAK,eAAe;AAC1B,SAAO,KAAK,QAAQ,QAAQ;;AAE9B,KAAI,QAAQ,aAAa;AACvB,QAAM,KAAK,mBAAmB;AAC9B,SAAO,KAAK,QAAQ,YAAY;;AAElC,KAAI,QAAQ,WAAW;AACrB,QAAM,KAAK,iBAAiB;AAC5B,SAAO,KAAK,QAAQ,UAAU;;AAEhC,KAAI,QAAQ,MAAM;AAChB,QAAM,KAAK,WAAW;AACtB,SAAO,KAAK,QAAQ,KAAK;;AAE3B,KAAI,QAAQ,QAAQ;AAClB,QAAM,KAAK,aAAa;AACxB,SAAO,KAAK,QAAQ,OAAO;;CAE7B,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,QAAQ,SAAS,IAAI,CAAC;CAC9D,MAAM,SAAS,KAAK,IAAI,GAAG,QAAQ,UAAU,EAAE;AAS/C,QARa,mBAAmB,CAC7B,QACC;SACG,MAAM,SAAS,IAAI,SAAS,MAAM,KAAK,QAAQ,KAAK,GAAG;;yBAG3D,CACA,IAAI,GAAG,QAAQ,OAAO,OACd,CAAC,IAAI,YAAY;;AAG9B,SAAgB,oBAAoB,SAA2D;CAC7F,MAAM,QAAQ,gBAAgB,QAAQ,MAAM;AAC5C,KAAI,CAAC,MAAO,QAAO,EAAE;CAErB,MAAM,UAAoB,CAAC,6BAA6B;CACxD,MAAM,SAAwC,CAAC,MAAM;AACrD,KAAI,QAAQ,SAAS;AACnB,UAAQ,KAAK,iBAAiB;AAC9B,SAAO,KAAK,QAAQ,QAAQ;;AAE9B,KAAI,QAAQ,aAAa;AACvB,UAAQ,KAAK,qBAAqB;AAClC,SAAO,KAAK,QAAQ,YAAY;;AAElC,KAAI,QAAQ,WAAW;AACrB,UAAQ,KAAK,mBAAmB;AAChC,SAAO,KAAK,QAAQ,UAAU;;AAEhC,KAAI,QAAQ,YAAY;AACtB,UAAQ,KAAK,oBAAoB;AACjC,SAAO,KAAK,QAAQ,WAAW;;AAEjC,KAAI,QAAQ,SAAS,QAAQ,MAAM,SAAS,GAAG;AAC7C,UAAQ,KAAK,cAAc,QAAQ,MAAM,UAAU,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG;AACtE,SAAO,KAAK,GAAG,QAAQ,MAAM;;CAE/B,MAAM,WAAW,QAAQ,YAAY,QAAQ,SAAS,SAAS,IAAI,QAAQ,WAAW,CAAC,SAAS;AAChG,SAAQ,KAAK,gBAAgB,SAAS,UAAU,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG;AACnE,QAAO,KAAK,GAAG,SAAS;CAExB,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,cAAc,EAAE,CAAC;CACrE,MAAM,WAAW,QAAQ,YAAY;AAcrC,QAba,mBAAmB,CAC7B,QACC;;;;;eAKS,QAAQ,KAAK,QAAQ,CAAC;;gBAGhC,CACA,IAAI,GAAG,QAAQ,aAAa,EAEpB,CACR,KAAK,QAAQ;EACZ,MAAM,SAAS,YAAY,IAAI;AAE/B,SAAO;GACL;GACA,OAHY,YAAY,IAAI,KAGvB;GACL,SAAS,OAAO;GAChB,UAAU;IACR,YAAY,IAAI;IAChB,UAAU,IAAI;IACd,MAAM,OAAO,OAAO;IACpB,WAAW,OAAO,OAAO;IACzB,SAAS,OAAO,OAAO;IACvB,WAAW,OAAO;IACnB;GACF;GACD,CACD,QAAQ,WAAW,OAAO,SAAS,SAAS,CAC5C,MAAM,GAAG,WAAW;;AAGzB,SAAgB,mBAAmB,UAA2B;AAC5D,QAAO,2BAA2B,OAAO;AACvC,KAAG,QAAQ,qDAAqD,CAAC,IAAI,SAAS;AAE9E,SADe,GAAG,QAAQ,iDAAiD,CAAC,IAAI,SACnE,CAAC,UAAU;GACxB;;AAGJ,SAAgB,mBAAmB,OAAwC;CACzE,MAAM,KAAK,YAAY;CACvB,MAAM,MAAM,MAAM,SAAS,KAAK,KAAK;AACrC,4BAA2B,OAAO;AAChC,KAAG,QACD;;;kDAID,CAAC,IACA,IACA,MAAM,OAAO,QACb,MAAM,OAAO,YAAY,MACzB,MAAM,cAAc,MACpB,MAAM,WAAW,MACjB,MAAM,eAAe,MACrB,MAAM,cAAc,MACpB,MAAM,OAAO,SAAS,MACtB,MAAM,OAAO,WAAW,MACxB,KAAK,UAAU,MAAM,OAAO,YAAY,EAAE,CAAC,EAC3C,IACD;AACD,MAAI,MAAM,OAAO,SACf,IAAG,QACD;;8BAGD,CAAC,IAAI,KAAK,KAAK,MAAM,OAAO,SAAS;GAExC;AACF,QAAO;;AAGT,SAAgB,kBAAkB,UAM9B,EAAE,EAA4B;CAChC,MAAM,QAAkB,EAAE;CAC1B,MAAM,SAAwC,EAAE;AAChD,KAAI,QAAQ,UAAU;AACpB,QAAM,KAAK,gBAAgB;AAC3B,SAAO,KAAK,QAAQ,SAAS;;AAE/B,KAAI,QAAQ,YAAY;AACtB,QAAM,KAAK,kBAAkB;AAC7B,SAAO,KAAK,QAAQ,WAAW;;AAEjC,KAAI,QAAQ,SAAS;AACnB,QAAM,KAAK,eAAe;AAC1B,SAAO,KAAK,QAAQ,QAAQ;;AAE9B,KAAI,QAAQ,aAAa;AACvB,QAAM,KAAK,mBAAmB;AAC9B,SAAO,KAAK,QAAQ,YAAY;;CAElC,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,QAAQ,SAAS,IAAI,CAAC;AAS9D,QARa,mBAAmB,CAC7B,QACC;SACG,MAAM,SAAS,IAAI,SAAS,MAAM,KAAK,QAAQ,KAAK,GAAG;;gBAG3D,CACA,IAAI,GAAG,QAAQ,MACP,CAAC,KAAK,QAAQ;EACvB,IAAI,WAAoC,EAAE;AAC1C,MAAI;GACF,MAAM,SAAS,KAAK,MAAM,IAAI,cAAc;AAC5C,OAAI,UAAU,OAAO,WAAW,SAAU,YAAW;UAC/C;AACN,cAAW,EAAE;;AAEf,SAAO;GACL,UAAU,IAAI;GACd,QAAQ,IAAI;GACZ,GAAI,IAAI,YAAY,EAAE,UAAU,IAAI,WAAW,GAAG,EAAE;GACpD,GAAI,IAAI,cAAc,EAAE,YAAY,IAAI,aAAa,GAAG,EAAE;GAC1D,GAAI,IAAI,WAAW,EAAE,SAAS,IAAI,UAAU,GAAG,EAAE;GACjD,GAAI,IAAI,eAAe,EAAE,aAAa,IAAI,cAAc,GAAG,EAAE;GAC7D,GAAI,IAAI,cAAc,EAAE,YAAY,IAAI,aAAa,GAAG,EAAE;GAC1D,GAAI,IAAI,SAAS,OAAO,EAAE,OAAO,IAAI,OAAO,GAAG,EAAE;GACjD,GAAI,IAAI,UAAU,EAAE,SAAS,IAAI,SAAS,GAAG,EAAE;GAC/C;GACA,WAAW,IAAI,KAAK,IAAI,WAAW,CAAC,aAAa;GAClD;GACD;;AAGJ,SAAgB,uBAAuB,YAAoB,UAAkC;CAC3F,MAAM,MAAM,mBAAmB,CAC5B,QAAQ,uFAAuF,CAC/F,IAAI,YAAY,SAAS;AAC5B,KAAI,CAAC,IAAK,QAAO;AACjB,KAAI;AACF,SAAO,KAAK,MAAM,IAAI,WAAW;SAC3B;AACN,SAAO;;;AAIX,SAAgB,uBAAuB,YAAoB,UAAkB,OAAsB;CACjG,MAAM,MAAM,KAAK,KAAK;AACtB,4BAA2B,OAAO;AAChC,KAAG,QACD;;;;2CAKD,CAAC,IAAI,YAAY,UAAU,KAAK,UAAU,SAAS,KAAK,EAAE,IAAI;GAC/D;;AAGJ,SAAgB,uBAAuB,OAA4C;CACjF,MAAM,KAAK,YAAY;CACvB,MAAM,MAAM,MAAM,SAAS,KAAK,KAAK;CACrC,MAAM,WAAW,MAAM,WACnB,6BAA6B,MAAM,UAAU,IAAI,GACjD,KAAA;AACJ,4BAA2B,OAAO;AAChC,KAAG,QACD;;;wDAID,CAAC,IACA,IACA,MAAM,cAAc,MACpB,MAAM,UAAU,MAChB,MAAM,OACN,MAAM,YACN,KAAK,UAAU,MAAM,WAAW,EAAE,CAAC,EACnC,MAAM,eAAe,MACrB,KAAK,UAAU,MAAM,qBAAqB,EAAE,CAAC,EAC7C,MAAM,iBAAiB,MACvB,MAAM,SAAS,MACf,KAAK,UAAU,YAAY,EAAE,CAAC,EAC9B,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,cAAc,EAAE,CAAC,EAC9C,IACD;GACD;AACF,QAAO;;AAGT,SAAgB,sBAAsB,UAKlC,EAAE,EAA6B;CACjC,MAAM,QAAkB,EAAE;CAC1B,MAAM,SAAwC,EAAE;AAChD,KAAI,QAAQ,YAAY;AACtB,QAAM,KAAK,kBAAkB;AAC7B,SAAO,KAAK,QAAQ,WAAW;;AAEjC,KAAI,QAAQ,YAAY;AACtB,QAAM,KAAK,kBAAkB;AAC7B,SAAO,KAAK,QAAQ,WAAW;;AAEjC,KAAI,QAAQ,OAAO;AACjB,QAAM,KAAK,YAAY;AACvB,SAAO,KAAK,QAAQ,MAAM;;CAE5B,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,QAAQ,SAAS,IAAI,CAAC;AAS9D,QARa,mBAAmB,CAC7B,QACC;SACG,MAAM,SAAS,IAAI,SAAS,MAAM,KAAK,QAAQ,KAAK,GAAG;;gBAG3D,CACA,IAAI,GAAG,QAAQ,MACP,CAAC,IAAI,wBAAwB;;AAG1C,SAAgB,uBAAuB,OAAoE;CACzG,MAAM,WAAW,mBAAmB,CACjC,QAAQ,uDAAuD,CAC/D,IAAI,MAAM,QAAQ;AACrB,KAAI,CAAC,SACH,QAAO;CAET,MAAM,WAAW,6BACf,MAAM,UACN,MAAM,OACN,yBAAyB,SAAS,cAAc,CACjD;AACD,4BAA2B,OAAO;AAChC,KAAG,QAAQ,sEAAsE,CAC9E,IAAI,KAAK,UAAU,SAAS,EAAE,MAAM,QAAQ;GAC/C;AACF,QAAO,wBAAwB;EAAE,GAAG;EAAU,eAAe,KAAK,UAAU,SAAS;EAAE,CAAC;;AAG1F,SAAgB,8BAA8B,UAK1C,EAAE,EAAiC;CACrC,MAAM,QAAQ,CAAC,kBAAkB;CACjC,MAAM,SAAiC,CAAC,UAAU,SAAS;AAC3D,KAAI,QAAQ,YAAY;AACtB,QAAM,KAAK,kBAAkB;AAC7B,SAAO,KAAK,QAAQ,WAAW;;AAEjC,KAAI,QAAQ,YAAY;AACtB,QAAM,KAAK,kBAAkB;AAC7B,SAAO,KAAK,QAAQ,WAAW;;CAEjC,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAM,QAAQ,SAAS,IAAK,CAAC;CAChE,MAAM,OAAO,mBAAmB,CAC7B,QACC;eACS,MAAM,KAAK,QAAQ,CAAC;;gBAG9B,CACA,IAAI,GAAG,QAAQ,MAAM;CAExB,MAAM,4BAAY,IAAI,KAAuF;AAC7G,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,yBAAyB,IAAI,cAAc;AAC5D,MAAI,CAAC,SAAU;EACf,MAAM,YAAY,iBAAiB,IAAI,yBAAyB;AAChE,OAAK,MAAM,YAAY,WAAW;AAChC,OAAI,QAAQ,YAAY,aAAa,QAAQ,SAAU;GACvD,MAAM,UAAU,UAAU,IAAI,SAAS,IAAI;IACzC;IACA,SAAS;IACT,YAAY;IACZ,OAAO;IACP,YAAY;IACZ,OAAO;IACP,cAAc;IACd,YAAY;IACZ,YAAY;IACb;AACD,OAAI,SAAS,YAAY,UAAW,SAAQ,WAAW;AACvD,OAAI,SAAS,YAAY,cAAe,SAAQ,cAAc;AAC9D,OAAI,SAAS,YAAY,QAAS,SAAQ,SAAS;AACnD,OAAI,SAAS,YAAY,aAAc,SAAQ,cAAc;AAC7D,WAAQ,SAAS;AACjB,OAAI,OAAO,SAAS,UAAU,UAAU;AACtC,YAAQ,cAAc,SAAS;AAC/B,YAAQ,cAAc;AACtB,YAAQ,eAAe,QAAQ,aAAa,QAAQ;;AAEtD,WAAQ,mBAAmB,SAAS,aAAa,SAAS;AAC1D,aAAU,IAAI,UAAU,QAAQ;;;AAIpC,QAAO,CAAC,GAAG,UAAU,QAAQ,CAAC,CAC3B,KAAK,EAAE,YAAY,aAAa,YAAY,aAAa,GAAG,cAAc,QAAQ,CAClF,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;;AAGtC,SAAS,eAAe,MAAc,UAA4B;AAChE,KAAI;AACF,SAAO,KAAK,MAAM,KAAK;SACjB;AACN,SAAO"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
2
|
+
project_id TEXT PRIMARY KEY,
|
|
3
|
+
name TEXT NOT NULL,
|
|
4
|
+
slug TEXT NOT NULL UNIQUE,
|
|
5
|
+
description TEXT,
|
|
6
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
7
|
+
workspace_root TEXT,
|
|
8
|
+
brief TEXT,
|
|
9
|
+
instructions TEXT,
|
|
10
|
+
created_at INTEGER NOT NULL,
|
|
11
|
+
updated_at INTEGER NOT NULL,
|
|
12
|
+
last_active_at INTEGER
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_projects_status
|
|
16
|
+
ON projects(status, updated_at DESC);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_projects_slug
|
|
18
|
+
ON projects(slug);
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_projects_updated
|
|
20
|
+
ON projects(updated_at DESC);
|
|
21
|
+
|
|
22
|
+
ALTER TABLE sessions ADD COLUMN project_id TEXT;
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
24
|
+
ON sessions(project_id, updated_at DESC);
|
|
25
|
+
|
|
26
|
+
ALTER TABLE goals ADD COLUMN project_id TEXT;
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_goals_project
|
|
28
|
+
ON goals(project_id, updated_at DESC);
|
|
29
|
+
|
|
30
|
+
ALTER TABLE workflow_runs ADD COLUMN project_id TEXT;
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_project
|
|
32
|
+
ON workflow_runs(project_id, created_at_ms DESC);
|
|
33
|
+
|
|
34
|
+
ALTER TABLE automations ADD COLUMN project_id TEXT;
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_automations_project
|
|
36
|
+
ON automations(project_id, updated_at_ms DESC);
|
|
37
|
+
|
|
38
|
+
ALTER TABLE memory_records ADD COLUMN project_id TEXT;
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_memory_records_project
|
|
40
|
+
ON memory_records(project_id, updated_at DESC);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE projects ADD COLUMN default_agent_id TEXT;
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 20;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
|
|
|
12
12
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
13
13
|
const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
14
14
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
15
|
-
const XOPC_DB_SCHEMA_VERSION =
|
|
15
|
+
const XOPC_DB_SCHEMA_VERSION = 20;
|
|
16
16
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
function resolveMigrationsDir(override) {
|
|
18
18
|
if (override) return override;
|
|
@@ -46,7 +46,7 @@ function applySingleMigration(db, migration) {
|
|
|
46
46
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
47
47
|
*/
|
|
48
48
|
function applyPendingMigrations(db, options = {}) {
|
|
49
|
-
const targetVersion = options.targetVersion ??
|
|
49
|
+
const targetVersion = options.targetVersion ?? 20;
|
|
50
50
|
let currentVersion = readSchemaVersion(db);
|
|
51
51
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
52
52
|
if (currentVersion >= targetVersion) return currentVersion;
|
|
@@ -62,7 +62,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
62
62
|
}
|
|
63
63
|
/** Inspect schema version without mutating the database. */
|
|
64
64
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
65
|
-
const appVersion = options.targetVersion ??
|
|
65
|
+
const appVersion = options.targetVersion ?? 20;
|
|
66
66
|
const dbVersion = readSchemaVersion(db);
|
|
67
67
|
if (dbVersion > appVersion) return {
|
|
68
68
|
dbVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION =
|
|
1
|
+
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION = 20;\n\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nexport function resolveMigrationsDir(override?: string): string {\n if (override) {\n return override;\n }\n // Packaged Electron gateway bundle: `out/server/index.js` + `out/server/migrations/`.\n const siblingDir = join(MODULE_DIR, 'migrations');\n if (existsSync(siblingDir)) {\n return siblingDir;\n }\n // Dev / dist: SQL files live next to `migrations/runner.js`.\n return MODULE_DIR;\n}\n\nfunction migrationByTarget(\n migrations: SqlMigration[],\n targetVersion: number,\n): SqlMigration | undefined {\n return migrations.find((migration) => migration.targetVersion === targetVersion);\n}\n\nfunction applySingleMigration(db: DatabaseSync, migration: SqlMigration): void {\n log.info({ targetVersion: migration.targetVersion, file: migration.filename }, 'Applying SQLite migration');\n db.exec('BEGIN IMMEDIATE');\n try {\n db.exec(migration.sql);\n setSchemaVersion(db, migration.targetVersion);\n db.exec('COMMIT');\n } catch (error) {\n try {\n db.exec('ROLLBACK');\n } catch {\n /* preserve original error */\n }\n const em = error instanceof Error ? error.message : String(error);\n throw new Error(\n `SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`,\n { cause: error },\n );\n }\n}\n\n/**\n * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.\n * Each step runs in its own transaction and updates schema_meta on success.\n */\nexport function applyPendingMigrations(\n db: DatabaseSync,\n options: ApplyMigrationsOptions = {},\n): number {\n const targetVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n let currentVersion = readSchemaVersion(db);\n\n if (currentVersion > targetVersion) {\n throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);\n }\n\n if (currentVersion >= targetVersion) {\n return currentVersion;\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n\n while (currentVersion < targetVersion) {\n const nextVersion = currentVersion + 1;\n const migration = migrationByTarget(migrations, nextVersion);\n if (!migration) {\n throw new DatabaseSchemaMigrationGapError(currentVersion, targetVersion, nextVersion);\n }\n applySingleMigration(db, migration);\n currentVersion = nextVersion;\n }\n\n return currentVersion;\n}\n\nexport type SchemaMigrationStatus = {\n dbVersion: number;\n appVersion: number;\n pendingVersions: number[];\n isTooNew: boolean;\n hasMigrationGap: boolean;\n missingVersion: number | null;\n};\n\n/** Inspect schema version without mutating the database. */\nexport function inspectSchemaMigrationStatus(\n db: DatabaseSync,\n options: Pick<ApplyMigrationsOptions, 'migrationsDir' | 'targetVersion'> = {},\n): SchemaMigrationStatus {\n const appVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n const dbVersion = readSchemaVersion(db);\n const isTooNew = dbVersion > appVersion;\n\n if (isTooNew) {\n return {\n dbVersion,\n appVersion,\n pendingVersions: [],\n isTooNew: true,\n hasMigrationGap: false,\n missingVersion: null,\n };\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n const pendingVersions: number[] = [];\n let hasMigrationGap = false;\n let missingVersion: number | null = null;\n\n for (let version = dbVersion + 1; version <= appVersion; version++) {\n if (migrationByTarget(migrations, version)) {\n pendingVersions.push(version);\n } else {\n hasMigrationGap = true;\n missingVersion = version;\n break;\n }\n }\n\n return {\n dbVersion,\n appVersion,\n pendingVersions,\n isTooNew: false,\n hasMigrationGap,\n missingVersion,\n };\n}\n"],"mappings":";;;;;;;;;aAMwD;AASxD,MAAM,MAAM,aAAa,oBAAoB;;AAG7C,MAAa,kCAAkC;;AAG/C,MAAa,yBAAyB;AAEtC,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE1D,SAAgB,qBAAqB,UAA2B;AAC9D,KAAI,SACF,QAAO;CAGT,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,KAAI,WAAW,WAAW,CACxB,QAAO;AAGT,QAAO;;AAGT,SAAS,kBACP,YACA,eAC0B;AAC1B,QAAO,WAAW,MAAM,cAAc,UAAU,kBAAkB,cAAc;;AAGlF,SAAS,qBAAqB,IAAkB,WAA+B;AAC7E,KAAI,KAAK;EAAE,eAAe,UAAU;EAAe,MAAM,UAAU;EAAU,EAAE,4BAA4B;AAC3G,IAAG,KAAK,kBAAkB;AAC1B,KAAI;AACF,KAAG,KAAK,UAAU,IAAI;AACtB,mBAAiB,IAAI,UAAU,cAAc;AAC7C,KAAG,KAAK,SAAS;UACV,OAAO;AACd,MAAI;AACF,MAAG,KAAK,WAAW;UACb;EAGR,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACjE,QAAM,IAAI,MACR,wBAAwB,UAAU,cAAc,IAAI,UAAU,SAAS,YAAY,MACnF,EAAE,OAAO,OAAO,CACjB;;;;;;;AAQL,SAAgB,uBACd,IACA,UAAkC,EAAE,EAC5B;CACR,MAAM,gBAAgB,QAAQ,iBAAA;CAC9B,IAAI,iBAAiB,kBAAkB,GAAG;AAE1C,KAAI,iBAAiB,cACnB,OAAM,IAAI,0BAA0B,gBAAgB,cAAc;AAGpE,KAAI,kBAAkB,cACpB,QAAO;CAGT,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;AAErF,QAAO,iBAAiB,eAAe;EACrC,MAAM,cAAc,iBAAiB;EACrC,MAAM,YAAY,kBAAkB,YAAY,YAAY;AAC5D,MAAI,CAAC,UACH,OAAM,IAAI,gCAAgC,gBAAgB,eAAe,YAAY;AAEvF,uBAAqB,IAAI,UAAU;AACnC,mBAAiB;;AAGnB,QAAO;;;AAaT,SAAgB,6BACd,IACA,UAA2E,EAAE,EACtD;CACvB,MAAM,aAAa,QAAQ,iBAAA;CAC3B,MAAM,YAAY,kBAAkB,GAAG;AAGvC,KAFiB,YAAY,WAG3B,QAAO;EACL;EACA;EACA,iBAAiB,EAAE;EACnB,UAAU;EACV,iBAAiB;EACjB,gBAAgB;EACjB;CAGH,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;CACrF,MAAM,kBAA4B,EAAE;CACpC,IAAI,kBAAkB;CACtB,IAAI,iBAAgC;AAEpC,MAAK,IAAI,UAAU,YAAY,GAAG,WAAW,YAAY,UACvD,KAAI,kBAAkB,YAAY,QAAQ,CACxC,iBAAgB,KAAK,QAAQ;MACxB;AACL,oBAAkB;AAClB,mBAAiB;AACjB;;AAIJ,QAAO;EACL;EACA;EACA;EACA,UAAU;EACV;EACA;EACD"}
|
|
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
|
|
|
19
19
|
export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
20
20
|
|
|
21
21
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
22
|
-
export const XOPC_DB_SCHEMA_VERSION =
|
|
22
|
+
export const XOPC_DB_SCHEMA_VERSION = 20;
|
|
23
23
|
|
|
24
24
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
@@ -56,6 +56,19 @@ function noteSearchContent(note) {
|
|
|
56
56
|
...note.tags ?? []
|
|
57
57
|
].filter(Boolean).join("\n");
|
|
58
58
|
}
|
|
59
|
+
function rowMatchesPlainSearch(row, search) {
|
|
60
|
+
const needle = search.trim().toLocaleLowerCase();
|
|
61
|
+
if (!needle) return true;
|
|
62
|
+
const chunks = [];
|
|
63
|
+
if (row.title) chunks.push(row.title);
|
|
64
|
+
if (row.snippet) chunks.push(row.snippet);
|
|
65
|
+
if (row.tags_json) chunks.push(row.tags_json);
|
|
66
|
+
if (row.attachment_names_json) chunks.push(row.attachment_names_json);
|
|
67
|
+
try {
|
|
68
|
+
chunks.push(noteSearchContent(JSON.parse(row.payload_json)));
|
|
69
|
+
} catch {}
|
|
70
|
+
return chunks.join("\n").toLocaleLowerCase().includes(needle);
|
|
71
|
+
}
|
|
59
72
|
function noteToRow(note) {
|
|
60
73
|
const meta = buildNoteIndexMeta(note);
|
|
61
74
|
return {
|
|
@@ -165,20 +178,23 @@ function listNoteRecords(query = {}) {
|
|
|
165
178
|
}
|
|
166
179
|
if (query.pendingTasksOnly) conditions.push(`kind = 'task' AND (task_done IS NULL OR task_done = 0)`);
|
|
167
180
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
168
|
-
|
|
181
|
+
const rows = db.prepare(`SELECT note_id, title, kind, status, payload_json, created_at, updated_at,
|
|
169
182
|
pinned, tags_json, snippet, cover_attachment_id, voice_attachment_id,
|
|
170
183
|
voice_duration_sec, attachment_names_json, group_id, last_opened_at,
|
|
171
184
|
task_done, task_due_at, heading_count, task_count, unchecked_task_count, link_count
|
|
172
|
-
FROM notes ${where}`).all(...params)
|
|
185
|
+
FROM notes ${where}`).all(...params);
|
|
186
|
+
let entries = rows.map(rowToIndexEntry);
|
|
173
187
|
if (query.tag) entries = entries.filter((entry) => entry.tags?.includes(query.tag));
|
|
174
188
|
if (query.search) {
|
|
175
|
-
const
|
|
189
|
+
const search = query.search.trim();
|
|
190
|
+
const ftsQuery = escapeFts5Query(search);
|
|
176
191
|
const ftsIds = /* @__PURE__ */ new Set();
|
|
177
192
|
if (ftsQuery) {
|
|
178
193
|
const ftsRows = db.prepare(`SELECT note_id FROM notes_fts WHERE notes_fts MATCH ? ORDER BY rank`).all(ftsQuery);
|
|
179
194
|
for (const row of ftsRows) ftsIds.add(row.note_id);
|
|
180
195
|
}
|
|
181
|
-
|
|
196
|
+
const plainIds = new Set(rows.filter((row) => rowMatchesPlainSearch(row, search)).map((row) => row.note_id));
|
|
197
|
+
entries = entries.filter((entry) => ftsIds.has(entry.id) || plainIds.has(entry.id));
|
|
182
198
|
}
|
|
183
199
|
const sortField = query.sortBy || "createdAt";
|
|
184
200
|
const sortDir = query.sortOrder === "asc" ? 1 : -1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notes-repository.js","names":[],"sources":["../../../../src/storage/sqlite/notes-repository.ts"],"sourcesContent":["import type { Note, NoteIndexEntry, NotesListQuery } from '../../notes/types.js';\nimport { buildNoteIndexMeta, extractAttachmentFileNames, notePlainText } from '../../notes/note-index-meta.js';\nimport { escapeFts5Query } from './fts.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype NoteRow = {\n note_id: string;\n title: string | null;\n kind: string;\n status: string;\n payload_json: string;\n created_at: number;\n updated_at: number;\n pinned: number;\n tags_json: string;\n snippet: string | null;\n cover_attachment_id: string | null;\n voice_attachment_id: string | null;\n voice_duration_sec: number | null;\n attachment_names_json: string | null;\n group_id: string | null;\n last_opened_at: number | null;\n task_done: number | null;\n task_due_at: number | null;\n heading_count: number | null;\n task_count: number | null;\n unchecked_task_count: number | null;\n link_count: number | null;\n};\n\nfunction parseTags(json: string): string[] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((t): t is string => typeof t === 'string') : [];\n } catch {\n return [];\n }\n}\n\nfunction parseAttachmentNames(json: string | null): string[] | undefined {\n if (!json) return undefined;\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((n): n is string => typeof n === 'string') : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction rowToIndexEntry(row: NoteRow): NoteIndexEntry {\n return {\n id: row.note_id,\n title: row.title ?? undefined,\n kind: row.kind as NoteIndexEntry['kind'],\n status: row.status as NoteIndexEntry['status'],\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n pinned: row.pinned ? true : undefined,\n tags: parseTags(row.tags_json),\n snippet: row.snippet ?? undefined,\n coverAttachmentId: row.cover_attachment_id ?? undefined,\n voiceAttachmentId: row.voice_attachment_id ?? undefined,\n voiceDurationSec: row.voice_duration_sec ?? undefined,\n attachmentNames: parseAttachmentNames(row.attachment_names_json),\n groupId: row.group_id ?? undefined,\n lastOpenedAt: row.last_opened_at ?? undefined,\n taskDone: row.task_done != null ? Boolean(row.task_done) : undefined,\n taskDueAt: row.task_due_at ?? undefined,\n headingCount: row.heading_count ?? undefined,\n taskCount: row.task_count ?? undefined,\n uncheckedTaskCount: row.unchecked_task_count ?? undefined,\n linkCount: row.link_count ?? undefined,\n };\n}\n\nfunction noteSearchContent(note: Note): string {\n const plain = notePlainText(note);\n const attachmentText = note.attachments?.map((a) => a.transcript).filter(Boolean).join(' ') ?? '';\n const attachmentNames = extractAttachmentFileNames(note)?.join(' ') ?? '';\n return [note.title, plain, attachmentText, attachmentNames, ...(note.tags ?? [])].filter(Boolean).join('\\n');\n}\n\nfunction noteToRow(note: Note): Omit<NoteRow, 'payload_json'> & { payload_json: string } {\n const meta = buildNoteIndexMeta(note);\n return {\n note_id: note.id,\n title: note.title ?? null,\n kind: note.kind,\n status: note.status,\n payload_json: JSON.stringify(note),\n created_at: note.createdAt,\n updated_at: note.updatedAt,\n pinned: note.pinned ? 1 : 0,\n tags_json: JSON.stringify(note.tags ?? []),\n snippet: meta.snippet ?? null,\n cover_attachment_id: meta.coverAttachmentId ?? null,\n voice_attachment_id: meta.voiceAttachmentId ?? null,\n voice_duration_sec: meta.voiceDurationSec ?? null,\n attachment_names_json: meta.attachmentNames ? JSON.stringify(meta.attachmentNames) : null,\n group_id: note.groupId ?? null,\n last_opened_at: note.lastOpenedAt ?? null,\n task_done: note.taskMeta?.done != null ? (note.taskMeta.done ? 1 : 0) : null,\n task_due_at: note.taskMeta?.dueAt ?? null,\n heading_count: meta.headingCount ?? null,\n task_count: meta.taskCount ?? null,\n unchecked_task_count: meta.uncheckedTaskCount ?? null,\n link_count: meta.linkCount ?? null,\n };\n}\n\nfunction upsertNoteFts(db: ReturnType<typeof getSqliteDatabase>, note: Note): void {\n db.prepare(`DELETE FROM notes_fts WHERE note_id = ?`).run(note.id);\n const content = noteSearchContent(note);\n if (!content.trim()) {\n return;\n }\n db.prepare(`INSERT INTO notes_fts (content, note_id) VALUES (?, ?)`).run(content, note.id);\n}\n\nexport function upsertNoteRecord(note: Note): void {\n const row = noteToRow(note);\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO notes (\n note_id, title, kind, status, payload_json, created_at, updated_at,\n pinned, tags_json, snippet, cover_attachment_id, voice_attachment_id,\n voice_duration_sec, attachment_names_json, group_id, last_opened_at,\n task_done, task_due_at, heading_count, task_count, unchecked_task_count, link_count\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(note_id) DO UPDATE SET\n title = excluded.title,\n kind = excluded.kind,\n status = excluded.status,\n payload_json = excluded.payload_json,\n updated_at = excluded.updated_at,\n pinned = excluded.pinned,\n tags_json = excluded.tags_json,\n snippet = excluded.snippet,\n cover_attachment_id = excluded.cover_attachment_id,\n voice_attachment_id = excluded.voice_attachment_id,\n voice_duration_sec = excluded.voice_duration_sec,\n attachment_names_json = excluded.attachment_names_json,\n group_id = excluded.group_id,\n last_opened_at = excluded.last_opened_at,\n task_done = excluded.task_done,\n task_due_at = excluded.task_due_at,\n heading_count = excluded.heading_count,\n task_count = excluded.task_count,\n unchecked_task_count = excluded.unchecked_task_count,\n link_count = excluded.link_count`,\n ).run(\n row.note_id,\n row.title,\n row.kind,\n row.status,\n row.payload_json,\n row.created_at,\n row.updated_at,\n row.pinned,\n row.tags_json,\n row.snippet,\n row.cover_attachment_id,\n row.voice_attachment_id,\n row.voice_duration_sec,\n row.attachment_names_json,\n row.group_id,\n row.last_opened_at,\n row.task_done,\n row.task_due_at,\n row.heading_count,\n row.task_count,\n row.unchecked_task_count,\n row.link_count,\n );\n upsertNoteFts(db, note);\n });\n}\n\nexport function getNoteRecord(noteId: string): Note | null {\n const db = getSqliteDatabase();\n const row = db.prepare(`SELECT payload_json FROM notes WHERE note_id = ?`).get(noteId) as\n | { payload_json: string }\n | undefined;\n if (!row?.payload_json) {\n return null;\n }\n try {\n return JSON.parse(row.payload_json) as Note;\n } catch {\n return null;\n }\n}\n\nexport function deleteNoteRecord(noteId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const existing = db.prepare(`SELECT note_id FROM notes WHERE note_id = ?`).get(noteId);\n if (!existing) {\n return false;\n }\n db.prepare(`DELETE FROM notes_fts WHERE note_id = ?`).run(noteId);\n db.prepare(`DELETE FROM notes WHERE note_id = ?`).run(noteId);\n return true;\n });\n}\n\nexport function listNoteRecords(\n query: NotesListQuery = {},\n): { items: NoteIndexEntry[]; total: number; limit: number; offset: number; hasMore: boolean } {\n const db = getSqliteDatabase();\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n\n if (query.status) {\n conditions.push(`status = ?`);\n params.push(query.status);\n } else {\n conditions.push(`status != ?`);\n params.push('trashed');\n }\n if (query.kind) {\n conditions.push(`kind = ?`);\n params.push(query.kind);\n }\n if (query.pinned !== undefined) {\n conditions.push(`pinned = ?`);\n params.push(query.pinned ? 1 : 0);\n }\n if (query.groupId !== undefined) {\n if (query.groupId === 'ungrouped') {\n conditions.push(`(group_id IS NULL OR group_id = '')`);\n } else {\n conditions.push(`group_id = ?`);\n params.push(query.groupId);\n }\n }\n if (query.pendingTasksOnly) {\n conditions.push(`kind = 'task' AND (task_done IS NULL OR task_done = 0)`);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const rows = db\n .prepare(\n `SELECT note_id, title, kind, status, payload_json, created_at, updated_at,\n pinned, tags_json, snippet, cover_attachment_id, voice_attachment_id,\n voice_duration_sec, attachment_names_json, group_id, last_opened_at,\n task_done, task_due_at, heading_count, task_count, unchecked_task_count, link_count\n FROM notes ${where}`,\n )\n .all(...params) as NoteRow[];\n\n let entries = rows.map(rowToIndexEntry);\n\n if (query.tag) {\n entries = entries.filter((entry) => entry.tags?.includes(query.tag!));\n }\n\n if (query.search) {\n const ftsQuery = escapeFts5Query(query.search);\n const ftsIds = new Set<string>();\n if (ftsQuery) {\n const ftsRows = db\n .prepare(\n `SELECT note_id FROM notes_fts WHERE notes_fts MATCH ? ORDER BY rank`,\n )\n .all(ftsQuery) as Array<{ note_id: string }>;\n for (const row of ftsRows) {\n ftsIds.add(row.note_id);\n }\n }\n entries = entries.filter((entry) => ftsIds.has(entry.id));\n }\n\n const sortField = query.sortBy || 'createdAt';\n const sortDir = query.sortOrder === 'asc' ? 1 : -1;\n entries = [...entries].sort((a, b) => {\n const aVal = (a[sortField] as number | undefined) ?? 0;\n const bVal = (b[sortField] as number | undefined) ?? 0;\n return (aVal - bVal) * sortDir;\n });\n\n const total = entries.length;\n const offset = query.offset || 0;\n const limit = Math.min(query.limit || 50, 200);\n const items = entries.slice(offset, offset + limit);\n const hasMore = offset + items.length < total;\n\n return { items, total, limit, offset, hasMore };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"notes-repository.js","names":[],"sources":["../../../../src/storage/sqlite/notes-repository.ts"],"sourcesContent":["import type { Note, NoteIndexEntry, NotesListQuery } from '../../notes/types.js';\nimport { buildNoteIndexMeta, extractAttachmentFileNames, notePlainText } from '../../notes/note-index-meta.js';\nimport { escapeFts5Query } from './fts.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype NoteRow = {\n note_id: string;\n title: string | null;\n kind: string;\n status: string;\n payload_json: string;\n created_at: number;\n updated_at: number;\n pinned: number;\n tags_json: string;\n snippet: string | null;\n cover_attachment_id: string | null;\n voice_attachment_id: string | null;\n voice_duration_sec: number | null;\n attachment_names_json: string | null;\n group_id: string | null;\n last_opened_at: number | null;\n task_done: number | null;\n task_due_at: number | null;\n heading_count: number | null;\n task_count: number | null;\n unchecked_task_count: number | null;\n link_count: number | null;\n};\n\nfunction parseTags(json: string): string[] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((t): t is string => typeof t === 'string') : [];\n } catch {\n return [];\n }\n}\n\nfunction parseAttachmentNames(json: string | null): string[] | undefined {\n if (!json) return undefined;\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((n): n is string => typeof n === 'string') : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction rowToIndexEntry(row: NoteRow): NoteIndexEntry {\n return {\n id: row.note_id,\n title: row.title ?? undefined,\n kind: row.kind as NoteIndexEntry['kind'],\n status: row.status as NoteIndexEntry['status'],\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n pinned: row.pinned ? true : undefined,\n tags: parseTags(row.tags_json),\n snippet: row.snippet ?? undefined,\n coverAttachmentId: row.cover_attachment_id ?? undefined,\n voiceAttachmentId: row.voice_attachment_id ?? undefined,\n voiceDurationSec: row.voice_duration_sec ?? undefined,\n attachmentNames: parseAttachmentNames(row.attachment_names_json),\n groupId: row.group_id ?? undefined,\n lastOpenedAt: row.last_opened_at ?? undefined,\n taskDone: row.task_done != null ? Boolean(row.task_done) : undefined,\n taskDueAt: row.task_due_at ?? undefined,\n headingCount: row.heading_count ?? undefined,\n taskCount: row.task_count ?? undefined,\n uncheckedTaskCount: row.unchecked_task_count ?? undefined,\n linkCount: row.link_count ?? undefined,\n };\n}\n\nfunction noteSearchContent(note: Note): string {\n const plain = notePlainText(note);\n const attachmentText = note.attachments?.map((a) => a.transcript).filter(Boolean).join(' ') ?? '';\n const attachmentNames = extractAttachmentFileNames(note)?.join(' ') ?? '';\n return [note.title, plain, attachmentText, attachmentNames, ...(note.tags ?? [])].filter(Boolean).join('\\n');\n}\n\nfunction rowMatchesPlainSearch(row: NoteRow, search: string): boolean {\n const needle = search.trim().toLocaleLowerCase();\n if (!needle) {\n return true;\n }\n\n const chunks: string[] = [];\n if (row.title) chunks.push(row.title);\n if (row.snippet) chunks.push(row.snippet);\n if (row.tags_json) chunks.push(row.tags_json);\n if (row.attachment_names_json) chunks.push(row.attachment_names_json);\n\n try {\n chunks.push(noteSearchContent(JSON.parse(row.payload_json) as Note));\n } catch {\n // Keep search usable even if a legacy row has malformed payload JSON.\n }\n\n return chunks.join('\\n').toLocaleLowerCase().includes(needle);\n}\n\nfunction noteToRow(note: Note): Omit<NoteRow, 'payload_json'> & { payload_json: string } {\n const meta = buildNoteIndexMeta(note);\n return {\n note_id: note.id,\n title: note.title ?? null,\n kind: note.kind,\n status: note.status,\n payload_json: JSON.stringify(note),\n created_at: note.createdAt,\n updated_at: note.updatedAt,\n pinned: note.pinned ? 1 : 0,\n tags_json: JSON.stringify(note.tags ?? []),\n snippet: meta.snippet ?? null,\n cover_attachment_id: meta.coverAttachmentId ?? null,\n voice_attachment_id: meta.voiceAttachmentId ?? null,\n voice_duration_sec: meta.voiceDurationSec ?? null,\n attachment_names_json: meta.attachmentNames ? JSON.stringify(meta.attachmentNames) : null,\n group_id: note.groupId ?? null,\n last_opened_at: note.lastOpenedAt ?? null,\n task_done: note.taskMeta?.done != null ? (note.taskMeta.done ? 1 : 0) : null,\n task_due_at: note.taskMeta?.dueAt ?? null,\n heading_count: meta.headingCount ?? null,\n task_count: meta.taskCount ?? null,\n unchecked_task_count: meta.uncheckedTaskCount ?? null,\n link_count: meta.linkCount ?? null,\n };\n}\n\nfunction upsertNoteFts(db: ReturnType<typeof getSqliteDatabase>, note: Note): void {\n db.prepare(`DELETE FROM notes_fts WHERE note_id = ?`).run(note.id);\n const content = noteSearchContent(note);\n if (!content.trim()) {\n return;\n }\n db.prepare(`INSERT INTO notes_fts (content, note_id) VALUES (?, ?)`).run(content, note.id);\n}\n\nexport function upsertNoteRecord(note: Note): void {\n const row = noteToRow(note);\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO notes (\n note_id, title, kind, status, payload_json, created_at, updated_at,\n pinned, tags_json, snippet, cover_attachment_id, voice_attachment_id,\n voice_duration_sec, attachment_names_json, group_id, last_opened_at,\n task_done, task_due_at, heading_count, task_count, unchecked_task_count, link_count\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(note_id) DO UPDATE SET\n title = excluded.title,\n kind = excluded.kind,\n status = excluded.status,\n payload_json = excluded.payload_json,\n updated_at = excluded.updated_at,\n pinned = excluded.pinned,\n tags_json = excluded.tags_json,\n snippet = excluded.snippet,\n cover_attachment_id = excluded.cover_attachment_id,\n voice_attachment_id = excluded.voice_attachment_id,\n voice_duration_sec = excluded.voice_duration_sec,\n attachment_names_json = excluded.attachment_names_json,\n group_id = excluded.group_id,\n last_opened_at = excluded.last_opened_at,\n task_done = excluded.task_done,\n task_due_at = excluded.task_due_at,\n heading_count = excluded.heading_count,\n task_count = excluded.task_count,\n unchecked_task_count = excluded.unchecked_task_count,\n link_count = excluded.link_count`,\n ).run(\n row.note_id,\n row.title,\n row.kind,\n row.status,\n row.payload_json,\n row.created_at,\n row.updated_at,\n row.pinned,\n row.tags_json,\n row.snippet,\n row.cover_attachment_id,\n row.voice_attachment_id,\n row.voice_duration_sec,\n row.attachment_names_json,\n row.group_id,\n row.last_opened_at,\n row.task_done,\n row.task_due_at,\n row.heading_count,\n row.task_count,\n row.unchecked_task_count,\n row.link_count,\n );\n upsertNoteFts(db, note);\n });\n}\n\nexport function getNoteRecord(noteId: string): Note | null {\n const db = getSqliteDatabase();\n const row = db.prepare(`SELECT payload_json FROM notes WHERE note_id = ?`).get(noteId) as\n | { payload_json: string }\n | undefined;\n if (!row?.payload_json) {\n return null;\n }\n try {\n return JSON.parse(row.payload_json) as Note;\n } catch {\n return null;\n }\n}\n\nexport function deleteNoteRecord(noteId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const existing = db.prepare(`SELECT note_id FROM notes WHERE note_id = ?`).get(noteId);\n if (!existing) {\n return false;\n }\n db.prepare(`DELETE FROM notes_fts WHERE note_id = ?`).run(noteId);\n db.prepare(`DELETE FROM notes WHERE note_id = ?`).run(noteId);\n return true;\n });\n}\n\nexport function listNoteRecords(\n query: NotesListQuery = {},\n): { items: NoteIndexEntry[]; total: number; limit: number; offset: number; hasMore: boolean } {\n const db = getSqliteDatabase();\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n\n if (query.status) {\n conditions.push(`status = ?`);\n params.push(query.status);\n } else {\n conditions.push(`status != ?`);\n params.push('trashed');\n }\n if (query.kind) {\n conditions.push(`kind = ?`);\n params.push(query.kind);\n }\n if (query.pinned !== undefined) {\n conditions.push(`pinned = ?`);\n params.push(query.pinned ? 1 : 0);\n }\n if (query.groupId !== undefined) {\n if (query.groupId === 'ungrouped') {\n conditions.push(`(group_id IS NULL OR group_id = '')`);\n } else {\n conditions.push(`group_id = ?`);\n params.push(query.groupId);\n }\n }\n if (query.pendingTasksOnly) {\n conditions.push(`kind = 'task' AND (task_done IS NULL OR task_done = 0)`);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const rows = db\n .prepare(\n `SELECT note_id, title, kind, status, payload_json, created_at, updated_at,\n pinned, tags_json, snippet, cover_attachment_id, voice_attachment_id,\n voice_duration_sec, attachment_names_json, group_id, last_opened_at,\n task_done, task_due_at, heading_count, task_count, unchecked_task_count, link_count\n FROM notes ${where}`,\n )\n .all(...params) as NoteRow[];\n\n let entries = rows.map(rowToIndexEntry);\n\n if (query.tag) {\n entries = entries.filter((entry) => entry.tags?.includes(query.tag!));\n }\n\n if (query.search) {\n const search = query.search.trim();\n const ftsQuery = escapeFts5Query(search);\n const ftsIds = new Set<string>();\n if (ftsQuery) {\n const ftsRows = db\n .prepare(\n `SELECT note_id FROM notes_fts WHERE notes_fts MATCH ? ORDER BY rank`,\n )\n .all(ftsQuery) as Array<{ note_id: string }>;\n for (const row of ftsRows) {\n ftsIds.add(row.note_id);\n }\n }\n const plainIds = new Set(\n rows\n .filter((row) => rowMatchesPlainSearch(row, search))\n .map((row) => row.note_id),\n );\n entries = entries.filter((entry) => ftsIds.has(entry.id) || plainIds.has(entry.id));\n }\n\n const sortField = query.sortBy || 'createdAt';\n const sortDir = query.sortOrder === 'asc' ? 1 : -1;\n entries = [...entries].sort((a, b) => {\n const aVal = (a[sortField] as number | undefined) ?? 0;\n const bVal = (b[sortField] as number | undefined) ?? 0;\n return (aVal - bVal) * sortDir;\n });\n\n const total = entries.length;\n const offset = query.offset || 0;\n const limit = Math.min(query.limit || 50, 200);\n const items = entries.slice(offset, offset + limit);\n const hasMore = offset + items.length < total;\n\n return { items, total, limit, offset, hasMore };\n}\n"],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@ export type SessionRow = {
|
|
|
23
23
|
workflow_definition_id: string | null;
|
|
24
24
|
workflow_agent_id: string | null;
|
|
25
25
|
workflow_agent_label: string | null;
|
|
26
|
+
project_id: string | null;
|
|
26
27
|
routing_json: string | null;
|
|
27
28
|
custom_data_json: string | null;
|
|
28
29
|
abort_cutoff_timestamp: number | null;
|
|
@@ -77,6 +78,7 @@ export declare function metadataToSessionInsert(sessionKey: string, sessionId: s
|
|
|
77
78
|
workflowDefinitionId: string | null;
|
|
78
79
|
workflowAgentId: string | null;
|
|
79
80
|
workflowAgentLabel: string | null;
|
|
81
|
+
projectId: string | null;
|
|
80
82
|
routingJson: string | null;
|
|
81
83
|
customDataJson: string | null;
|
|
82
84
|
abortCutoffTimestamp: number | null;
|
|
@@ -44,6 +44,7 @@ function sessionRowToMetadata(sessionKey, row) {
|
|
|
44
44
|
workflowDefinitionId: row.workflow_definition_id ?? void 0,
|
|
45
45
|
workflowAgentId: row.workflow_agent_id ?? void 0,
|
|
46
46
|
workflowAgentLabel: row.workflow_agent_label ?? void 0,
|
|
47
|
+
projectId: row.project_id ?? void 0,
|
|
47
48
|
...routing ? { routing } : {},
|
|
48
49
|
...customData ? { customData } : {},
|
|
49
50
|
abortCutoffTimestamp: row.abort_cutoff_timestamp ?? void 0,
|
|
@@ -84,6 +85,7 @@ function metadataToSessionInsert(sessionKey, sessionId, metadata, thinkingLevel,
|
|
|
84
85
|
workflowDefinitionId: metadata.workflowDefinitionId ?? null,
|
|
85
86
|
workflowAgentId: metadata.workflowAgentId ?? null,
|
|
86
87
|
workflowAgentLabel: metadata.workflowAgentLabel ?? null,
|
|
88
|
+
projectId: metadata.projectId ?? null,
|
|
87
89
|
routingJson: metadata.routing ? JSON.stringify(metadata.routing) : null,
|
|
88
90
|
customDataJson: metadata.customData ? JSON.stringify(metadata.customData) : null,
|
|
89
91
|
abortCutoffTimestamp: metadata.abortCutoffTimestamp ?? null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"row-mappers.js","names":["msg"],"sources":["../../../../src/storage/sqlite/row-mappers.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { SessionAgentConfig } from '../../session/config-types.js';\nimport {\n isTranscriptContextEntry,\n type TranscriptStoredRow,\n type XopcTranscriptContextEntry,\n} from '../../session/session-context-for-llm.js';\nimport { SessionStatus, type GlobalSessionStats, type SessionMetadata } from '../../session/types.js';\nimport { resolveAgentIdFromSessionKey } from '../../routing/agent-session-key.js';\nimport { buildDefaultSessionMetadata } from './session-metadata.js';\n\nexport type SessionRow = {\n session_key: string;\n agent_id: string;\n session_id: string;\n status: string;\n name: string | null;\n tags_json: string;\n created_at: number;\n updated_at: number;\n last_accessed_at: number;\n session_started_at: number | null;\n last_interaction_at: number | null;\n source_channel: string;\n source_chat_id: string;\n session_type: string | null;\n hidden_from_session_list: number | null;\n parent_session_key: string | null;\n workflow_run_id: string | null;\n workflow_definition_id: string | null;\n workflow_agent_id: string | null;\n workflow_agent_label: string | null;\n routing_json: string | null;\n custom_data_json: string | null;\n abort_cutoff_timestamp: number | null;\n message_count: number;\n estimated_tokens: number;\n compacted_count: number;\n last_flushed_at: string | null;\n flush_count: number;\n thinking_level: string | null;\n verbose_level: string | null;\n cwd?: string | null;\n};\n\nexport type TranscriptEntryRow = {\n entry_id: string;\n session_id: string;\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n created_at: number;\n};\n\nexport type SessionConfigRow = {\n session_key: string;\n thinking_level: string | null;\n reasoning_level: string | null;\n verbose_level: string | null;\n elevated_mode: string | null;\n model_override: string | null;\n provider_override: string | null;\n working_directory_override: string | null;\n updated_at: number;\n};\n\nfunction parseJson<T>(raw: string | null | undefined): T | undefined {\n if (!raw?.trim()) {\n return undefined;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction isoFromMs(ms: number | null | undefined): string | undefined {\n if (ms == null || !Number.isFinite(ms)) {\n return undefined;\n }\n return new Date(ms).toISOString();\n}\n\nexport function sessionRowToMetadata(sessionKey: string, row: SessionRow): SessionMetadata {\n const defaults = buildDefaultSessionMetadata(sessionKey);\n const routing = parseJson<SessionMetadata['routing']>(row.routing_json);\n const customData = parseJson<Record<string, unknown>>(row.custom_data_json);\n const tags = parseJson<string[]>(row.tags_json) ?? [];\n const createdAt = isoFromMs(row.created_at) ?? defaults.createdAt;\n const updatedAt = isoFromMs(row.updated_at) ?? defaults.updatedAt;\n const lastAccessedAt = isoFromMs(row.last_accessed_at) ?? defaults.lastAccessedAt;\n\n return {\n ...defaults,\n key: sessionKey,\n status: row.status as SessionStatus,\n name: row.name ?? undefined,\n tags,\n createdAt,\n updatedAt,\n lastAccessedAt,\n sessionStartedAt: isoFromMs(row.session_started_at),\n lastInteractionAt: isoFromMs(row.last_interaction_at),\n sourceChannel: row.source_channel,\n sourceChatId: row.source_chat_id,\n sessionType: (row.session_type ?? defaults.sessionType) as SessionMetadata['sessionType'],\n hiddenFromSessionList: Boolean(row.hidden_from_session_list),\n parentSessionKey: row.parent_session_key ?? undefined,\n workflowRunId: row.workflow_run_id ?? undefined,\n workflowDefinitionId: row.workflow_definition_id ?? undefined,\n workflowAgentId: row.workflow_agent_id ?? undefined,\n workflowAgentLabel: row.workflow_agent_label ?? undefined,\n ...(routing ? { routing } : {}),\n ...(customData ? { customData } : {}),\n abortCutoffTimestamp: row.abort_cutoff_timestamp ?? undefined,\n messageCount: row.message_count,\n estimatedTokens: row.estimated_tokens,\n compactedCount: row.compacted_count,\n lastFlushedAt: row.last_flushed_at ?? undefined,\n flushCount: row.flush_count,\n sessionId: row.session_id,\n cwd: row.cwd ?? undefined,\n stats: {\n messageCount: row.message_count,\n tokenCount: row.estimated_tokens,\n lastTurnAt: row.last_interaction_at ?? undefined,\n },\n };\n}\n\nexport function metadataToSessionInsert(\n sessionKey: string,\n sessionId: string,\n metadata: SessionMetadata,\n thinkingLevel?: string | null,\n verboseLevel?: string | null,\n): {\n sessionKey: string;\n agentId: string;\n sessionId: string;\n status: string;\n name: string | null;\n tagsJson: string;\n createdAt: number;\n updatedAt: number;\n lastAccessedAt: number;\n sessionStartedAt: number | null;\n lastInteractionAt: number | null;\n sourceChannel: string;\n sourceChatId: string;\n sessionType: string;\n hiddenFromSessionList: number;\n parentSessionKey: string | null;\n workflowRunId: string | null;\n workflowDefinitionId: string | null;\n workflowAgentId: string | null;\n workflowAgentLabel: string | null;\n routingJson: string | null;\n customDataJson: string | null;\n abortCutoffTimestamp: number | null;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n lastFlushedAt: string | null;\n flushCount: number;\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} {\n const now = Date.now();\n const agentId = metadata.routing?.agentId?.trim().toLowerCase()\n || resolveAgentIdFromSessionKey(sessionKey);\n return {\n sessionKey,\n agentId,\n sessionId,\n status: metadata.status,\n name: metadata.name ?? null,\n tagsJson: JSON.stringify(metadata.tags ?? []),\n createdAt: Date.parse(metadata.createdAt) || now,\n updatedAt: Date.parse(metadata.updatedAt) || now,\n lastAccessedAt: Date.parse(metadata.lastAccessedAt) || now,\n sessionStartedAt: metadata.sessionStartedAt ? Date.parse(metadata.sessionStartedAt) : now,\n lastInteractionAt: metadata.lastInteractionAt ? Date.parse(metadata.lastInteractionAt) : null,\n sourceChannel: metadata.sourceChannel,\n sourceChatId: metadata.sourceChatId,\n sessionType: metadata.sessionType,\n hiddenFromSessionList: metadata.hiddenFromSessionList ? 1 : 0,\n parentSessionKey: metadata.parentSessionKey ?? null,\n workflowRunId: metadata.workflowRunId ?? null,\n workflowDefinitionId: metadata.workflowDefinitionId ?? null,\n workflowAgentId: metadata.workflowAgentId ?? null,\n workflowAgentLabel: metadata.workflowAgentLabel ?? null,\n routingJson: metadata.routing ? JSON.stringify(metadata.routing) : null,\n customDataJson: metadata.customData ? JSON.stringify(metadata.customData) : null,\n abortCutoffTimestamp: metadata.abortCutoffTimestamp ?? null,\n messageCount: metadata.messageCount,\n estimatedTokens: metadata.estimatedTokens,\n compactedCount: metadata.compactedCount,\n lastFlushedAt: metadata.lastFlushedAt ?? null,\n flushCount: metadata.flushCount ?? 0,\n thinkingLevel: thinkingLevel ?? null,\n verboseLevel: verboseLevel ?? null,\n };\n}\n\nexport function sessionConfigRowToConfig(row: SessionConfigRow): SessionAgentConfig {\n return {\n ...(row.thinking_level ? { thinkingLevel: row.thinking_level as SessionAgentConfig['thinkingLevel'] } : {}),\n ...(row.reasoning_level ? { reasoningLevel: row.reasoning_level as SessionAgentConfig['reasoningLevel'] } : {}),\n ...(row.verbose_level ? { verboseLevel: row.verbose_level as SessionAgentConfig['verboseLevel'] } : {}),\n ...(row.elevated_mode ? { elevatedMode: row.elevated_mode as SessionAgentConfig['elevatedMode'] } : {}),\n ...(row.model_override ? { modelOverride: row.model_override } : {}),\n ...(row.provider_override ? { providerOverride: row.provider_override } : {}),\n ...(row.working_directory_override ? { workingDirectoryOverride: row.working_directory_override } : {}),\n updatedAt: row.updated_at,\n };\n}\n\nexport function transcriptEntryRowToStoredRow(row: TranscriptEntryRow): TranscriptStoredRow {\n const payload = JSON.parse(row.payload_json) as TranscriptStoredRow;\n return payload;\n}\n\nexport function classifyStoredRow(row: TranscriptStoredRow): {\n entryKind: 'message' | 'context' | 'compaction';\n role: string | null;\n} {\n if (isTranscriptContextEntry(row)) {\n return { entryKind: 'context', role: null };\n }\n const record = row as AgentMessage & { type?: string };\n if (record.type === 'compaction' || (row as { kind?: string }).kind === 'compaction') {\n return { entryKind: 'compaction', role: null };\n }\n return { entryKind: 'message', role: record.role ?? null };\n}\n\nexport function extractFtsContent(row: TranscriptStoredRow): string {\n if (isTranscriptContextEntry(row)) {\n const ctx = row as XopcTranscriptContextEntry;\n return [ctx.text, ctx.id].filter(Boolean).join(' ');\n }\n const msg = row as AgentMessage;\n return extractTextFromMessageContent((msg as { content?: unknown }).content);\n}\n\nfunction extractTextFromMessageContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n const parts: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== 'object') {\n continue;\n }\n const type = (block as { type?: string }).type;\n if (type === 'text' && typeof (block as { text?: string }).text === 'string') {\n parts.push((block as { text: string }).text);\n }\n }\n return parts.join('\\n');\n}\n\nexport function estimateTokensFromMessages(messages: AgentMessage[]): number {\n let total = 0;\n for (const msg of messages) {\n const content = (msg as { content?: unknown }).content;\n const text = extractTextFromMessageContent(content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n}\n\nexport function buildGlobalSessionStats(sessions: SessionMetadata[]): GlobalSessionStats {\n const byChannel: Record<string, number> = {};\n for (const session of sessions) {\n byChannel[session.sourceChannel] = (byChannel[session.sourceChannel] || 0) + 1;\n }\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0]!.createdAt;\n newestSession = sorted[sorted.length - 1]!.createdAt;\n }\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter(\n (s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE,\n ).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"row-mappers.js","names":["msg"],"sources":["../../../../src/storage/sqlite/row-mappers.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { SessionAgentConfig } from '../../session/config-types.js';\nimport {\n isTranscriptContextEntry,\n type TranscriptStoredRow,\n type XopcTranscriptContextEntry,\n} from '../../session/session-context-for-llm.js';\nimport { SessionStatus, type GlobalSessionStats, type SessionMetadata } from '../../session/types.js';\nimport { resolveAgentIdFromSessionKey } from '../../routing/agent-session-key.js';\nimport { buildDefaultSessionMetadata } from './session-metadata.js';\n\nexport type SessionRow = {\n session_key: string;\n agent_id: string;\n session_id: string;\n status: string;\n name: string | null;\n tags_json: string;\n created_at: number;\n updated_at: number;\n last_accessed_at: number;\n session_started_at: number | null;\n last_interaction_at: number | null;\n source_channel: string;\n source_chat_id: string;\n session_type: string | null;\n hidden_from_session_list: number | null;\n parent_session_key: string | null;\n workflow_run_id: string | null;\n workflow_definition_id: string | null;\n workflow_agent_id: string | null;\n workflow_agent_label: string | null;\n project_id: string | null;\n routing_json: string | null;\n custom_data_json: string | null;\n abort_cutoff_timestamp: number | null;\n message_count: number;\n estimated_tokens: number;\n compacted_count: number;\n last_flushed_at: string | null;\n flush_count: number;\n thinking_level: string | null;\n verbose_level: string | null;\n cwd?: string | null;\n};\n\nexport type TranscriptEntryRow = {\n entry_id: string;\n session_id: string;\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n created_at: number;\n};\n\nexport type SessionConfigRow = {\n session_key: string;\n thinking_level: string | null;\n reasoning_level: string | null;\n verbose_level: string | null;\n elevated_mode: string | null;\n model_override: string | null;\n provider_override: string | null;\n working_directory_override: string | null;\n updated_at: number;\n};\n\nfunction parseJson<T>(raw: string | null | undefined): T | undefined {\n if (!raw?.trim()) {\n return undefined;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction isoFromMs(ms: number | null | undefined): string | undefined {\n if (ms == null || !Number.isFinite(ms)) {\n return undefined;\n }\n return new Date(ms).toISOString();\n}\n\nexport function sessionRowToMetadata(sessionKey: string, row: SessionRow): SessionMetadata {\n const defaults = buildDefaultSessionMetadata(sessionKey);\n const routing = parseJson<SessionMetadata['routing']>(row.routing_json);\n const customData = parseJson<Record<string, unknown>>(row.custom_data_json);\n const tags = parseJson<string[]>(row.tags_json) ?? [];\n const createdAt = isoFromMs(row.created_at) ?? defaults.createdAt;\n const updatedAt = isoFromMs(row.updated_at) ?? defaults.updatedAt;\n const lastAccessedAt = isoFromMs(row.last_accessed_at) ?? defaults.lastAccessedAt;\n\n return {\n ...defaults,\n key: sessionKey,\n status: row.status as SessionStatus,\n name: row.name ?? undefined,\n tags,\n createdAt,\n updatedAt,\n lastAccessedAt,\n sessionStartedAt: isoFromMs(row.session_started_at),\n lastInteractionAt: isoFromMs(row.last_interaction_at),\n sourceChannel: row.source_channel,\n sourceChatId: row.source_chat_id,\n sessionType: (row.session_type ?? defaults.sessionType) as SessionMetadata['sessionType'],\n hiddenFromSessionList: Boolean(row.hidden_from_session_list),\n parentSessionKey: row.parent_session_key ?? undefined,\n workflowRunId: row.workflow_run_id ?? undefined,\n workflowDefinitionId: row.workflow_definition_id ?? undefined,\n workflowAgentId: row.workflow_agent_id ?? undefined,\n workflowAgentLabel: row.workflow_agent_label ?? undefined,\n projectId: row.project_id ?? undefined,\n ...(routing ? { routing } : {}),\n ...(customData ? { customData } : {}),\n abortCutoffTimestamp: row.abort_cutoff_timestamp ?? undefined,\n messageCount: row.message_count,\n estimatedTokens: row.estimated_tokens,\n compactedCount: row.compacted_count,\n lastFlushedAt: row.last_flushed_at ?? undefined,\n flushCount: row.flush_count,\n sessionId: row.session_id,\n cwd: row.cwd ?? undefined,\n stats: {\n messageCount: row.message_count,\n tokenCount: row.estimated_tokens,\n lastTurnAt: row.last_interaction_at ?? undefined,\n },\n };\n}\n\nexport function metadataToSessionInsert(\n sessionKey: string,\n sessionId: string,\n metadata: SessionMetadata,\n thinkingLevel?: string | null,\n verboseLevel?: string | null,\n): {\n sessionKey: string;\n agentId: string;\n sessionId: string;\n status: string;\n name: string | null;\n tagsJson: string;\n createdAt: number;\n updatedAt: number;\n lastAccessedAt: number;\n sessionStartedAt: number | null;\n lastInteractionAt: number | null;\n sourceChannel: string;\n sourceChatId: string;\n sessionType: string;\n hiddenFromSessionList: number;\n parentSessionKey: string | null;\n workflowRunId: string | null;\n workflowDefinitionId: string | null;\n workflowAgentId: string | null;\n workflowAgentLabel: string | null;\n projectId: string | null;\n routingJson: string | null;\n customDataJson: string | null;\n abortCutoffTimestamp: number | null;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n lastFlushedAt: string | null;\n flushCount: number;\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} {\n const now = Date.now();\n const agentId = metadata.routing?.agentId?.trim().toLowerCase()\n || resolveAgentIdFromSessionKey(sessionKey);\n return {\n sessionKey,\n agentId,\n sessionId,\n status: metadata.status,\n name: metadata.name ?? null,\n tagsJson: JSON.stringify(metadata.tags ?? []),\n createdAt: Date.parse(metadata.createdAt) || now,\n updatedAt: Date.parse(metadata.updatedAt) || now,\n lastAccessedAt: Date.parse(metadata.lastAccessedAt) || now,\n sessionStartedAt: metadata.sessionStartedAt ? Date.parse(metadata.sessionStartedAt) : now,\n lastInteractionAt: metadata.lastInteractionAt ? Date.parse(metadata.lastInteractionAt) : null,\n sourceChannel: metadata.sourceChannel,\n sourceChatId: metadata.sourceChatId,\n sessionType: metadata.sessionType,\n hiddenFromSessionList: metadata.hiddenFromSessionList ? 1 : 0,\n parentSessionKey: metadata.parentSessionKey ?? null,\n workflowRunId: metadata.workflowRunId ?? null,\n workflowDefinitionId: metadata.workflowDefinitionId ?? null,\n workflowAgentId: metadata.workflowAgentId ?? null,\n workflowAgentLabel: metadata.workflowAgentLabel ?? null,\n projectId: metadata.projectId ?? null,\n routingJson: metadata.routing ? JSON.stringify(metadata.routing) : null,\n customDataJson: metadata.customData ? JSON.stringify(metadata.customData) : null,\n abortCutoffTimestamp: metadata.abortCutoffTimestamp ?? null,\n messageCount: metadata.messageCount,\n estimatedTokens: metadata.estimatedTokens,\n compactedCount: metadata.compactedCount,\n lastFlushedAt: metadata.lastFlushedAt ?? null,\n flushCount: metadata.flushCount ?? 0,\n thinkingLevel: thinkingLevel ?? null,\n verboseLevel: verboseLevel ?? null,\n };\n}\n\nexport function sessionConfigRowToConfig(row: SessionConfigRow): SessionAgentConfig {\n return {\n ...(row.thinking_level ? { thinkingLevel: row.thinking_level as SessionAgentConfig['thinkingLevel'] } : {}),\n ...(row.reasoning_level ? { reasoningLevel: row.reasoning_level as SessionAgentConfig['reasoningLevel'] } : {}),\n ...(row.verbose_level ? { verboseLevel: row.verbose_level as SessionAgentConfig['verboseLevel'] } : {}),\n ...(row.elevated_mode ? { elevatedMode: row.elevated_mode as SessionAgentConfig['elevatedMode'] } : {}),\n ...(row.model_override ? { modelOverride: row.model_override } : {}),\n ...(row.provider_override ? { providerOverride: row.provider_override } : {}),\n ...(row.working_directory_override ? { workingDirectoryOverride: row.working_directory_override } : {}),\n updatedAt: row.updated_at,\n };\n}\n\nexport function transcriptEntryRowToStoredRow(row: TranscriptEntryRow): TranscriptStoredRow {\n const payload = JSON.parse(row.payload_json) as TranscriptStoredRow;\n return payload;\n}\n\nexport function classifyStoredRow(row: TranscriptStoredRow): {\n entryKind: 'message' | 'context' | 'compaction';\n role: string | null;\n} {\n if (isTranscriptContextEntry(row)) {\n return { entryKind: 'context', role: null };\n }\n const record = row as AgentMessage & { type?: string };\n if (record.type === 'compaction' || (row as { kind?: string }).kind === 'compaction') {\n return { entryKind: 'compaction', role: null };\n }\n return { entryKind: 'message', role: record.role ?? null };\n}\n\nexport function extractFtsContent(row: TranscriptStoredRow): string {\n if (isTranscriptContextEntry(row)) {\n const ctx = row as XopcTranscriptContextEntry;\n return [ctx.text, ctx.id].filter(Boolean).join(' ');\n }\n const msg = row as AgentMessage;\n return extractTextFromMessageContent((msg as { content?: unknown }).content);\n}\n\nfunction extractTextFromMessageContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n const parts: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== 'object') {\n continue;\n }\n const type = (block as { type?: string }).type;\n if (type === 'text' && typeof (block as { text?: string }).text === 'string') {\n parts.push((block as { text: string }).text);\n }\n }\n return parts.join('\\n');\n}\n\nexport function estimateTokensFromMessages(messages: AgentMessage[]): number {\n let total = 0;\n for (const msg of messages) {\n const content = (msg as { content?: unknown }).content;\n const text = extractTextFromMessageContent(content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n}\n\nexport function buildGlobalSessionStats(sessions: SessionMetadata[]): GlobalSessionStats {\n const byChannel: Record<string, number> = {};\n for (const session of sessions) {\n byChannel[session.sourceChannel] = (byChannel[session.sourceChannel] || 0) + 1;\n }\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0]!.createdAt;\n newestSession = sorted[sorted.length - 1]!.createdAt;\n }\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter(\n (s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE,\n ).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n}\n"],"mappings":"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"}
|
|
@@ -31,7 +31,7 @@ function ensureXopcDatabaseSchema(db) {
|
|
|
31
31
|
bootstrapFreshDatabase(db);
|
|
32
32
|
currentVersion = 11;
|
|
33
33
|
}
|
|
34
|
-
applyPendingMigrations(db, { targetVersion:
|
|
34
|
+
applyPendingMigrations(db, { targetVersion: 20 });
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, ensureSchemaMetaTable, ensureXopcDatabaseSchema, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
|