@xopcai/xopc 0.0.145 → 0.0.147
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 +2 -2
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/demo-memory/src/index.js +6 -4
- package/dist/extensions/demo-memory/src/index.js.map +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/Combination-14nmIgxH.js +41 -0
- package/dist/gateway/static/root/assets/activity-store-DXeSSlw1.js +1 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BEUxdhZx.js → agent-avatar-dicebear-CFdJKSjY.js} +1 -1
- package/dist/gateway/static/root/assets/agents-DKjbRpbA.js +2 -0
- package/dist/gateway/static/root/assets/api-Bs69B5zA.js +1 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CRHSZJWi.js → app-management-settings-panel-ag1ViXT_.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BunADYJb.js → appearance-settings-C6iMpwOM.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-CpX3lBFF.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-B6vTkH2A.js → archive-plugin-Dwi3mIBy.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-BjZXzaNv.js → attachment-load-BaCDoq40.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-ZDA-Stg1.js → automation-suggestion-card-CwqtFgnw.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-DlupOxFQ.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DYZfLvPz.js → binary-plugins-DoAYhsxa.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-C3MUlYC9.js → block-editor-Bw72pO7S.js} +31 -31
- package/dist/gateway/static/root/assets/browser-settings-page-BwLoUBcZ.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-vdbAR65Q.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-BAsKbJgm.js +2 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-fTlTnWB4.js → channel-recipient-api-rTG26jpV.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-CxFWy0cE.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-OKlqTtKJ.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-BVFNT2zW.js → connector-logo-DIMP4NWY.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-BjQQwX62.js +2 -0
- package/dist/gateway/static/root/assets/{desktop-pet-_jUmoXd3.js → desktop-pet-kQq2e0BN.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DwGOy4AQ.js → desktop-pet-settings-B6vtozMY.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-akvF0F-u.js → directory-picker-path-field-Dn5-e3v4.js} +1 -1
- package/dist/gateway/static/root/assets/dist-C230izr8.js +1 -0
- package/dist/gateway/static/root/assets/dist-CxGUk8J-.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-BzwIujPG.js +3 -0
- package/dist/gateway/static/root/assets/extension-debug-page-D_e_hsEy.js +1 -0
- package/dist/gateway/static/root/assets/{extension-page-CwWktRNz.js → extension-page-D15CWHBd.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CuggWOIl.js → extension-settings-page-D4P0n1om.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-B7sxIbza.js → gateway-config-api-B4qZctjG.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-DIv0eKMf.js → gateway-settings-BvndV7TP.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DtoLihQE.js → gateway-startup-retry-BpgMunEb.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-C4Kw0mfS.js → goal-create-dialog-DrVHKXHq.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-PHlCwv68.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api-B0jm5JaA.js → goals-config-api-B6YJB_ct.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-Hg9uyO6e.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-N5_iL7es.js → goals-settings-BROWCIcL.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-XzH3lbRR.js → heartbeat-settings-CoCwIOIg.js} +1 -1
- package/dist/gateway/static/root/assets/index-CxkNzw3e.js +259 -0
- package/dist/gateway/static/root/assets/index-DjCSOyaN.css +1 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-Bm_AIYQ7.js +1 -0
- package/dist/gateway/static/root/assets/local-app-workbench-page-BJWFcxwG.js +2 -0
- package/dist/gateway/static/root/assets/local-apps-page-BsiZMrEf.js +1 -0
- package/dist/gateway/static/root/assets/{locale-store-CiM-D9jA.js → locale-store-Bp8joZck.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DOmK61ey.js +7 -0
- package/dist/gateway/static/root/assets/{markdown-split-5rJx5flj.js → markdown-split-Dy7QpaAz.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DYXccwAd.js → media-plugins-3Lxliy9D.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CQGpD-dC.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DGCZom4o.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-BgLyndCO.js → notes-page-BJtT4vSf.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-B_bN7cOR.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-6Al9_sGz.js → page-tabs-DKB6RDre.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-Cm3G72Hv.js → preview-open-alternatives-Br_YhYHp.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-Dq8JouJj.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DvfL7fSe.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DqwMXSXP.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BYtfzu_F.js → save-bar-controls-BgIE9BBh.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CvIRc6yK.js → schedule-field-BXDP-oMz.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CQuKx0n7.js → schema-form-Dhv-SmjG.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-B_mOZkQk.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BHGmH6YS.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-CF3HHM-t.js → settings-collapsible-section-CUTExnlK.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-CwWjoRN6.js → settings-form-section-CmnDhsf0.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-aUFmJa13.js → settings-loading-skeleton-B79dq-XY.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B2UUU9TF.js → settings-page-Db8uAv3X.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-DqpbTWqI.js → settings-page-layout-DaFwstQw.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BUSEj0KK.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BOhlPsS1.js → share-preview-page-Bi00uXzV.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-DzsTYfuX.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-DSsdU081.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DeB1tQZL.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-CWxSvuwD.js → system-settings-panel-Cxaph7hW.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C9Dxcppm.js → theme-store-eCTN1RpG.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-qROAvqTw.js → typed-models-lib-CcZ_tyFY.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-DE9cpn4G.js → use-channel-catalog-C4PWbNoi.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-QTVMx_hG.js +1 -0
- package/dist/gateway/static/root/assets/{user-profile-fields-editor-CWxc0ghP.js → user-profile-fields-editor-CPOO9Jz5.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-DW-Up6vt.js → user-profile-settings-panel-C8cgtSF7.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-D53uRXVr.js → utils-CiZK2KwH.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-b-YZtvUu.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-4zVDOl2G.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-CHe2tfkc.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-D_x9ELJp.js → workflow-run-setup-panel-8SLCYeXE.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DEOFqiWn.js +11 -0
- package/dist/gateway/static/root/index.html +10 -9
- package/dist/package.js +1 -1
- package/dist/src/activity/types.d.ts +1 -1
- package/dist/src/agent/agent-instance-gateway.d.ts +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -1
- package/dist/src/agent/agent-manager.js +5 -4
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/coordinator.js +0 -1
- package/dist/src/agent/background-review/coordinator.js.map +1 -1
- package/dist/src/agent/background-review/settings.d.ts +1 -1
- package/dist/src/agent/background-review/settings.js +10 -17
- package/dist/src/agent/background-review/settings.js.map +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.d.ts +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +2 -3
- package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
- package/dist/src/agent/code-intelligence/binary.js +1 -1
- package/dist/src/agent/context/project-context.d.ts +14 -0
- package/dist/src/agent/context/project-context.js +23 -1
- package/dist/src/agent/context/project-context.js.map +1 -1
- package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
- package/dist/src/agent/context/workspace.d.ts +1 -2
- package/dist/src/agent/context/workspace.js +2 -4
- package/dist/src/agent/context/workspace.js.map +1 -1
- package/dist/src/agent/embedded/run-for-session.js +1 -2
- package/dist/src/agent/embedded/run-for-session.js.map +1 -1
- package/dist/src/agent/embedded/run-turn.js +6 -1
- package/dist/src/agent/embedded/run-turn.js.map +1 -1
- package/dist/src/agent/embedded/types.d.ts +17 -0
- package/dist/src/agent/memory/builtin-memory-store.d.ts +2 -2
- package/dist/src/agent/memory/builtin-memory-store.js +2 -2
- package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
- package/dist/src/agent/memory/builtin-provider.d.ts +1 -3
- package/dist/src/agent/memory/builtin-provider.js +21 -48
- package/dist/src/agent/memory/builtin-provider.js.map +1 -1
- package/dist/src/agent/memory/context/planner.js +48 -9
- package/dist/src/agent/memory/context/planner.js.map +1 -1
- package/dist/src/agent/memory/context/types.d.ts +7 -1
- package/dist/src/agent/memory/context-fence.js +1 -1
- package/dist/src/agent/memory/context-fence.js.map +1 -1
- package/dist/src/agent/memory/create-memory-manager.js +2 -9
- package/dist/src/agent/memory/create-memory-manager.js.map +1 -1
- package/dist/src/agent/memory/dreaming/config.js +5 -10
- package/dist/src/agent/memory/dreaming/config.js.map +1 -1
- package/dist/src/agent/memory/dreaming/promotion-lifecycle.js +2 -2
- package/dist/src/agent/memory/dreaming/promotion-lifecycle.js.map +1 -1
- package/dist/src/agent/memory/dreaming/scope.d.ts +2 -3
- package/dist/src/agent/memory/dreaming/scope.js +8 -8
- package/dist/src/agent/memory/dreaming/scope.js.map +1 -1
- package/dist/src/agent/memory/manager.d.ts +1 -5
- package/dist/src/agent/memory/manager.js +2 -23
- package/dist/src/agent/memory/manager.js.map +1 -1
- package/dist/src/agent/memory/memory-config.d.ts +3 -3
- package/dist/src/agent/memory/memory-config.js +9 -14
- package/dist/src/agent/memory/memory-config.js.map +1 -1
- package/dist/src/agent/memory/types.d.ts +9 -5
- package/dist/src/agent/memory/understanding/service.js +21 -8
- package/dist/src/agent/memory/understanding/service.js.map +1 -1
- package/dist/src/agent/memory/understanding/types.d.ts +5 -0
- package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -1
- package/dist/src/agent/memory/user-context-coordinator.js +6 -2
- package/dist/src/agent/memory/user-context-coordinator.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +2 -2
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/prompt/memory/index.d.ts +1 -2
- package/dist/src/agent/prompt/memory/index.js +6 -7
- package/dist/src/agent/prompt/memory/index.js.map +1 -1
- package/dist/src/agent/prompt/sections/tooling.js +1 -1
- package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
- package/dist/src/agent/service/direct-turn-helpers.js +13 -2
- package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
- package/dist/src/agent/tools/curated-memory-tool.js +4 -4
- package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +1 -1
- package/dist/src/agent/tools/factory.js +2 -2
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/memory-tool.js +1 -1
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/agent/workflow/workflow-child-tools.js +2 -3
- package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -1
- package/dist/src/agent/workspace-runtime/registry.d.ts +5 -5
- package/dist/src/agent/workspace-runtime/registry.js +6 -11
- package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
- package/dist/src/agent-manifest/index.js +2 -2
- package/dist/src/agent-manifest/prompt.js +1 -7
- package/dist/src/agent-manifest/prompt.js.map +1 -1
- package/dist/src/agent-manifest/schema.d.ts +0 -598
- package/dist/src/agent-manifest/schema.js +2 -143
- package/dist/src/agent-manifest/schema.js.map +1 -1
- package/dist/src/agent-manifest/validator.js +0 -12
- package/dist/src/agent-manifest/validator.js.map +1 -1
- package/dist/src/agent-runtime/index.js +1 -1
- package/dist/src/agent-runtime/memory-runtime.d.ts +2 -2
- package/dist/src/agent-runtime/memory-runtime.js +8 -7
- package/dist/src/agent-runtime/memory-runtime.js.map +1 -1
- package/dist/src/agent-runtime/runtime-profile.d.ts +2 -0
- package/dist/src/agent-runtime/runtime-profile.js +2 -2
- package/dist/src/agent-runtime/runtime-profile.js.map +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -2
- package/dist/src/chat-commands/agent-edit.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/init.js +0 -22
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/templates.d.ts +1 -1
- package/dist/src/cli/templates.js +1 -16
- package/dist/src/cli/templates.js.map +1 -1
- package/dist/src/commands/agents.config.js +0 -10
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/paths.d.ts +0 -1
- package/dist/src/config/paths.js +1 -2
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/schema.d.ts +118 -532
- package/dist/src/config/schema.js +32 -26
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/loader.js +2 -1
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +0 -2
- package/dist/src/gateway/agents-admin.js +1 -7
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/capability-presets-admin.d.ts +0 -2
- package/dist/src/gateway/capability-presets-admin.js.map +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +26 -0
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +16 -1
- package/dist/src/gateway/dreaming-automation-reconciler.d.ts +0 -1
- package/dist/src/gateway/dreaming-automation-reconciler.js +41 -63
- package/dist/src/gateway/dreaming-automation-reconciler.js.map +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +3 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +0 -130
- package/dist/src/gateway/hono/lib/config-payload.js +0 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +6 -12
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +6 -0
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
- package/dist/src/gateway/hono/routes/capability-presets.js +2 -2
- package/dist/src/gateway/hono/routes/capability-presets.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +9 -20
- package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
- package/dist/src/gateway/hono/routes/index.js +4 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/local-apps.d.ts +5 -0
- package/dist/src/gateway/hono/routes/local-apps.js +99 -0
- package/dist/src/gateway/hono/routes/local-apps.js.map +1 -0
- package/dist/src/gateway/hono/routes/memory.js +48 -65
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.js +2 -2
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/work-discovery.d.ts +3 -0
- package/dist/src/gateway/hono/routes/work-discovery.js +150 -0
- package/dist/src/gateway/hono/routes/work-discovery.js.map +1 -0
- package/dist/src/gateway/hono/routes/you.d.ts +3 -0
- package/dist/src/gateway/hono/routes/you.js +358 -91
- package/dist/src/gateway/hono/routes/you.js.map +1 -1
- package/dist/src/gateway/service.d.ts +3 -0
- package/dist/src/gateway/service.js +12 -0
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/knowledge/connected-knowledge-pipeline.js +9 -5
- package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -1
- package/dist/src/local-apps/acceptance.d.ts +30 -0
- package/dist/src/local-apps/acceptance.js +93 -0
- package/dist/src/local-apps/acceptance.js.map +1 -0
- package/dist/src/local-apps/index.d.ts +5 -0
- package/dist/src/local-apps/index.js +4 -0
- package/dist/src/local-apps/preview-runtime-bridge.d.ts +4 -0
- package/dist/src/local-apps/preview-runtime-bridge.js +171 -0
- package/dist/src/local-apps/preview-runtime-bridge.js.map +1 -0
- package/dist/src/local-apps/runtime-entry.d.ts +8 -0
- package/dist/src/local-apps/runtime-entry.js +25 -0
- package/dist/src/local-apps/runtime-entry.js.map +1 -0
- package/dist/src/local-apps/scaffold.d.ts +8 -0
- package/dist/src/local-apps/scaffold.js +122 -0
- package/dist/src/local-apps/scaffold.js.map +1 -0
- package/dist/src/local-apps/service.d.ts +37 -0
- package/dist/src/local-apps/service.js +553 -0
- package/dist/src/local-apps/service.js.map +1 -0
- package/dist/src/local-apps/store.d.ts +44 -0
- package/dist/src/local-apps/store.js +195 -0
- package/dist/src/local-apps/store.js.map +1 -0
- package/dist/src/local-apps/types.d.ts +105 -0
- package/dist/src/local-apps/types.js +1 -0
- package/dist/src/storage/sqlite/index.d.ts +3 -2
- package/dist/src/storage/sqlite/index.js +4 -3
- package/dist/src/storage/sqlite/memory-index-repository.d.ts +2 -3
- package/dist/src/storage/sqlite/memory-index-repository.js +16 -20
- package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +18 -10
- package/dist/src/storage/sqlite/memory-records-repository.js +79 -34
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
- package/dist/src/storage/sqlite/memory-reference-consent-repository.d.ts +27 -0
- package/dist/src/storage/sqlite/memory-reference-consent-repository.js +109 -0
- package/dist/src/storage/sqlite/memory-reference-consent-repository.js.map +1 -0
- package/dist/src/storage/sqlite/migrations/038_user_owned_memory.sql +68 -0
- package/dist/src/storage/sqlite/migrations/039_memory_reference_consents.sql +19 -0
- package/dist/src/storage/sqlite/migrations/040_local_apps.sql +32 -0
- package/dist/src/storage/sqlite/migrations/041_local_app_release_lifecycle.sql +17 -0
- package/dist/src/storage/sqlite/migrations/042_work_discovery.sql +41 -0
- package/dist/src/storage/sqlite/migrations/043_local_app_acceptance_runs.sql +16 -0
- package/dist/src/storage/sqlite/migrations/044_repair_work_discovery_foreign_keys.sql +75 -0
- package/dist/src/storage/sqlite/migrations/045_extension_ui_grants.sql +13 -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/schema.js +1 -1
- package/dist/src/storage/sqlite/schema.sql +20 -0
- package/dist/src/user-context/config.d.ts +227 -0
- package/dist/src/user-context/config.js +158 -0
- package/dist/src/user-context/config.js.map +1 -0
- package/dist/src/user-context/import.d.ts +1 -1
- package/dist/src/user-context/import.js +6 -1
- package/dist/src/user-context/import.js.map +1 -1
- package/dist/src/user-context/owner.d.ts +5 -0
- package/dist/src/user-context/owner.js +9 -0
- package/dist/src/user-context/owner.js.map +1 -0
- package/dist/src/user-context/projection.d.ts +11 -9
- package/dist/src/user-context/projection.js +74 -47
- package/dist/src/user-context/projection.js.map +1 -1
- package/dist/src/user-context/runtime.d.ts +13 -0
- package/dist/src/user-context/runtime.js +36 -0
- package/dist/src/user-context/runtime.js.map +1 -0
- package/dist/src/work-discovery/analyzer.d.ts +11 -0
- package/dist/src/work-discovery/analyzer.js +171 -0
- package/dist/src/work-discovery/analyzer.js.map +1 -0
- package/dist/src/work-discovery/index.d.ts +5 -0
- package/dist/src/work-discovery/index.js +5 -0
- package/dist/src/work-discovery/probe.d.ts +12 -0
- package/dist/src/work-discovery/probe.js +335 -0
- package/dist/src/work-discovery/probe.js.map +1 -0
- package/dist/src/work-discovery/repository.d.ts +11 -0
- package/dist/src/work-discovery/repository.js +100 -0
- package/dist/src/work-discovery/repository.js.map +1 -0
- package/dist/src/work-discovery/service.d.ts +30 -0
- package/dist/src/work-discovery/service.js +387 -0
- package/dist/src/work-discovery/service.js.map +1 -0
- package/dist/src/work-discovery/types.d.ts +114 -0
- package/dist/src/work-discovery/types.js +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
- package/package.json +5 -3
- package/skills/engineering/build-xopc-local-app/SKILL.md +45 -0
- package/skills/engineering/build-xopc-local-app/references/app-contract.md +33 -0
- package/skills/engineering/build-xopc-local-app/scripts/validate-local-app.mjs +72 -0
- package/dist/gateway/static/root/assets/Combination-DolwnuKi.js +0 -41
- package/dist/gateway/static/root/assets/activity-center-store-3l9ahUqb.js +0 -1
- package/dist/gateway/static/root/assets/agents-1bpT0xRO.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-Cc4JgVUQ.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-mb0fdVpq.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-Bb0xhaMN.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-BV76yN9a.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CUcGKZqC.js +0 -2
- package/dist/gateway/static/root/assets/channels-settings-BlSDMGdy.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CMgAFGvb.js +0 -2
- package/dist/gateway/static/root/assets/dist-C99RGfAH.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BLetQiYY.js +0 -3
- package/dist/gateway/static/root/assets/extension-debug-page-Cof2ncxE.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BMmzYpZL.js +0 -4
- package/dist/gateway/static/root/assets/goals-page-CGeT77PC.js +0 -2
- package/dist/gateway/static/root/assets/index-CSLF84-Q.css +0 -1
- package/dist/gateway/static/root/assets/index-I-ZCenVw.js +0 -259
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-x_v0nrIi.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-BSh1t8cD.js +0 -7
- package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-BNpTenAI.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-teklkhcE.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-CCB_dKoh.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-VOAFAHYj.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-USYu64Ru.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-oGC8O5h_.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4rP66R1.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Ck9keROI.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-BYLsCuqj.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BNHUyVML.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CpXbQH4n.js +0 -2
- package/dist/gateway/static/root/assets/user-context-page-DbNEK4Yp.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-Cs7MOrg0.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-D1ziG1Pp.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-DHGV_n6i.js +0 -11
- package/dist/src/agent/context/workspace-templates/MEMORY.md +0 -33
- package/dist/src/agent/memory/access-policy.d.ts +0 -12
- package/dist/src/agent/memory/access-policy.js +0 -44
- package/dist/src/agent/memory/access-policy.js.map +0 -1
|
@@ -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 MemoryDisclosurePolicy,\n MemoryDurability,\n MemoryExplicitness,\n MemoryKind,\n MemoryRecord,\n MemorySensitivity,\n MemorySearchResult,\n MemorySignal,\n MemoryStatus,\n} from '../../agent/memory/types.js';\nimport { buildFts5SearchQuery, fts5RankToScore, memoryLexicalSimilarity } 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 canonical_key: string | null;\n explicitness: string;\n durability: string;\n importance: number;\n disclosure_policy: string;\n valid_from: number | null;\n valid_to: number | null;\n review_after: number | null;\n expires_at: number | null;\n supersedes_record_id: string | null;\n conflict_group_id: string | 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 canonicalKey?: string;\n source?: MemoryRecord['source'];\n confidence?: number;\n tags?: string[];\n status?: MemoryStatus;\n sensitivity?: MemorySensitivity;\n explicitness?: MemoryExplicitness;\n durability?: MemoryDurability;\n importance?: number;\n disclosurePolicy?: MemoryDisclosurePolicy;\n evidence?: MemoryRecord['evidence'];\n validFrom?: string;\n validTo?: string;\n reviewAfter?: string;\n expiresAt?: string;\n supersedesRecordId?: string;\n conflictGroupId?: string;\n nowMs?: number;\n}\n\nexport interface ListMemoryRecordsOptions {\n providerId?: string;\n agentId?: string;\n workspaceId?: string;\n projectId?: string;\n /** Include unscoped records plus records visible to this session. */\n visibleToSessionKey?: string;\n /** Return only records that are not scoped to a session. */\n unscopedSessionOnly?: boolean;\n /** Include unscoped records plus records visible to this project. */\n visibleToProjectId?: string;\n /** Return only records that are not scoped to a project. */\n unscopedProjectOnly?: boolean;\n kind?: MemoryKind;\n status?: MemoryStatus;\n canonicalKey?: string;\n limit?: number;\n offset?: number;\n}\n\nexport interface SearchMemoryRecordsOptions {\n query: string;\n agentId?: string;\n workspaceId?: string;\n projectId?: string;\n /** Include unscoped records plus records visible to this session. */\n visibleToSessionKey?: string;\n /** Return only records that are not scoped to a session. */\n unscopedSessionOnly?: boolean;\n /** Include unscoped records plus records visible to this project. */\n visibleToProjectId?: string;\n /** Return only records that are not scoped to a project. */\n unscopedProjectOnly?: boolean;\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 traceId?: string;\n sessionKey?: string;\n turnId?: string;\n phase: 'search' | 'read' | 'write' | 'update' | 'delete' | 'sync' | 'inject' | 'test' | 'understanding' | 'remediation';\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 SetLatestMemoryInjectFeedbackInput {\n sessionKey: string;\n beforeMs?: number;\n requireSelectedRecords?: boolean;\n feedback: SetMemoryTraceFeedbackInput['feedback'];\n nowMs?: number;\n}\n\nexport type FindLatestMemoryInjectTraceInput = Pick<\n SetLatestMemoryInjectFeedbackInput,\n 'sessionKey' | 'beforeMs' | 'requireSelectedRecords'\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 remediation?: MemoryFeedbackRemediationResult;\n durationMs: number;\n createdAt: string;\n}\n\nexport interface MemoryFeedbackRemediationResult {\n triggerTraceId: string;\n evaluatedRecordIds: string[];\n needsReviewRecordIds: string[];\n}\n\nexport const USER_UNDERSTANDING_REMEDIATION_POLICY = {\n minNotHelpful: 2,\n confidencePenalty: 0.2,\n} as const;\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\nexport interface UserUnderstandingQualityMetrics {\n agentId?: string;\n windowDays: number;\n since: string;\n attempts: {\n total: number;\n turn: number;\n background: number;\n };\n candidates: {\n proposed: number;\n created: number;\n deduplicated: number;\n rejectedByPolicy: number;\n };\n records: {\n total: number;\n candidate: number;\n active: number;\n rejected: number;\n needsReview: number;\n stale: number;\n archived: number;\n agingCandidates: number;\n explicit: number;\n inferred: number;\n averageConfidence: number | null;\n };\n decisions: {\n total: number;\n acceptanceRate: number | null;\n };\n recall: {\n total: number;\n helpful: number;\n notHelpful: number;\n mixed: number;\n irrelevant: number;\n helpfulRate: number | null;\n };\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 ...(row.canonical_key ? { canonicalKey: row.canonical_key } : {}),\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 explicitness: row.explicitness as MemoryExplicitness,\n durability: row.durability as MemoryDurability,\n importance: row.importance,\n disclosurePolicy: row.disclosure_policy as MemoryDisclosurePolicy,\n evidence: parseEvidence(row.evidence_json),\n ...(timestampToIso(row.valid_from) ? { validFrom: timestampToIso(row.valid_from) } : {}),\n ...(timestampToIso(row.valid_to) ? { validTo: timestampToIso(row.valid_to) } : {}),\n ...(timestampToIso(row.review_after) ? { reviewAfter: timestampToIso(row.review_after) } : {}),\n ...(timestampToIso(row.expires_at) ? { expiresAt: timestampToIso(row.expires_at) } : {}),\n ...(row.supersedes_record_id ? { supersedesRecordId: row.supersedes_record_id } : {}),\n ...(row.conflict_group_id ? { conflictGroupId: row.conflict_group_id } : {}),\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 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 explicitness = input.explicitness ?? 'inferred';\n const durability = input.durability ?? 'durable';\n const importance = Math.max(0, Math.min(1, input.importance ?? 0.5));\n const disclosurePolicy = input.disclosurePolicy ?? 'referenceable';\n const validFrom = parseOptionalTimestamp(input.validFrom);\n const validTo = parseOptionalTimestamp(input.validTo);\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, canonical_key, explicitness, durability, importance,\n disclosure_policy, valid_from, valid_to, review_after, expires_at,\n supersedes_record_id, conflict_group_id, 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 canonical_key = excluded.canonical_key,\n explicitness = excluded.explicitness,\n durability = excluded.durability,\n importance = excluded.importance,\n disclosure_policy = excluded.disclosure_policy,\n valid_from = excluded.valid_from,\n valid_to = excluded.valid_to,\n review_after = excluded.review_after,\n expires_at = excluded.expires_at,\n supersedes_record_id = excluded.supersedes_record_id,\n conflict_group_id = excluded.conflict_group_id,\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 input.canonicalKey ?? null,\n explicitness,\n durability,\n importance,\n disclosurePolicy,\n validFrom,\n validTo,\n reviewAfter,\n expiresAt,\n input.supersedesRecordId ?? null,\n input.conflictGroupId ?? null,\n now,\n now,\n );\n upsertMemoryRecordFts(db, { ...input, id });\n if (input.supersedesRecordId && input.supersedesRecordId !== id) {\n const target = db.prepare(`SELECT 1 FROM memory_records WHERE record_id = ?`)\n .get(input.supersedesRecordId);\n if (target) {\n db.prepare(\n `INSERT INTO memory_relations (\n relation_id, from_record_id, relation_type, to_record_id,\n confidence, valid_from, valid_to, created_at, updated_at\n ) VALUES (?, ?, 'supersedes', ?, ?, ?, NULL, ?, ?)\n ON CONFLICT(from_record_id, relation_type, to_record_id) DO UPDATE SET\n confidence = excluded.confidence,\n valid_from = excluded.valid_from,\n valid_to = NULL,\n updated_at = excluded.updated_at`,\n ).run(\n randomUUID(),\n id,\n input.supersedesRecordId,\n input.confidence ?? 1,\n validFrom ?? now,\n now,\n now,\n );\n if (status === 'active') {\n db.prepare(\n `UPDATE memory_records\n SET status = 'archived', valid_to = COALESCE(valid_to, ?), updated_at = ?\n WHERE record_id = ? AND status IN ('active', 'needs_review', 'stale')`,\n ).run(now, now, input.supersedesRecordId);\n }\n }\n }\n });\n\n return {\n id,\n kind: input.kind,\n status,\n ...(input.canonicalKey ? { canonicalKey: input.canonicalKey } : {}),\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 explicitness,\n durability,\n importance,\n disclosurePolicy,\n evidence: input.evidence ?? [],\n ...(validFrom != null ? { validFrom: new Date(validFrom).toISOString() } : {}),\n ...(validTo != null ? { validTo: new Date(validTo).toISOString() } : {}),\n ...(reviewAfter != null ? { reviewAfter: new Date(reviewAfter).toISOString() } : {}),\n ...(expiresAt != null ? { expiresAt: new Date(expiresAt).toISOString() } : {}),\n ...(input.supersedesRecordId ? { supersedesRecordId: input.supersedesRecordId } : {}),\n ...(input.conflictGroupId ? { conflictGroupId: input.conflictGroupId } : {}),\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.visibleToSessionKey) {\n where.push('(session_key IS NULL OR session_key = ?)');\n params.push(options.visibleToSessionKey);\n } else if (options.unscopedSessionOnly) {\n where.push('session_key IS NULL');\n }\n if (options.visibleToProjectId) {\n where.push('(project_id IS NULL OR project_id = ?)');\n params.push(options.visibleToProjectId);\n } else if (options.unscopedProjectOnly) {\n where.push('project_id IS NULL');\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 if (options.canonicalKey) {\n where.push('canonical_key = ?');\n params.push(options.canonicalKey);\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 = buildFts5SearchQuery(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.visibleToSessionKey) {\n filters.push('(r.session_key IS NULL OR r.session_key = ?)');\n params.push(options.visibleToSessionKey);\n } else if (options.unscopedSessionOnly) {\n filters.push('r.session_key IS NULL');\n }\n if (options.visibleToProjectId) {\n filters.push('(r.project_id IS NULL OR r.project_id = ?)');\n params.push(options.visibleToProjectId);\n } else if (options.unscopedProjectOnly) {\n filters.push('r.project_id IS NULL');\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 if (rows.length === 0) {\n const fallbackRecords = listMemoryRecords({\n providerId: options.providerId,\n agentId: options.agentId,\n workspaceId: options.workspaceId,\n projectId: options.projectId,\n visibleToSessionKey: options.visibleToSessionKey,\n unscopedSessionOnly: options.unscopedSessionOnly,\n visibleToProjectId: options.visibleToProjectId,\n unscopedProjectOnly: options.unscopedProjectOnly,\n limit: 500,\n }).filter((record) =>\n statuses.includes(record.status ?? 'active')\n && (!options.kinds?.length || options.kinds.includes(record.kind)),\n );\n return fallbackRecords\n .map((record) => ({\n record,\n score: memoryLexicalSimilarity(options.query, record.content),\n snippet: record.content,\n citation: {\n providerId: record.source.provider ?? options.providerId ?? 'local',\n recordId: record.id,\n path: record.source.path,\n lineStart: record.source.lineStart,\n lineEnd: record.source.lineEnd,\n createdAt: record.createdAt,\n },\n }))\n .filter((result) => result.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n }\n\n const bestRank = Math.min(...rows.map((row) => row.rank));\n const worstRank = Math.max(...rows.map((row) => row.rank));\n return rows\n .map((row) => {\n const record = rowToRecord(row);\n const score = fts5RankToScore(row.rank, bestRank, worstRank);\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 = input.traceId ?? 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 agentId?: 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.agentId) {\n where.push('session_key LIKE ?');\n params.push(`agent:${options.agentId}:%`);\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 nowMs = input.nowMs ?? Date.now();\n const feedback = normalizeMemoryTraceFeedback(\n input.feedback,\n nowMs,\n parseMemoryTraceFeedback(existing.feedback_json),\n );\n let remediation: MemoryFeedbackRemediationResult | undefined;\n runSqliteWriteTransaction((db) => {\n db.prepare(`UPDATE memory_trace_events SET feedback_json = ? WHERE trace_id = ?`)\n .run(JSON.stringify(feedback), input.traceId);\n remediation = reconcileUserUnderstandingFeedback(db, existing, feedback, nowMs);\n });\n return {\n ...memoryTraceRowToPayload({ ...existing, feedback_json: JSON.stringify(feedback) }),\n ...(remediation ? { remediation } : {}),\n };\n}\n\nfunction reconcileUserUnderstandingFeedback(\n db: ReturnType<typeof getSqliteDatabase>,\n trace: MemoryTraceRow,\n feedback: MemoryTraceFeedback,\n nowMs: number,\n): MemoryFeedbackRemediationResult | undefined {\n if (trace.phase !== 'inject' || feedback.outcome !== 'not_helpful') return undefined;\n const explicitCorrection = feedback.source === 'system'\n && feedback.reason === 'detected_explicit_user_correction';\n const evaluatedRecordIds: string[] = [];\n const needsReviewRecordIds: string[] = [];\n const actions: Array<Record<string, unknown>> = [];\n\n for (const recordId of new Set(parseStringArray(trace.selected_record_ids_json))) {\n const record = db.prepare(\n `SELECT * FROM memory_records\n WHERE record_id = ?\n AND status = 'active'\n AND tags_json LIKE '%\\\"user-understanding\\\"%'`,\n ).get(recordId) as MemoryRecordRow | undefined;\n if (!record) continue;\n evaluatedRecordIds.push(recordId);\n const feedbackRows = db.prepare(\n `SELECT trace.feedback_json\n FROM memory_trace_events AS trace\n JOIN json_each(trace.selected_record_ids_json) AS selected\n ON selected.value = ?\n WHERE trace.phase = 'inject'`,\n ).all(recordId) as Array<{ feedback_json: string }>;\n let helpful = 0;\n let notHelpful = 0;\n for (const row of feedbackRows) {\n const recordFeedback = parseMemoryTraceFeedback(row.feedback_json);\n if (recordFeedback?.outcome === 'helpful') helpful += 1;\n if (recordFeedback?.outcome === 'not_helpful') notHelpful += 1;\n }\n if (!explicitCorrection && (\n notHelpful < USER_UNDERSTANDING_REMEDIATION_POLICY.minNotHelpful\n || notHelpful <= helpful\n )) {\n continue;\n }\n const nextConfidence = record.confidence == null\n ? null\n : Math.round(Math.max(0.1, record.confidence - USER_UNDERSTANDING_REMEDIATION_POLICY.confidencePenalty) * 1000) / 1000;\n const update = db.prepare(\n `UPDATE memory_records\n SET status = 'needs_review', confidence = ?, review_after = ?, updated_at = ?\n WHERE record_id = ? AND status = 'active'`,\n ).run(nextConfidence, nowMs, nowMs, recordId);\n if (update.changes === 0) continue;\n needsReviewRecordIds.push(recordId);\n actions.push({\n recordId,\n previousStatus: record.status,\n nextStatus: 'needs_review',\n previousConfidence: record.confidence,\n nextConfidence,\n helpful,\n notHelpful,\n });\n }\n\n const result: MemoryFeedbackRemediationResult = {\n triggerTraceId: trace.trace_id,\n evaluatedRecordIds,\n needsReviewRecordIds,\n };\n if (needsReviewRecordIds.length > 0) {\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,\n feedback_json, duration_ms, created_at\n ) VALUES (?, ?, ?, 'remediation', 'user-understanding', ?, ?, ?, NULL, NULL, '{}', 0, ?)`,\n ).run(\n randomUUID(),\n trace.session_key,\n trace.turn_id,\n JSON.stringify({\n triggerTraceId: trace.trace_id,\n reason: explicitCorrection\n ? 'explicit_user_correction'\n : 'repeated_not_helpful_feedback',\n policy: USER_UNDERSTANDING_REMEDIATION_POLICY,\n actions,\n }),\n needsReviewRecordIds.length,\n JSON.stringify(needsReviewRecordIds),\n nowMs,\n );\n }\n return result;\n}\n\nexport function findLatestMemoryInjectTrace(\n input: FindLatestMemoryInjectTraceInput,\n): MemoryTraceEventPayload | null {\n const where = [\n `session_key = ?`,\n `phase = 'inject'`,\n `created_at <= ?`,\n ];\n const row = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_trace_events\n WHERE ${where.join(' AND ')}\n ORDER BY created_at DESC\n LIMIT 1`,\n )\n .get(input.sessionKey, input.beforeMs ?? Date.now()) as MemoryTraceRow | undefined;\n if (!row) return null;\n const trace = memoryTraceRowToPayload(row);\n if (input.requireSelectedRecords && trace.selectedRecordIds.length === 0) return null;\n return trace;\n}\n\nexport function setLatestMemoryInjectFeedback(\n input: SetLatestMemoryInjectFeedbackInput,\n): MemoryTraceEventPayload | null {\n const row = findLatestMemoryInjectTrace(input);\n if (!row) return null;\n return setMemoryTraceFeedback({\n traceId: row.traceId,\n feedback: input.feedback,\n nowMs: input.nowMs,\n });\n}\n\nexport function summarizeMemoryRecallFeedback(options: {\n recordId?: string;\n providerId?: string;\n agentId?: 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.agentId) {\n where.push('session_key LIKE ?');\n params.push(`agent:${options.agentId}:%`);\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 boundedMetricCount(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.max(0, Math.floor(value))\n : 0;\n}\n\nfunction metricRate(numerator: number, denominator: number): number | null {\n return denominator > 0 ? Math.round((numerator / denominator) * 10_000) / 10_000 : null;\n}\n\nexport function summarizeUserUnderstandingQuality(options: {\n agentId?: string;\n windowDays?: number;\n agingCandidateDays?: number;\n nowMs?: number;\n} = {}): UserUnderstandingQualityMetrics {\n const nowMs = options.nowMs ?? Date.now();\n const windowDays = Math.max(1, Math.min(365, Math.floor(options.windowDays ?? 30)));\n const sinceMs = nowMs - windowDays * 24 * 60 * 60 * 1000;\n const agingCandidateDays = Math.max(1, Math.min(windowDays, Math.floor(options.agingCandidateDays ?? 7)));\n const agingCutoffMs = nowMs - agingCandidateDays * 24 * 60 * 60 * 1000;\n const db = getSqliteDatabase();\n const agentWhere = options.agentId ? 'AND agent_id = ?' : '';\n const recordRows = db.prepare(\n `SELECT record_id, status, explicitness, confidence, created_at, updated_at\n FROM memory_records\n WHERE tags_json LIKE '%\"user-understanding\"%'\n ${agentWhere}`,\n ).all(...(options.agentId ? [options.agentId] : [])) as Array<{\n record_id: string;\n status: string;\n explicitness: string;\n confidence: number | null;\n created_at: number;\n updated_at: number;\n }>;\n\n const records: UserUnderstandingQualityMetrics['records'] = {\n total: recordRows.length,\n candidate: 0,\n active: 0,\n rejected: 0,\n needsReview: 0,\n stale: 0,\n archived: 0,\n agingCandidates: 0,\n explicit: 0,\n inferred: 0,\n averageConfidence: null,\n };\n let confidenceTotal = 0;\n let confidenceCount = 0;\n const understandingRecordIds = new Set<string>();\n for (const row of recordRows) {\n understandingRecordIds.add(row.record_id);\n if (row.status === 'candidate') records.candidate += 1;\n if (row.status === 'active') records.active += 1;\n if (row.status === 'rejected') records.rejected += 1;\n if (row.status === 'needs_review') records.needsReview += 1;\n if (row.status === 'stale') records.stale += 1;\n if (row.status === 'archived') records.archived += 1;\n if (row.status === 'candidate' && row.created_at <= agingCutoffMs) records.agingCandidates += 1;\n if (row.explicitness === 'explicit') records.explicit += 1;\n if (row.explicitness === 'inferred') records.inferred += 1;\n if (typeof row.confidence === 'number') {\n confidenceTotal += row.confidence;\n confidenceCount += 1;\n }\n }\n records.averageConfidence = confidenceCount > 0\n ? Math.round((confidenceTotal / confidenceCount) * 10_000) / 10_000\n : null;\n\n const attempts: UserUnderstandingQualityMetrics['attempts'] = { total: 0, turn: 0, background: 0 };\n const candidates: UserUnderstandingQualityMetrics['candidates'] = {\n proposed: 0,\n created: 0,\n deduplicated: 0,\n rejectedByPolicy: 0,\n };\n const understandingTraces = db.prepare(\n `SELECT request_json FROM memory_trace_events\n WHERE phase = 'understanding' AND created_at >= ?`,\n ).all(sinceMs) as Array<{ request_json: string }>;\n for (const row of understandingTraces) {\n const parsed = parseJsonValue(row.request_json, {});\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) continue;\n const request = parsed as Record<string, unknown>;\n if (options.agentId && request.agentId !== options.agentId) continue;\n attempts.total += 1;\n if (request.source === 'turn') attempts.turn += 1;\n if (request.source === 'background') attempts.background += 1;\n candidates.proposed += boundedMetricCount(request.proposed);\n candidates.created += boundedMetricCount(request.created);\n candidates.deduplicated += boundedMetricCount(request.deduplicated);\n candidates.rejectedByPolicy += boundedMetricCount(request.rejected);\n }\n\n const recall: UserUnderstandingQualityMetrics['recall'] = {\n total: 0,\n helpful: 0,\n notHelpful: 0,\n mixed: 0,\n irrelevant: 0,\n helpfulRate: null,\n };\n if (understandingRecordIds.size > 0) {\n const recallRows = db.prepare(\n `SELECT selected_record_ids_json, feedback_json FROM memory_trace_events\n WHERE phase IN ('search', 'inject') AND created_at >= ?`,\n ).all(sinceMs) as Array<{ selected_record_ids_json: string; feedback_json: string }>;\n for (const row of recallRows) {\n const feedback = parseMemoryTraceFeedback(row.feedback_json);\n if (!feedback) continue;\n const selected = parseStringArray(row.selected_record_ids_json);\n if (!selected.some((recordId) => understandingRecordIds.has(recordId))) continue;\n recall.total += 1;\n if (feedback.outcome === 'helpful') recall.helpful += 1;\n if (feedback.outcome === 'not_helpful') recall.notHelpful += 1;\n if (feedback.outcome === 'mixed') recall.mixed += 1;\n if (feedback.outcome === 'irrelevant') recall.irrelevant += 1;\n }\n }\n recall.helpfulRate = metricRate(recall.helpful, recall.total);\n const recentDecisions = recordRows.filter(\n (row) => (row.status === 'active' || row.status === 'rejected') && row.updated_at >= sinceMs,\n );\n const acceptedDecisions = recentDecisions.filter((row) => row.status === 'active').length;\n const decisionTotal = recentDecisions.length;\n\n return {\n ...(options.agentId ? { agentId: options.agentId } : {}),\n windowDays,\n since: new Date(sinceMs).toISOString(),\n attempts,\n candidates,\n records,\n decisions: {\n total: decisionTotal,\n acceptanceRate: metricRate(acceptedDecisions, decisionTotal),\n },\n recall,\n };\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 MemoryDisclosurePolicy,\n MemoryDurability,\n MemoryExplicitness,\n MemoryKind,\n MemoryRecord,\n MemorySensitivity,\n MemorySearchResult,\n MemorySignal,\n MemoryStatus,\n} from '../../agent/memory/types.js';\nimport { buildFts5SearchQuery, fts5RankToScore, memoryLexicalSimilarity } from './fts.js';\nimport { LOCAL_USER_ID } from '../../user-context/owner.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype MemoryRecordRow = {\n record_id: string;\n provider_id: string;\n kind: string;\n user_id: string;\n source_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 canonical_key: string | null;\n explicitness: string;\n durability: string;\n importance: number;\n disclosure_policy: string;\n valid_from: number | null;\n valid_to: number | null;\n review_after: number | null;\n expires_at: number | null;\n supersedes_record_id: string | null;\n conflict_group_id: string | 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 userId?: string;\n sourceAgentId: string;\n workspaceId?: string;\n sessionKey?: string;\n projectId?: string;\n content: string;\n canonicalKey?: string;\n source?: MemoryRecord['source'];\n confidence?: number;\n tags?: string[];\n status?: MemoryStatus;\n sensitivity?: MemorySensitivity;\n explicitness?: MemoryExplicitness;\n durability?: MemoryDurability;\n importance?: number;\n disclosurePolicy?: MemoryDisclosurePolicy;\n evidence?: MemoryRecord['evidence'];\n validFrom?: string;\n validTo?: string;\n reviewAfter?: string;\n expiresAt?: string;\n supersedesRecordId?: string;\n conflictGroupId?: string;\n nowMs?: number;\n}\n\nexport interface ListMemoryRecordsOptions {\n providerId?: string;\n userId?: string;\n sourceAgentId?: string;\n workspaceId?: string;\n projectId?: string;\n /** Include unscoped records plus records visible to this session. */\n visibleToSessionKey?: string;\n /** Return only records that are not scoped to a session. */\n unscopedSessionOnly?: boolean;\n /** Include unscoped records plus records visible to this project. */\n visibleToProjectId?: string;\n /** Return only records that are not scoped to a project. */\n unscopedProjectOnly?: boolean;\n kind?: MemoryKind;\n status?: MemoryStatus;\n canonicalKey?: string;\n limit?: number;\n offset?: number;\n}\n\nexport interface SearchMemoryRecordsOptions {\n query: string;\n userId?: string;\n sourceAgentId?: string;\n workspaceId?: string;\n projectId?: string;\n /** Include unscoped records plus records visible to this session. */\n visibleToSessionKey?: string;\n /** Return only records that are not scoped to a session. */\n unscopedSessionOnly?: boolean;\n /** Include unscoped records plus records visible to this project. */\n visibleToProjectId?: string;\n /** Return only records that are not scoped to a project. */\n unscopedProjectOnly?: boolean;\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 userId?: string;\n sourceAgentId?: 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 userId?: string;\n sourceAgentId?: 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 user_id: string;\n source_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 user_id: string;\n source_agent_id: string | null;\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 traceId?: string;\n sessionKey?: string;\n turnId?: string;\n phase: 'search' | 'read' | 'write' | 'update' | 'delete' | 'sync' | 'inject' | 'test' | 'understanding' | 'remediation';\n providerId: string;\n sourceAgentId?: 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 SetLatestMemoryInjectFeedbackInput {\n sessionKey: string;\n beforeMs?: number;\n requireSelectedRecords?: boolean;\n feedback: SetMemoryTraceFeedbackInput['feedback'];\n nowMs?: number;\n}\n\nexport type FindLatestMemoryInjectTraceInput = Pick<\n SetLatestMemoryInjectFeedbackInput,\n 'sessionKey' | 'beforeMs' | 'requireSelectedRecords'\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 remediation?: MemoryFeedbackRemediationResult;\n durationMs: number;\n createdAt: string;\n}\n\nexport interface MemoryFeedbackRemediationResult {\n triggerTraceId: string;\n evaluatedRecordIds: string[];\n needsReviewRecordIds: string[];\n}\n\nexport const USER_UNDERSTANDING_REMEDIATION_POLICY = {\n minNotHelpful: 2,\n confidencePenalty: 0.2,\n} as const;\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\nexport interface UserUnderstandingQualityMetrics {\n windowDays: number;\n since: string;\n attempts: {\n total: number;\n turn: number;\n background: number;\n };\n candidates: {\n proposed: number;\n created: number;\n deduplicated: number;\n rejectedByPolicy: number;\n };\n records: {\n total: number;\n candidate: number;\n active: number;\n rejected: number;\n needsReview: number;\n stale: number;\n archived: number;\n agingCandidates: number;\n explicit: number;\n inferred: number;\n averageConfidence: number | null;\n };\n decisions: {\n total: number;\n acceptanceRate: number | null;\n };\n recall: {\n total: number;\n helpful: number;\n notHelpful: number;\n mixed: number;\n irrelevant: number;\n helpfulRate: number | null;\n };\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 ...(row.canonical_key ? { canonicalKey: row.canonical_key } : {}),\n scope: {\n userId: row.user_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 provenance: { sourceAgentId: row.source_agent_id },\n content: row.content,\n source: parseSource(row.source_json),\n ...(row.confidence != null ? { confidence: row.confidence } : {}),\n sensitivity: row.sensitivity as MemorySensitivity,\n explicitness: row.explicitness as MemoryExplicitness,\n durability: row.durability as MemoryDurability,\n importance: row.importance,\n disclosurePolicy: row.disclosure_policy as MemoryDisclosurePolicy,\n evidence: parseEvidence(row.evidence_json),\n ...(timestampToIso(row.valid_from) ? { validFrom: timestampToIso(row.valid_from) } : {}),\n ...(timestampToIso(row.valid_to) ? { validTo: timestampToIso(row.valid_to) } : {}),\n ...(timestampToIso(row.review_after) ? { reviewAfter: timestampToIso(row.review_after) } : {}),\n ...(timestampToIso(row.expires_at) ? { expiresAt: timestampToIso(row.expires_at) } : {}),\n ...(row.supersedes_record_id ? { supersedesRecordId: row.supersedes_record_id } : {}),\n ...(row.conflict_group_id ? { conflictGroupId: row.conflict_group_id } : {}),\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 upsertMemoryRecordFts(\n db: ReturnType<typeof getSqliteDatabase>,\n row: Pick<UpsertMemoryRecordInput, 'providerId' | 'kind' | 'userId' | 'sourceAgentId' | '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, user_id, source_agent_id, workspace_id\n ) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(row.content, row.id, row.providerId, row.kind, row.userId ?? LOCAL_USER_ID, row.sourceAgentId, 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 explicitness = input.explicitness ?? 'inferred';\n const durability = input.durability ?? 'durable';\n const importance = Math.max(0, Math.min(1, input.importance ?? 0.5));\n const disclosurePolicy = input.disclosurePolicy ?? 'referenceable';\n const validFrom = parseOptionalTimestamp(input.validFrom);\n const validTo = parseOptionalTimestamp(input.validTo);\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, user_id, source_agent_id, workspace_id, session_key, project_id,\n content, source_json, confidence, tags_json, status, sensitivity,\n evidence_json, canonical_key, explicitness, durability, importance,\n disclosure_policy, valid_from, valid_to, review_after, expires_at,\n supersedes_record_id, conflict_group_id, 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 user_id = excluded.user_id,\n source_agent_id = excluded.source_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 canonical_key = excluded.canonical_key,\n explicitness = excluded.explicitness,\n durability = excluded.durability,\n importance = excluded.importance,\n disclosure_policy = excluded.disclosure_policy,\n valid_from = excluded.valid_from,\n valid_to = excluded.valid_to,\n review_after = excluded.review_after,\n expires_at = excluded.expires_at,\n supersedes_record_id = excluded.supersedes_record_id,\n conflict_group_id = excluded.conflict_group_id,\n updated_at = excluded.updated_at`,\n ).run(\n id,\n input.providerId,\n input.kind,\n input.userId ?? LOCAL_USER_ID,\n input.sourceAgentId,\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 input.canonicalKey ?? null,\n explicitness,\n durability,\n importance,\n disclosurePolicy,\n validFrom,\n validTo,\n reviewAfter,\n expiresAt,\n input.supersedesRecordId ?? null,\n input.conflictGroupId ?? null,\n now,\n now,\n );\n upsertMemoryRecordFts(db, { ...input, id });\n if (input.supersedesRecordId && input.supersedesRecordId !== id) {\n const target = db.prepare(`SELECT 1 FROM memory_records WHERE record_id = ?`)\n .get(input.supersedesRecordId);\n if (target) {\n db.prepare(\n `INSERT INTO memory_relations (\n relation_id, from_record_id, relation_type, to_record_id,\n confidence, valid_from, valid_to, created_at, updated_at\n ) VALUES (?, ?, 'supersedes', ?, ?, ?, NULL, ?, ?)\n ON CONFLICT(from_record_id, relation_type, to_record_id) DO UPDATE SET\n confidence = excluded.confidence,\n valid_from = excluded.valid_from,\n valid_to = NULL,\n updated_at = excluded.updated_at`,\n ).run(\n randomUUID(),\n id,\n input.supersedesRecordId,\n input.confidence ?? 1,\n validFrom ?? now,\n now,\n now,\n );\n if (status === 'active') {\n db.prepare(\n `UPDATE memory_records\n SET status = 'archived', valid_to = COALESCE(valid_to, ?), updated_at = ?\n WHERE record_id = ? AND status IN ('active', 'needs_review', 'stale')`,\n ).run(now, now, input.supersedesRecordId);\n }\n }\n }\n });\n\n return {\n id,\n kind: input.kind,\n status,\n ...(input.canonicalKey ? { canonicalKey: input.canonicalKey } : {}),\n scope: {\n userId: input.userId ?? LOCAL_USER_ID,\n ...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),\n ...(input.sessionKey ? { sessionKey: input.sessionKey } : {}),\n ...(input.projectId ? { projectId: input.projectId } : {}),\n },\n provenance: { sourceAgentId: input.sourceAgentId },\n content: input.content,\n source,\n ...(input.confidence != null ? { confidence: input.confidence } : {}),\n sensitivity,\n explicitness,\n durability,\n importance,\n disclosurePolicy,\n evidence: input.evidence ?? [],\n ...(validFrom != null ? { validFrom: new Date(validFrom).toISOString() } : {}),\n ...(validTo != null ? { validTo: new Date(validTo).toISOString() } : {}),\n ...(reviewAfter != null ? { reviewAfter: new Date(reviewAfter).toISOString() } : {}),\n ...(expiresAt != null ? { expiresAt: new Date(expiresAt).toISOString() } : {}),\n ...(input.supersedesRecordId ? { supersedesRecordId: input.supersedesRecordId } : {}),\n ...(input.conflictGroupId ? { conflictGroupId: input.conflictGroupId } : {}),\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 hasUnresolvedMemoryConflict(conflictGroupId: string): boolean {\n if (!conflictGroupId.trim()) return false;\n const row = getSqliteDatabase().prepare(`\n SELECT COUNT(*) AS count\n FROM memory_records\n WHERE conflict_group_id = ?\n AND status IN ('active', 'candidate', 'needs_review', 'stale')\n `).get(conflictGroupId) as { count: number };\n return Number(row.count) > 1;\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.userId) {\n where.push('user_id = ?');\n params.push(options.userId);\n }\n if (options.sourceAgentId) {\n where.push('source_agent_id = ?');\n params.push(options.sourceAgentId);\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.visibleToSessionKey) {\n where.push('(session_key IS NULL OR session_key = ?)');\n params.push(options.visibleToSessionKey);\n } else if (options.unscopedSessionOnly) {\n where.push('session_key IS NULL');\n }\n if (options.visibleToProjectId) {\n where.push('(project_id IS NULL OR project_id = ?)');\n params.push(options.visibleToProjectId);\n } else if (options.unscopedProjectOnly) {\n where.push('project_id IS NULL');\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 if (options.canonicalKey) {\n where.push('canonical_key = ?');\n params.push(options.canonicalKey);\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 = buildFts5SearchQuery(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.userId) {\n filters.push('f.user_id = ?');\n params.push(options.userId);\n }\n if (options.sourceAgentId) {\n filters.push('f.source_agent_id = ?');\n params.push(options.sourceAgentId);\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.visibleToSessionKey) {\n filters.push('(r.session_key IS NULL OR r.session_key = ?)');\n params.push(options.visibleToSessionKey);\n } else if (options.unscopedSessionOnly) {\n filters.push('r.session_key IS NULL');\n }\n if (options.visibleToProjectId) {\n filters.push('(r.project_id IS NULL OR r.project_id = ?)');\n params.push(options.visibleToProjectId);\n } else if (options.unscopedProjectOnly) {\n filters.push('r.project_id IS NULL');\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 if (rows.length === 0) {\n const fallbackRecords = listMemoryRecords({\n providerId: options.providerId,\n userId: options.userId,\n sourceAgentId: options.sourceAgentId,\n workspaceId: options.workspaceId,\n projectId: options.projectId,\n visibleToSessionKey: options.visibleToSessionKey,\n unscopedSessionOnly: options.unscopedSessionOnly,\n visibleToProjectId: options.visibleToProjectId,\n unscopedProjectOnly: options.unscopedProjectOnly,\n limit: 500,\n }).filter((record) =>\n statuses.includes(record.status ?? 'active')\n && (!options.kinds?.length || options.kinds.includes(record.kind)),\n );\n return fallbackRecords\n .map((record) => ({\n record,\n score: memoryLexicalSimilarity(options.query, record.content),\n snippet: record.content,\n citation: {\n providerId: record.source.provider ?? options.providerId ?? 'local',\n recordId: record.id,\n path: record.source.path,\n lineStart: record.source.lineStart,\n lineEnd: record.source.lineEnd,\n createdAt: record.createdAt,\n },\n }))\n .filter((result) => result.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n }\n\n const bestRank = Math.min(...rows.map((row) => row.rank));\n const worstRank = Math.max(...rows.map((row) => row.rank));\n return rows\n .map((row) => {\n const record = rowToRecord(row);\n const score = fts5RankToScore(row.rank, bestRank, worstRank);\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 markMemoryRecordsConflicted(recordIds: string[], conflictGroupId: string): number {\n const ids = [...new Set(recordIds.filter(Boolean))];\n if (!ids.length || !conflictGroupId.trim()) return 0;\n return runSqliteWriteTransaction((db) => {\n const now = Date.now();\n const update = db.prepare(`\n UPDATE memory_records\n SET conflict_group_id = ?, status = 'needs_review', review_after = ?, updated_at = ?\n WHERE record_id = ? AND status IN ('active', 'candidate', 'needs_review', 'stale')\n `);\n let changed = 0;\n for (const id of ids) changed += Number(update.run(conflictGroupId, now, now, id).changes);\n return changed;\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, user_id, source_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.userId ?? LOCAL_USER_ID,\n input.sourceAgentId ?? 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 userId?: string;\n sourceAgentId?: 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.userId) {\n where.push('user_id = ?');\n params.push(options.userId);\n }\n if (options.sourceAgentId) {\n where.push('source_agent_id = ?');\n params.push(options.sourceAgentId);\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 userId: row.user_id,\n ...(row.source_agent_id ? { sourceAgentId: row.source_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 = input.traceId ?? 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 user_id, source_agent_id\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 LOCAL_USER_ID,\n input.sourceAgentId ?? null,\n );\n });\n return id;\n}\n\nexport function listMemoryTraceEvents(options: {\n providerId?: string;\n userId?: string;\n sourceAgentId?: 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 where.push('user_id = ?');\n params.push(options.userId ?? LOCAL_USER_ID);\n if (options.sourceAgentId) {\n where.push('source_agent_id = ?');\n params.push(options.sourceAgentId);\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 nowMs = input.nowMs ?? Date.now();\n const feedback = normalizeMemoryTraceFeedback(\n input.feedback,\n nowMs,\n parseMemoryTraceFeedback(existing.feedback_json),\n );\n let remediation: MemoryFeedbackRemediationResult | undefined;\n runSqliteWriteTransaction((db) => {\n db.prepare(`UPDATE memory_trace_events SET feedback_json = ? WHERE trace_id = ?`)\n .run(JSON.stringify(feedback), input.traceId);\n remediation = reconcileUserUnderstandingFeedback(db, existing, feedback, nowMs);\n });\n return {\n ...memoryTraceRowToPayload({ ...existing, feedback_json: JSON.stringify(feedback) }),\n ...(remediation ? { remediation } : {}),\n };\n}\n\nfunction reconcileUserUnderstandingFeedback(\n db: ReturnType<typeof getSqliteDatabase>,\n trace: MemoryTraceRow,\n feedback: MemoryTraceFeedback,\n nowMs: number,\n): MemoryFeedbackRemediationResult | undefined {\n if (trace.phase !== 'inject' || feedback.outcome !== 'not_helpful') return undefined;\n const explicitCorrection = feedback.source === 'system'\n && feedback.reason === 'detected_explicit_user_correction';\n const evaluatedRecordIds: string[] = [];\n const needsReviewRecordIds: string[] = [];\n const actions: Array<Record<string, unknown>> = [];\n\n for (const recordId of new Set(parseStringArray(trace.selected_record_ids_json))) {\n const record = db.prepare(\n `SELECT * FROM memory_records\n WHERE record_id = ?\n AND status = 'active'\n AND tags_json LIKE '%\\\"user-understanding\\\"%'`,\n ).get(recordId) as MemoryRecordRow | undefined;\n if (!record) continue;\n evaluatedRecordIds.push(recordId);\n const feedbackRows = db.prepare(\n `SELECT trace.feedback_json\n FROM memory_trace_events AS trace\n JOIN json_each(trace.selected_record_ids_json) AS selected\n ON selected.value = ?\n WHERE trace.phase = 'inject'`,\n ).all(recordId) as Array<{ feedback_json: string }>;\n let helpful = 0;\n let notHelpful = 0;\n for (const row of feedbackRows) {\n const recordFeedback = parseMemoryTraceFeedback(row.feedback_json);\n if (recordFeedback?.outcome === 'helpful') helpful += 1;\n if (recordFeedback?.outcome === 'not_helpful') notHelpful += 1;\n }\n if (!explicitCorrection && (\n notHelpful < USER_UNDERSTANDING_REMEDIATION_POLICY.minNotHelpful\n || notHelpful <= helpful\n )) {\n continue;\n }\n const nextConfidence = record.confidence == null\n ? null\n : Math.round(Math.max(0.1, record.confidence - USER_UNDERSTANDING_REMEDIATION_POLICY.confidencePenalty) * 1000) / 1000;\n const update = db.prepare(\n `UPDATE memory_records\n SET status = 'needs_review', confidence = ?, review_after = ?, updated_at = ?\n WHERE record_id = ? AND status = 'active'`,\n ).run(nextConfidence, nowMs, nowMs, recordId);\n if (update.changes === 0) continue;\n needsReviewRecordIds.push(recordId);\n actions.push({\n recordId,\n previousStatus: record.status,\n nextStatus: 'needs_review',\n previousConfidence: record.confidence,\n nextConfidence,\n helpful,\n notHelpful,\n });\n }\n\n const result: MemoryFeedbackRemediationResult = {\n triggerTraceId: trace.trace_id,\n evaluatedRecordIds,\n needsReviewRecordIds,\n };\n if (needsReviewRecordIds.length > 0) {\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,\n feedback_json, duration_ms, created_at\n ) VALUES (?, ?, ?, 'remediation', 'user-understanding', ?, ?, ?, NULL, NULL, '{}', 0, ?)`,\n ).run(\n randomUUID(),\n trace.session_key,\n trace.turn_id,\n JSON.stringify({\n triggerTraceId: trace.trace_id,\n reason: explicitCorrection\n ? 'explicit_user_correction'\n : 'repeated_not_helpful_feedback',\n policy: USER_UNDERSTANDING_REMEDIATION_POLICY,\n actions,\n }),\n needsReviewRecordIds.length,\n JSON.stringify(needsReviewRecordIds),\n nowMs,\n );\n }\n return result;\n}\n\nexport function findLatestMemoryInjectTrace(\n input: FindLatestMemoryInjectTraceInput,\n): MemoryTraceEventPayload | null {\n const where = [\n `session_key = ?`,\n `phase = 'inject'`,\n `created_at <= ?`,\n ];\n const row = getSqliteDatabase()\n .prepare(\n `SELECT * FROM memory_trace_events\n WHERE ${where.join(' AND ')}\n ORDER BY created_at DESC\n LIMIT 1`,\n )\n .get(input.sessionKey, input.beforeMs ?? Date.now()) as MemoryTraceRow | undefined;\n if (!row) return null;\n const trace = memoryTraceRowToPayload(row);\n if (input.requireSelectedRecords && trace.selectedRecordIds.length === 0) return null;\n return trace;\n}\n\nexport function setLatestMemoryInjectFeedback(\n input: SetLatestMemoryInjectFeedbackInput,\n): MemoryTraceEventPayload | null {\n const row = findLatestMemoryInjectTrace(input);\n if (!row) return null;\n return setMemoryTraceFeedback({\n traceId: row.traceId,\n feedback: input.feedback,\n nowMs: input.nowMs,\n });\n}\n\nexport function summarizeMemoryRecallFeedback(options: {\n recordId?: string;\n providerId?: string;\n sourceAgentId?: 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 where.push('user_id = ?');\n params.push(LOCAL_USER_ID);\n if (options.sourceAgentId) {\n where.push('source_agent_id = ?');\n params.push(options.sourceAgentId);\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 boundedMetricCount(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.max(0, Math.floor(value))\n : 0;\n}\n\nfunction metricRate(numerator: number, denominator: number): number | null {\n return denominator > 0 ? Math.round((numerator / denominator) * 10_000) / 10_000 : null;\n}\n\nexport function summarizeUserUnderstandingQuality(options: {\n windowDays?: number;\n agingCandidateDays?: number;\n nowMs?: number;\n} = {}): UserUnderstandingQualityMetrics {\n const nowMs = options.nowMs ?? Date.now();\n const windowDays = Math.max(1, Math.min(365, Math.floor(options.windowDays ?? 30)));\n const sinceMs = nowMs - windowDays * 24 * 60 * 60 * 1000;\n const agingCandidateDays = Math.max(1, Math.min(windowDays, Math.floor(options.agingCandidateDays ?? 7)));\n const agingCutoffMs = nowMs - agingCandidateDays * 24 * 60 * 60 * 1000;\n const db = getSqliteDatabase();\n const recordRows = db.prepare(\n `SELECT record_id, status, explicitness, confidence, created_at, updated_at\n FROM memory_records\n WHERE tags_json LIKE '%\"user-understanding\"%'\n AND user_id = ?`,\n ).all(LOCAL_USER_ID) as Array<{\n record_id: string;\n status: string;\n explicitness: string;\n confidence: number | null;\n created_at: number;\n updated_at: number;\n }>;\n\n const records: UserUnderstandingQualityMetrics['records'] = {\n total: recordRows.length,\n candidate: 0,\n active: 0,\n rejected: 0,\n needsReview: 0,\n stale: 0,\n archived: 0,\n agingCandidates: 0,\n explicit: 0,\n inferred: 0,\n averageConfidence: null,\n };\n let confidenceTotal = 0;\n let confidenceCount = 0;\n const understandingRecordIds = new Set<string>();\n for (const row of recordRows) {\n understandingRecordIds.add(row.record_id);\n if (row.status === 'candidate') records.candidate += 1;\n if (row.status === 'active') records.active += 1;\n if (row.status === 'rejected') records.rejected += 1;\n if (row.status === 'needs_review') records.needsReview += 1;\n if (row.status === 'stale') records.stale += 1;\n if (row.status === 'archived') records.archived += 1;\n if (row.status === 'candidate' && row.created_at <= agingCutoffMs) records.agingCandidates += 1;\n if (row.explicitness === 'explicit') records.explicit += 1;\n if (row.explicitness === 'inferred') records.inferred += 1;\n if (typeof row.confidence === 'number') {\n confidenceTotal += row.confidence;\n confidenceCount += 1;\n }\n }\n records.averageConfidence = confidenceCount > 0\n ? Math.round((confidenceTotal / confidenceCount) * 10_000) / 10_000\n : null;\n\n const attempts: UserUnderstandingQualityMetrics['attempts'] = { total: 0, turn: 0, background: 0 };\n const candidates: UserUnderstandingQualityMetrics['candidates'] = {\n proposed: 0,\n created: 0,\n deduplicated: 0,\n rejectedByPolicy: 0,\n };\n const understandingTraces = db.prepare(\n `SELECT request_json FROM memory_trace_events\n WHERE phase = 'understanding' AND created_at >= ?`,\n ).all(sinceMs) as Array<{ request_json: string }>;\n for (const row of understandingTraces) {\n const parsed = parseJsonValue(row.request_json, {});\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) continue;\n const request = parsed as Record<string, unknown>;\n attempts.total += 1;\n if (request.source === 'turn') attempts.turn += 1;\n if (request.source === 'background') attempts.background += 1;\n candidates.proposed += boundedMetricCount(request.proposed);\n candidates.created += boundedMetricCount(request.created);\n candidates.deduplicated += boundedMetricCount(request.deduplicated);\n candidates.rejectedByPolicy += boundedMetricCount(request.rejected);\n }\n\n const recall: UserUnderstandingQualityMetrics['recall'] = {\n total: 0,\n helpful: 0,\n notHelpful: 0,\n mixed: 0,\n irrelevant: 0,\n helpfulRate: null,\n };\n if (understandingRecordIds.size > 0) {\n const recallRows = db.prepare(\n `SELECT selected_record_ids_json, feedback_json FROM memory_trace_events\n WHERE phase IN ('search', 'inject') AND created_at >= ?`,\n ).all(sinceMs) as Array<{ selected_record_ids_json: string; feedback_json: string }>;\n for (const row of recallRows) {\n const feedback = parseMemoryTraceFeedback(row.feedback_json);\n if (!feedback) continue;\n const selected = parseStringArray(row.selected_record_ids_json);\n if (!selected.some((recordId) => understandingRecordIds.has(recordId))) continue;\n recall.total += 1;\n if (feedback.outcome === 'helpful') recall.helpful += 1;\n if (feedback.outcome === 'not_helpful') recall.notHelpful += 1;\n if (feedback.outcome === 'mixed') recall.mixed += 1;\n if (feedback.outcome === 'irrelevant') recall.irrelevant += 1;\n }\n }\n recall.helpfulRate = metricRate(recall.helpful, recall.total);\n const recentDecisions = recordRows.filter(\n (row) => (row.status === 'active' || row.status === 'rejected') && row.updated_at >= sinceMs,\n );\n const acceptedDecisions = recentDecisions.filter((row) => row.status === 'active').length;\n const decisionTotal = recentDecisions.length;\n\n return {\n windowDays,\n since: new Date(sinceMs).toISOString(),\n attempts,\n candidates,\n records,\n decisions: {\n total: decisionTotal,\n acceptanceRate: metricRate(acceptedDecisions, decisionTotal),\n },\n recall,\n };\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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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type MemoryReferenceGrantScope = 'once' | 'session' | 'always';
|
|
2
|
+
export type MemoryReferenceConsentStatus = 'pending' | 'granted' | 'denied' | 'consumed';
|
|
3
|
+
export type MemoryReferenceConsent = {
|
|
4
|
+
id: string;
|
|
5
|
+
recordId: string;
|
|
6
|
+
sessionKey: string;
|
|
7
|
+
purpose: string;
|
|
8
|
+
status: MemoryReferenceConsentStatus;
|
|
9
|
+
grantScope?: MemoryReferenceGrantScope;
|
|
10
|
+
expiresAt?: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
updatedAt: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function ensureMemoryReferenceConsentRequest(input: {
|
|
15
|
+
recordId: string;
|
|
16
|
+
sessionKey: string;
|
|
17
|
+
purpose: string;
|
|
18
|
+
}): MemoryReferenceConsent;
|
|
19
|
+
export declare function listMemoryReferenceConsents(options?: {
|
|
20
|
+
status?: MemoryReferenceConsentStatus;
|
|
21
|
+
sessionKey?: string;
|
|
22
|
+
}): MemoryReferenceConsent[];
|
|
23
|
+
export declare function decideMemoryReferenceConsent(id: string, decision: 'deny' | MemoryReferenceGrantScope): MemoryReferenceConsent | undefined;
|
|
24
|
+
export declare function revokeMemoryReferenceConsent(id: string): MemoryReferenceConsent | undefined;
|
|
25
|
+
export declare function hasMemoryReferenceConsent(recordId: string, sessionKey: string): boolean;
|
|
26
|
+
/** Resolve a valid grant. One-shot grants are consumed atomically. */
|
|
27
|
+
export declare function consumeMemoryReferenceConsent(recordId: string, sessionKey: string): boolean;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
//#region src/storage/sqlite/memory-reference-consent-repository.ts
|
|
4
|
+
function fromRow(row) {
|
|
5
|
+
return {
|
|
6
|
+
id: row.consent_id,
|
|
7
|
+
recordId: row.record_id,
|
|
8
|
+
sessionKey: row.session_key,
|
|
9
|
+
purpose: row.purpose,
|
|
10
|
+
status: row.status,
|
|
11
|
+
...row.grant_scope ? { grantScope: row.grant_scope } : {},
|
|
12
|
+
...row.expires_at ? { expiresAt: new Date(row.expires_at).toISOString() } : {},
|
|
13
|
+
createdAt: new Date(row.created_at).toISOString(),
|
|
14
|
+
updatedAt: new Date(row.updated_at).toISOString()
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function ensureMemoryReferenceConsentRequest(input) {
|
|
18
|
+
return runSqliteWriteTransaction((db) => {
|
|
19
|
+
const existing = db.prepare(`
|
|
20
|
+
SELECT * FROM memory_reference_consents
|
|
21
|
+
WHERE record_id = ? AND session_key = ? AND status = 'pending'
|
|
22
|
+
`).get(input.recordId, input.sessionKey);
|
|
23
|
+
if (existing) return fromRow(existing);
|
|
24
|
+
const id = randomUUID();
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
db.prepare(`
|
|
27
|
+
INSERT INTO memory_reference_consents (
|
|
28
|
+
consent_id, record_id, session_key, purpose, status, created_at, updated_at
|
|
29
|
+
) VALUES (?, ?, ?, ?, 'pending', ?, ?)
|
|
30
|
+
`).run(id, input.recordId, input.sessionKey, input.purpose.trim().slice(0, 500), now, now);
|
|
31
|
+
return fromRow(db.prepare("SELECT * FROM memory_reference_consents WHERE consent_id = ?").get(id));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function listMemoryReferenceConsents(options = {}) {
|
|
35
|
+
const clauses = [];
|
|
36
|
+
const params = [];
|
|
37
|
+
if (options.status) {
|
|
38
|
+
clauses.push("status = ?");
|
|
39
|
+
params.push(options.status);
|
|
40
|
+
}
|
|
41
|
+
if (options.sessionKey) {
|
|
42
|
+
clauses.push("session_key = ?");
|
|
43
|
+
params.push(options.sessionKey);
|
|
44
|
+
}
|
|
45
|
+
const where = clauses.length ? ` WHERE ${clauses.join(" AND ")}` : "";
|
|
46
|
+
return getSqliteDatabase().prepare(`SELECT * FROM memory_reference_consents${where} ORDER BY created_at DESC`).all(...params).map(fromRow);
|
|
47
|
+
}
|
|
48
|
+
function decideMemoryReferenceConsent(id, decision) {
|
|
49
|
+
return runSqliteWriteTransaction((db) => {
|
|
50
|
+
const current = db.prepare("SELECT * FROM memory_reference_consents WHERE consent_id = ?").get(id);
|
|
51
|
+
if (!current || current.status !== "pending") return void 0;
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const expiresAt = decision === "once" ? now + 900 * 1e3 : decision === "session" ? now + 1440 * 60 * 1e3 : null;
|
|
54
|
+
db.prepare(`
|
|
55
|
+
UPDATE memory_reference_consents
|
|
56
|
+
SET status = ?, grant_scope = ?, expires_at = ?, updated_at = ?
|
|
57
|
+
WHERE consent_id = ?
|
|
58
|
+
`).run(decision === "deny" ? "denied" : "granted", decision === "deny" ? null : decision, expiresAt, now, id);
|
|
59
|
+
return fromRow(db.prepare("SELECT * FROM memory_reference_consents WHERE consent_id = ?").get(id));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function revokeMemoryReferenceConsent(id) {
|
|
63
|
+
return runSqliteWriteTransaction((db) => {
|
|
64
|
+
const current = db.prepare("SELECT * FROM memory_reference_consents WHERE consent_id = ?").get(id);
|
|
65
|
+
if (!current || current.status !== "granted") return void 0;
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
db.prepare(`
|
|
68
|
+
UPDATE memory_reference_consents
|
|
69
|
+
SET status = 'denied', grant_scope = NULL, expires_at = NULL, updated_at = ?
|
|
70
|
+
WHERE consent_id = ? AND status = 'granted'
|
|
71
|
+
`).run(now, id);
|
|
72
|
+
return fromRow(db.prepare("SELECT * FROM memory_reference_consents WHERE consent_id = ?").get(id));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function hasMemoryReferenceConsent(recordId, sessionKey) {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
return Boolean(getSqliteDatabase().prepare(`
|
|
78
|
+
SELECT 1 FROM memory_reference_consents
|
|
79
|
+
WHERE record_id = ? AND status = 'granted'
|
|
80
|
+
AND (expires_at IS NULL OR expires_at > ?)
|
|
81
|
+
AND (grant_scope = 'always' OR session_key = ?)
|
|
82
|
+
LIMIT 1
|
|
83
|
+
`).get(recordId, now, sessionKey));
|
|
84
|
+
}
|
|
85
|
+
/** Resolve a valid grant. One-shot grants are consumed atomically. */
|
|
86
|
+
function consumeMemoryReferenceConsent(recordId, sessionKey) {
|
|
87
|
+
return runSqliteWriteTransaction((db) => {
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
const row = db.prepare(`
|
|
90
|
+
SELECT * FROM memory_reference_consents
|
|
91
|
+
WHERE record_id = ? AND status = 'granted'
|
|
92
|
+
AND (expires_at IS NULL OR expires_at > ?)
|
|
93
|
+
AND (grant_scope = 'always' OR session_key = ?)
|
|
94
|
+
ORDER BY CASE grant_scope WHEN 'once' THEN 0 WHEN 'session' THEN 1 ELSE 2 END, updated_at DESC
|
|
95
|
+
LIMIT 1
|
|
96
|
+
`).get(recordId, now, sessionKey);
|
|
97
|
+
if (!row) return false;
|
|
98
|
+
if (row.grant_scope === "once") db.prepare(`UPDATE memory_reference_consents SET status = 'consumed', updated_at = ? WHERE consent_id = ?`).run(now, row.consent_id);
|
|
99
|
+
db.prepare(`
|
|
100
|
+
UPDATE memory_reference_consents SET status = 'consumed', updated_at = ?
|
|
101
|
+
WHERE record_id = ? AND session_key = ? AND status = 'pending'
|
|
102
|
+
`).run(now, recordId, sessionKey);
|
|
103
|
+
return true;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { consumeMemoryReferenceConsent, decideMemoryReferenceConsent, ensureMemoryReferenceConsentRequest, hasMemoryReferenceConsent, listMemoryReferenceConsents, revokeMemoryReferenceConsent };
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=memory-reference-consent-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-reference-consent-repository.js","names":[],"sources":["../../../../src/storage/sqlite/memory-reference-consent-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nexport type MemoryReferenceGrantScope = 'once' | 'session' | 'always';\nexport type MemoryReferenceConsentStatus = 'pending' | 'granted' | 'denied' | 'consumed';\n\nexport type MemoryReferenceConsent = {\n id: string;\n recordId: string;\n sessionKey: string;\n purpose: string;\n status: MemoryReferenceConsentStatus;\n grantScope?: MemoryReferenceGrantScope;\n expiresAt?: string;\n createdAt: string;\n updatedAt: string;\n};\n\ntype ConsentRow = {\n consent_id: string;\n record_id: string;\n session_key: string;\n purpose: string;\n status: MemoryReferenceConsentStatus;\n grant_scope: MemoryReferenceGrantScope | null;\n expires_at: number | null;\n created_at: number;\n updated_at: number;\n};\n\nfunction fromRow(row: ConsentRow): MemoryReferenceConsent {\n return {\n id: row.consent_id,\n recordId: row.record_id,\n sessionKey: row.session_key,\n purpose: row.purpose,\n status: row.status,\n ...(row.grant_scope ? { grantScope: row.grant_scope } : {}),\n ...(row.expires_at ? { expiresAt: new Date(row.expires_at).toISOString() } : {}),\n createdAt: new Date(row.created_at).toISOString(),\n updatedAt: new Date(row.updated_at).toISOString(),\n };\n}\n\nexport function ensureMemoryReferenceConsentRequest(input: {\n recordId: string;\n sessionKey: string;\n purpose: string;\n}): MemoryReferenceConsent {\n return runSqliteWriteTransaction((db) => {\n const existing = db.prepare(`\n SELECT * FROM memory_reference_consents\n WHERE record_id = ? AND session_key = ? AND status = 'pending'\n `).get(input.recordId, input.sessionKey) as ConsentRow | undefined;\n if (existing) return fromRow(existing);\n const id = randomUUID();\n const now = Date.now();\n db.prepare(`\n INSERT INTO memory_reference_consents (\n consent_id, record_id, session_key, purpose, status, created_at, updated_at\n ) VALUES (?, ?, ?, ?, 'pending', ?, ?)\n `).run(id, input.recordId, input.sessionKey, input.purpose.trim().slice(0, 500), now, now);\n return fromRow(db.prepare('SELECT * FROM memory_reference_consents WHERE consent_id = ?').get(id) as ConsentRow);\n });\n}\n\nexport function listMemoryReferenceConsents(options: {\n status?: MemoryReferenceConsentStatus;\n sessionKey?: string;\n} = {}): MemoryReferenceConsent[] {\n const clauses: string[] = [];\n const params: string[] = [];\n if (options.status) {\n clauses.push('status = ?');\n params.push(options.status);\n }\n if (options.sessionKey) {\n clauses.push('session_key = ?');\n params.push(options.sessionKey);\n }\n const where = clauses.length ? ` WHERE ${clauses.join(' AND ')}` : '';\n return (getSqliteDatabase().prepare(\n `SELECT * FROM memory_reference_consents${where} ORDER BY created_at DESC`,\n ).all(...params) as ConsentRow[]).map(fromRow);\n}\n\nexport function decideMemoryReferenceConsent(\n id: string,\n decision: 'deny' | MemoryReferenceGrantScope,\n): MemoryReferenceConsent | undefined {\n return runSqliteWriteTransaction((db) => {\n const current = db.prepare('SELECT * FROM memory_reference_consents WHERE consent_id = ?').get(id) as ConsentRow | undefined;\n if (!current || current.status !== 'pending') return undefined;\n const now = Date.now();\n const expiresAt = decision === 'once'\n ? now + 15 * 60 * 1_000\n : decision === 'session' ? now + 24 * 60 * 60 * 1_000 : null;\n db.prepare(`\n UPDATE memory_reference_consents\n SET status = ?, grant_scope = ?, expires_at = ?, updated_at = ?\n WHERE consent_id = ?\n `).run(decision === 'deny' ? 'denied' : 'granted', decision === 'deny' ? null : decision, expiresAt, now, id);\n return fromRow(db.prepare('SELECT * FROM memory_reference_consents WHERE consent_id = ?').get(id) as ConsentRow);\n });\n}\n\nexport function revokeMemoryReferenceConsent(id: string): MemoryReferenceConsent | undefined {\n return runSqliteWriteTransaction((db) => {\n const current = db.prepare('SELECT * FROM memory_reference_consents WHERE consent_id = ?').get(id) as ConsentRow | undefined;\n if (!current || current.status !== 'granted') return undefined;\n const now = Date.now();\n db.prepare(`\n UPDATE memory_reference_consents\n SET status = 'denied', grant_scope = NULL, expires_at = NULL, updated_at = ?\n WHERE consent_id = ? AND status = 'granted'\n `).run(now, id);\n return fromRow(db.prepare('SELECT * FROM memory_reference_consents WHERE consent_id = ?').get(id) as ConsentRow);\n });\n}\n\nexport function hasMemoryReferenceConsent(recordId: string, sessionKey: string): boolean {\n const now = Date.now();\n return Boolean(getSqliteDatabase().prepare(`\n SELECT 1 FROM memory_reference_consents\n WHERE record_id = ? AND status = 'granted'\n AND (expires_at IS NULL OR expires_at > ?)\n AND (grant_scope = 'always' OR session_key = ?)\n LIMIT 1\n `).get(recordId, now, sessionKey));\n}\n\n/** Resolve a valid grant. One-shot grants are consumed atomically. */\nexport function consumeMemoryReferenceConsent(recordId: string, sessionKey: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const now = Date.now();\n const row = db.prepare(`\n SELECT * FROM memory_reference_consents\n WHERE record_id = ? AND status = 'granted'\n AND (expires_at IS NULL OR expires_at > ?)\n AND (grant_scope = 'always' OR session_key = ?)\n ORDER BY CASE grant_scope WHEN 'once' THEN 0 WHEN 'session' THEN 1 ELSE 2 END, updated_at DESC\n LIMIT 1\n `).get(recordId, now, sessionKey) as ConsentRow | undefined;\n if (!row) return false;\n if (row.grant_scope === 'once') {\n db.prepare(`UPDATE memory_reference_consents SET status = 'consumed', updated_at = ? WHERE consent_id = ?`)\n .run(now, row.consent_id);\n }\n db.prepare(`\n UPDATE memory_reference_consents SET status = 'consumed', updated_at = ?\n WHERE record_id = ? AND session_key = ? AND status = 'pending'\n `).run(now, recordId, sessionKey);\n return true;\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
DROP INDEX IF EXISTS idx_memory_records_scope_updated;
|
|
2
|
+
DROP INDEX IF EXISTS idx_memory_records_canonical_status;
|
|
3
|
+
ALTER TABLE memory_records RENAME COLUMN agent_id TO source_agent_id;
|
|
4
|
+
ALTER TABLE memory_records ADD COLUMN user_id TEXT NOT NULL DEFAULT 'local-owner';
|
|
5
|
+
UPDATE memory_records SET kind = 'curated_note' WHERE kind = 'agent_note';
|
|
6
|
+
|
|
7
|
+
DELETE FROM automations
|
|
8
|
+
WHERE automation_id IN ('system-dreaming-light', 'system-dreaming-deep', 'system-dreaming-rem')
|
|
9
|
+
OR automation_id LIKE 'system-dreaming:%';
|
|
10
|
+
CREATE INDEX idx_memory_records_user_updated
|
|
11
|
+
ON memory_records(user_id, updated_at DESC);
|
|
12
|
+
CREATE INDEX idx_memory_records_user_project
|
|
13
|
+
ON memory_records(user_id, project_id);
|
|
14
|
+
CREATE INDEX idx_memory_records_user_status
|
|
15
|
+
ON memory_records(user_id, status);
|
|
16
|
+
CREATE INDEX idx_memory_records_source_agent
|
|
17
|
+
ON memory_records(source_agent_id);
|
|
18
|
+
CREATE INDEX idx_memory_records_canonical_status
|
|
19
|
+
ON memory_records(user_id, canonical_key, status);
|
|
20
|
+
|
|
21
|
+
DROP TABLE memory_records_fts;
|
|
22
|
+
CREATE VIRTUAL TABLE memory_records_fts USING fts5(
|
|
23
|
+
content,
|
|
24
|
+
record_id UNINDEXED,
|
|
25
|
+
provider_id UNINDEXED,
|
|
26
|
+
kind UNINDEXED,
|
|
27
|
+
user_id UNINDEXED,
|
|
28
|
+
source_agent_id UNINDEXED,
|
|
29
|
+
workspace_id UNINDEXED,
|
|
30
|
+
tokenize='unicode61'
|
|
31
|
+
);
|
|
32
|
+
INSERT INTO memory_records_fts (
|
|
33
|
+
content, record_id, provider_id, kind, user_id, source_agent_id, workspace_id
|
|
34
|
+
)
|
|
35
|
+
SELECT content, record_id, provider_id, kind, user_id, source_agent_id, workspace_id
|
|
36
|
+
FROM memory_records
|
|
37
|
+
WHERE trim(content) <> '';
|
|
38
|
+
|
|
39
|
+
DROP INDEX IF EXISTS idx_memory_signals_scope_created;
|
|
40
|
+
ALTER TABLE memory_signals RENAME COLUMN agent_id TO source_agent_id;
|
|
41
|
+
ALTER TABLE memory_signals ADD COLUMN user_id TEXT NOT NULL DEFAULT 'local-owner';
|
|
42
|
+
CREATE INDEX idx_memory_signals_user_created
|
|
43
|
+
ON memory_signals(user_id, created_at DESC);
|
|
44
|
+
CREATE INDEX idx_memory_signals_source_agent_created
|
|
45
|
+
ON memory_signals(source_agent_id, created_at DESC);
|
|
46
|
+
|
|
47
|
+
ALTER TABLE memory_trace_events ADD COLUMN user_id TEXT NOT NULL DEFAULT 'local-owner';
|
|
48
|
+
ALTER TABLE memory_trace_events ADD COLUMN source_agent_id TEXT;
|
|
49
|
+
CREATE INDEX idx_memory_trace_user_created
|
|
50
|
+
ON memory_trace_events(user_id, created_at DESC);
|
|
51
|
+
CREATE INDEX idx_memory_trace_source_agent_created
|
|
52
|
+
ON memory_trace_events(source_agent_id, created_at DESC);
|
|
53
|
+
|
|
54
|
+
ALTER TABLE memory_files RENAME COLUMN agent_id TO user_id;
|
|
55
|
+
DROP TABLE memory_fts;
|
|
56
|
+
CREATE VIRTUAL TABLE memory_fts USING fts5(
|
|
57
|
+
content,
|
|
58
|
+
chunk_id UNINDEXED,
|
|
59
|
+
user_id UNINDEXED,
|
|
60
|
+
path UNINDEXED,
|
|
61
|
+
start_line UNINDEXED,
|
|
62
|
+
end_line UNINDEXED,
|
|
63
|
+
tokenize='unicode61'
|
|
64
|
+
);
|
|
65
|
+
INSERT INTO memory_fts (content, chunk_id, user_id, path, start_line, end_line)
|
|
66
|
+
SELECT chunks.content, chunks.chunk_id, files.user_id, files.path, chunks.start_line, chunks.end_line
|
|
67
|
+
FROM memory_chunks AS chunks
|
|
68
|
+
JOIN memory_files AS files ON files.file_id = chunks.file_id;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS memory_reference_consents (
|
|
2
|
+
consent_id TEXT PRIMARY KEY,
|
|
3
|
+
record_id TEXT NOT NULL,
|
|
4
|
+
session_key TEXT NOT NULL,
|
|
5
|
+
purpose TEXT NOT NULL,
|
|
6
|
+
status TEXT NOT NULL CHECK (status IN ('pending', 'granted', 'denied', 'consumed')),
|
|
7
|
+
grant_scope TEXT CHECK (grant_scope IN ('once', 'session', 'always')),
|
|
8
|
+
expires_at INTEGER,
|
|
9
|
+
created_at INTEGER NOT NULL,
|
|
10
|
+
updated_at INTEGER NOT NULL,
|
|
11
|
+
FOREIGN KEY (record_id) REFERENCES memory_records(record_id) ON DELETE CASCADE
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_memory_reference_consents_pending
|
|
15
|
+
ON memory_reference_consents(record_id, session_key)
|
|
16
|
+
WHERE status = 'pending';
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_memory_reference_consents_resolution
|
|
19
|
+
ON memory_reference_consents(record_id, status, session_key, expires_at);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS local_apps (
|
|
2
|
+
app_id TEXT PRIMARY KEY,
|
|
3
|
+
extension_id TEXT NOT NULL UNIQUE,
|
|
4
|
+
project_id TEXT NOT NULL UNIQUE,
|
|
5
|
+
name TEXT NOT NULL,
|
|
6
|
+
description TEXT,
|
|
7
|
+
idea TEXT NOT NULL,
|
|
8
|
+
status TEXT NOT NULL CHECK (status IN ('preview_ready', 'installed', 'degraded')),
|
|
9
|
+
workspace_root TEXT NOT NULL UNIQUE,
|
|
10
|
+
preview_token TEXT NOT NULL UNIQUE,
|
|
11
|
+
draft_version INTEGER NOT NULL DEFAULT 1,
|
|
12
|
+
active_version INTEGER,
|
|
13
|
+
created_at INTEGER NOT NULL,
|
|
14
|
+
updated_at INTEGER NOT NULL,
|
|
15
|
+
installed_at INTEGER,
|
|
16
|
+
FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_local_apps_updated_at ON local_apps(updated_at DESC);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_local_apps_status ON local_apps(status, updated_at DESC);
|
|
21
|
+
|
|
22
|
+
CREATE TABLE IF NOT EXISTS local_app_releases (
|
|
23
|
+
release_id TEXT PRIMARY KEY,
|
|
24
|
+
app_id TEXT NOT NULL,
|
|
25
|
+
version INTEGER NOT NULL,
|
|
26
|
+
source_hash TEXT NOT NULL,
|
|
27
|
+
created_at INTEGER NOT NULL,
|
|
28
|
+
UNIQUE(app_id, version),
|
|
29
|
+
FOREIGN KEY (app_id) REFERENCES local_apps(app_id) ON DELETE CASCADE
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_local_app_releases_app ON local_app_releases(app_id, version DESC);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
ALTER TABLE local_apps ADD COLUMN installation_state TEXT NOT NULL DEFAULT 'not_installed'
|
|
2
|
+
CHECK (installation_state IN ('not_installed', 'installed'));
|
|
3
|
+
ALTER TABLE local_apps ADD COLUMN enabled INTEGER NOT NULL DEFAULT 0 CHECK (enabled IN (0, 1));
|
|
4
|
+
ALTER TABLE local_apps ADD COLUMN active_release_id TEXT;
|
|
5
|
+
|
|
6
|
+
UPDATE local_apps
|
|
7
|
+
SET installation_state = CASE WHEN active_version IS NULL THEN 'not_installed' ELSE 'installed' END,
|
|
8
|
+
enabled = CASE WHEN active_version IS NULL THEN 0 ELSE 1 END;
|
|
9
|
+
|
|
10
|
+
ALTER TABLE local_app_releases ADD COLUMN artifact_path TEXT;
|
|
11
|
+
ALTER TABLE local_app_releases ADD COLUMN manifest_json TEXT NOT NULL DEFAULT '{}';
|
|
12
|
+
ALTER TABLE local_app_releases ADD COLUMN health_status TEXT NOT NULL DEFAULT 'healthy'
|
|
13
|
+
CHECK (health_status IN ('healthy', 'failed'));
|
|
14
|
+
ALTER TABLE local_app_releases ADD COLUMN activated_at INTEGER;
|
|
15
|
+
|
|
16
|
+
CREATE INDEX IF NOT EXISTS idx_local_app_releases_created
|
|
17
|
+
ON local_app_releases(app_id, created_at DESC);
|