@xopcai/xopc 0.0.143 → 0.0.145
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/Combination-DolwnuKi.js +41 -0
- package/dist/gateway/static/root/assets/{activity-center-store-jITiX2vJ.js → activity-center-store-3l9ahUqb.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DiME9u_P.js → agent-avatar-dicebear-BEUxdhZx.js} +1 -1
- package/dist/gateway/static/root/assets/agents-1bpT0xRO.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-CRHSZJWi.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-DUfdNQqk.js → appearance-settings-BunADYJb.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-Cc4JgVUQ.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-B-Qk6cGr.js → archive-plugin-B6vTkH2A.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Di6EEbQX.js → attachment-load-BjZXzaNv.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-DGxvRxtO.js → automation-suggestion-card-ZDA-Stg1.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-mb0fdVpq.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-ZaOZDH2I.js → binary-plugins-DYZfLvPz.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BTFhjWfg.js → block-editor-C3MUlYC9.js} +46 -46
- package/dist/gateway/static/root/assets/browser-settings-page-Bb0xhaMN.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-BV76yN9a.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CUcGKZqC.js +2 -0
- package/dist/gateway/static/root/assets/channel-recipient-api-fTlTnWB4.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BlSDMGdy.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +1 -0
- package/dist/gateway/static/root/assets/{code-editor--tMO38Tr.js → code-editor-D4_k-6QN.js} +1 -1
- package/dist/gateway/static/root/assets/connector-logo-BVFNT2zW.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-CMgAFGvb.js +2 -0
- package/dist/gateway/static/root/assets/copy-to-clipboard-HOJ79TWv.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet---yzVxVR.js → desktop-pet-_jUmoXd3.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-Dc3gqBJE.js → desktop-pet-settings-DwGOy4AQ.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DCmOGLtK.js → directory-picker-path-field-akvF0F-u.js} +1 -1
- package/dist/gateway/static/root/assets/dist-C99RGfAH.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-BLetQiYY.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-ByaKTTq4.js → extension-debug-page-Cof2ncxE.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BmRzHAZr.js → extension-page-CwWktRNz.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CuggWOIl.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-B7sxIbza.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-DIv0eKMf.js +1 -0
- package/dist/gateway/static/root/assets/gateway-startup-retry-DtoLihQE.js +1 -0
- package/dist/gateway/static/root/assets/goal-create-dialog-C4Kw0mfS.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-BMmzYpZL.js +4 -0
- package/dist/gateway/static/root/assets/goals-config-api-B0jm5JaA.js +1 -0
- package/dist/gateway/static/root/assets/goals-page-CGeT77PC.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-N5_iL7es.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-XzH3lbRR.js +1 -0
- package/dist/gateway/static/root/assets/{html-workspace-editor-COqmtESs.js → html-workspace-editor-C3DKAkMc.js} +1 -1
- package/dist/gateway/static/root/assets/index-CSLF84-Q.css +1 -0
- package/dist/gateway/static/root/assets/index-I-ZCenVw.js +259 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-x_v0nrIi.js +1 -0
- package/dist/gateway/static/root/assets/locale-store-CiM-D9jA.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-BSh1t8cD.js +7 -0
- package/dist/gateway/static/root/assets/markdown-editor-BvUebh3C.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-Dw-yE2-y.js → markdown-split-5rJx5flj.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-D9NNNUgE.js → media-plugins-DYXccwAd.js} +1 -1
- package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BNpTenAI.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-CRgbJGgk.js → notes-page-BgLyndCO.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-teklkhcE.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-D4hkWVt_.js → page-tabs-6Al9_sGz.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CIckv0uy.js → preview-open-alternatives-Cm3G72Hv.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CCB_dKoh.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-VOAFAHYj.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-USYu64Ru.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-B0b42kB2.js → save-bar-controls-BYtfzu_F.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BtjEkczK.js → schedule-field-CvIRc6yK.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Bf0bk3i6.js → schema-form-CQuKx0n7.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-oGC8O5h_.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4rP66R1.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BTKTdTNX.js → settings-collapsible-section-CF3HHM-t.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DL4P0sAj.js → settings-form-section-CwWjoRN6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BV6KFNj4.js → settings-loading-skeleton-aUFmJa13.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B_4DsX5W.js → settings-page-B2UUU9TF.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-CyGlrInB.js → settings-page-layout-DqpbTWqI.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Ck9keROI.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-BOhlPsS1.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-BYLsCuqj.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BNHUyVML.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-CpXbQH4n.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-D85fBBuI.js → system-settings-panel-CWxSvuwD.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-Bt6vAkSB.js → theme-store-C9Dxcppm.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-CwEuYrHX.js → typed-models-lib-qROAvqTw.js} +1 -1
- package/dist/gateway/static/root/assets/use-channel-catalog-DE9cpn4G.js +1 -0
- package/dist/gateway/static/root/assets/user-context-page-DbNEK4Yp.js +1 -0
- package/dist/gateway/static/root/assets/user-profile-fields-editor-CWxc0ghP.js +1 -0
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-CWM0fEpb.js → user-profile-settings-panel-DW-Up6vt.js} +1 -1
- package/dist/gateway/static/root/assets/{utils--BO6xT3u.js → utils-D53uRXVr.js} +1 -1
- package/dist/gateway/static/root/assets/{vendor-codemirror-C1WKjCk5.js → vendor-codemirror--gTq3YAI.js} +17 -17
- package/dist/gateway/static/root/assets/voice-api-key-field-Cs7MOrg0.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-D1ziG1Pp.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-D_x9ELJp.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-DHGV_n6i.js +11 -0
- package/dist/gateway/static/root/index.html +10 -10
- package/dist/package.js +1 -1
- package/dist/src/agent/embedded/session-runner.js +3 -3
- package/dist/src/agent/embedded/session-runner.js.map +1 -1
- package/dist/src/agent/embedded/xopc-auth-storage.d.ts +3 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js +11 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
- package/dist/src/agent/memory/dreaming/config.js +1 -1
- package/dist/src/agent-runtime/index.js +1 -1
- package/dist/src/agent-runtime/runtime-profile.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/schema.d.ts +60 -5
- package/dist/src/config/schema.js +30 -9
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/composio-catalog.js +11 -2
- package/dist/src/connectors/composio-catalog.js.map +1 -1
- package/dist/src/connectors/connector-memory-sync.js +135 -26
- package/dist/src/connectors/connector-memory-sync.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +11 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -0
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +6 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +11 -1
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/status.js +3 -0
- package/dist/src/gateway/hono/routes/status.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.d.ts +5 -5
- package/dist/src/gateway/hono/routes/voice.js +288 -62
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/you.js +5 -1
- package/dist/src/gateway/hono/routes/you.js.map +1 -1
- package/dist/src/gateway/service.d.ts +8 -0
- package/dist/src/gateway/service.js +41 -11
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/knowledge/connected-knowledge-pipeline.d.ts +21 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js +267 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -0
- package/dist/src/knowledge/coordinator.d.ts +10 -0
- package/dist/src/knowledge/coordinator.js +52 -0
- package/dist/src/knowledge/coordinator.js.map +1 -0
- package/dist/src/knowledge/index.d.ts +2 -0
- package/dist/src/knowledge/index.js +3 -1
- package/dist/src/knowledge/ingestion-service.d.ts +7 -1
- package/dist/src/knowledge/ingestion-service.js +28 -0
- package/dist/src/knowledge/ingestion-service.js.map +1 -1
- package/dist/src/knowledge/types.d.ts +18 -0
- package/dist/src/media-understanding/audio-transcription-runner.js +5 -3
- package/dist/src/media-understanding/audio-transcription-runner.js.map +1 -1
- package/dist/src/media-understanding/runner.js +8 -3
- package/dist/src/media-understanding/runner.js.map +1 -1
- package/dist/src/media-understanding/types.d.ts +7 -0
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +26 -1
- package/dist/src/storage/sqlite/knowledge-repository.js +120 -6
- package/dist/src/storage/sqlite/knowledge-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/037_connected_knowledge_pipeline.sql +47 -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/user-context/projection.d.ts +13 -4
- package/dist/src/user-context/projection.js +19 -7
- package/dist/src/user-context/projection.js.map +1 -1
- package/dist/src/voice/language-profile.d.ts +20 -0
- package/dist/src/voice/language-profile.js +156 -0
- package/dist/src/voice/language-profile.js.map +1 -0
- package/dist/src/voice/local/model-files.d.ts +14 -0
- package/dist/src/voice/local/model-files.js +94 -0
- package/dist/src/voice/local/model-files.js.map +1 -0
- package/dist/src/voice/local/model-manager.d.ts +18 -0
- package/dist/src/voice/local/model-manager.js +135 -0
- package/dist/src/voice/local/model-manager.js.map +1 -0
- package/dist/src/voice/local/models.d.ts +28 -0
- package/dist/src/voice/local/models.js +101 -0
- package/dist/src/voice/local/models.js.map +1 -0
- package/dist/src/voice/local/runtime-client.d.ts +26 -0
- package/dist/src/voice/local/runtime-client.js +166 -0
- package/dist/src/voice/local/runtime-client.js.map +1 -0
- package/dist/src/voice/local/runtime-dispatcher.d.ts +9 -0
- package/dist/src/voice/local/runtime-dispatcher.js +22 -0
- package/dist/src/voice/local/runtime-dispatcher.js.map +1 -0
- package/dist/src/voice/local/runtime-worker.d.ts +1 -0
- package/dist/src/voice/local/runtime-worker.js +249 -0
- package/dist/src/voice/local/runtime-worker.js.map +1 -0
- package/dist/src/voice/local/wav.d.ts +6 -0
- package/dist/src/voice/local/wav.js +69 -0
- package/dist/src/voice/local/wav.js.map +1 -0
- package/dist/src/voice/metadata/builtin.js +46 -0
- package/dist/src/voice/metadata/builtin.js.map +1 -1
- package/dist/src/voice/stt/factory.js +8 -2
- package/dist/src/voice/stt/factory.js.map +1 -1
- package/dist/src/voice/stt/providers/alibaba-transcription.js +14 -8
- package/dist/src/voice/stt/providers/alibaba-transcription.js.map +1 -1
- package/dist/src/voice/stt/providers/index.d.ts +1 -0
- package/dist/src/voice/stt/providers/index.js +2 -1
- package/dist/src/voice/stt/providers/local-transcription.d.ts +2 -0
- package/dist/src/voice/stt/providers/local-transcription.js +51 -0
- package/dist/src/voice/stt/providers/local-transcription.js.map +1 -0
- package/dist/src/voice/stt/providers/openai-transcription.js +5 -3
- package/dist/src/voice/stt/providers/openai-transcription.js.map +1 -1
- package/dist/src/voice/stt/transcribe-core.js +5 -4
- package/dist/src/voice/stt/transcribe-core.js.map +1 -1
- package/dist/src/voice/stt/types.d.ts +4 -0
- package/dist/src/voice/stt/types.js +5 -4
- package/dist/src/voice/stt/types.js.map +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/voice/tts/providers/edge-speech.js.map +1 -1
- package/dist/src/voice/tts/types.js +3 -3
- package/dist/src/voice/tts/types.js.map +1 -1
- package/package.json +4 -2
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
- package/dist/gateway/static/root/assets/agents-DzBXhZXO.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-Cuo7SbUP.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-CKuKK5AU.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-D-FF_WKV.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-BvKLdEQt.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-BU-pVZiZ.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DgyeEwzh.js +0 -2
- package/dist/gateway/static/root/assets/channel-recipient-api-DuulnhIj.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BbVsdD_I.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-B5lVrUsN.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-B8QqVy3I.js +0 -2
- package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BgT5KZVe.js +0 -3
- package/dist/gateway/static/root/assets/extension-settings-page-JUtwIhbF.js +0 -1
- package/dist/gateway/static/root/assets/fetch-8QNN25Sh.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-CxicF50_.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-Bz2I_EO5.js +0 -1
- package/dist/gateway/static/root/assets/gateway-startup-retry-5YBnS9Fj.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-VcoyuZGN.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-Cilkap2G.js +0 -4
- package/dist/gateway/static/root/assets/goals-config-api-CwcHRdC0.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-C6eoni4z.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-o4i-qAOS.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-DQQkETX8.js +0 -1
- package/dist/gateway/static/root/assets/index-BKRqeRHm.js +0 -257
- package/dist/gateway/static/root/assets/index-CphxClbs.css +0 -1
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-LT6qB-ua.js +0 -1
- package/dist/gateway/static/root/assets/locale-store-Ba72bFmP.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-Dli2e3fS.js +0 -7
- package/dist/gateway/static/root/assets/markdown-editor-jD1LPiP-.js +0 -2
- package/dist/gateway/static/root/assets/messages-CzKxM6I4.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-DVn2Q1Ik.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-BxZRHLWx.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-Doz53xLn.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DoMtcRLg.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BeHVEgrm.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Bk4bQsBe.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DdFyaRMm.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DJ1_MLgX.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-BQBV7YrV.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-0VuFLBeU.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DO87Ckeo.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-D3VdtxAn.js +0 -2
- package/dist/gateway/static/root/assets/use-channel-catalog-COXIfXcl.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-m9JVQ73F.js +0 -1
- package/dist/gateway/static/root/assets/user-profile-fields-editor-DtQiuY7E.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-LoT3wgZ9.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-DSTMz_mO.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-B35lJzvm.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-BfSm9oR8.js +0 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","names":[],"sources":["../../../src/media-understanding/runner.ts"],"sourcesContent":["/**\n * MediaUnderstanding capability runner.\n *\n * Orchestrates per-attachment fallback across providers for one capability\n * (audio / image / video). For each attachment:\n * 1. Build the candidate provider list from the registry, ordered by\n * autoPriority (lower = first), with caller-supplied `providerOrder`\n * overriding when set.\n * 2. Try providers in order. A provider is SKIPPED when it does not declare\n * the requested capability OR does not implement the corresponding method.\n * 3. The first provider whose capability call resolves successfully wins;\n * its output is recorded as `chosen`. Failures are recorded as attempts\n * and the next provider is tried.\n * 4. When ALL providers for an attachment fail, the attachment outcome is\n * `failed`; when no provider was even eligible (capability not registered),\n * it's `disabled`.\n *\n * DECISION (per docs/voice-rearchitecture.md §5):\n * - This is a thin runner only. xopc v2.0 ships ONLY audio; image/video\n * branches exist as type-level fall-throughs (the runner happily handles\n * them once a provider registers describeImage / describeVideo, but no\n * image-specific helpers — like multi-image batching — live here yet).\n * - We deliberately do NOT port openclaw's `MediaAttachmentCache` (which\n * handles temp-file extraction, SSRF re-validation on remote URLs, etc.).\n * For xopc v2.0 the caller is expected to pre-load the buffer; remote URL\n * fetching is the caller's responsibility (typically the channel adapter\n * does this before invoking the runner).\n */\n\nimport { createLogger } from '../utils/logger.js';\n\nimport { listProvidersForCapability } from './registry.js';\nimport type {\n AudioTranscriptionRequest,\n AudioTranscriptionResult,\n CapabilityAttachmentInput,\n ImageDescriptionRequest,\n ImageDescriptionResult,\n MediaCapability,\n MediaUnderstandingAttachmentDecision,\n MediaUnderstandingKind,\n MediaUnderstandingModelDecision,\n MediaUnderstandingOutput,\n MediaUnderstandingProvider,\n MediaUnderstandingResult,\n VideoDescriptionRequest,\n VideoDescriptionResult,\n} from './types.js';\n\nconst log = createLogger('MediaUnderstandingRunner');\n\nexport type { CapabilityAttachmentInput } from './types.js';\n\nexport interface RunCapabilityOptions {\n capability: MediaCapability;\n attachments: readonly CapabilityAttachmentInput[];\n /** Caller-supplied ordering. When set, overrides registry autoPriority. */\n providerOrder?: readonly string[];\n /**\n * Per-provider request builder. Returned shape MUST match the capability:\n * audio → AudioTranscriptionRequest\n * image → ImageDescriptionRequest\n * video → VideoDescriptionRequest\n * The runner does not synthesize api keys / urls — that's the caller's job\n * (typically reads from cfg.tools.media.audio.providers[providerId]).\n */\n buildRequest: (params: {\n provider: MediaUnderstandingProvider;\n attachment: CapabilityAttachmentInput;\n }) =>\n | AudioTranscriptionRequest\n | ImageDescriptionRequest\n | VideoDescriptionRequest\n | undefined;\n /** Optional global timeout signal (e.g. from agent-level cancellation). */\n signal?: AbortSignal;\n}\n\nconst KIND_BY_CAPABILITY: Record<MediaCapability, MediaUnderstandingKind> = {\n audio: 'audio.transcription',\n image: 'image.description',\n video: 'video.description',\n};\n\nfunction orderProviders(\n providers: MediaUnderstandingProvider[],\n capability: MediaCapability,\n override?: readonly string[],\n): MediaUnderstandingProvider[] {\n if (override && override.length > 0) {\n const lookup = new Map(providers.map((p) => [p.id.toLowerCase(), p]));\n const ordered: MediaUnderstandingProvider[] = [];\n const used = new Set<string>();\n for (const id of override) {\n const provider = lookup.get(id.toLowerCase());\n if (provider && !used.has(provider.id)) {\n ordered.push(provider);\n used.add(provider.id);\n }\n }\n // Append any registered providers not in the override (preserves capability\n // availability when the override list is incomplete).\n for (const provider of providers) {\n if (!used.has(provider.id)) {\n ordered.push(provider);\n }\n }\n return ordered;\n }\n return [...providers].sort((a, b) => {\n const aPriority = a.autoPriority?.[capability] ?? Number.MAX_SAFE_INTEGER;\n const bPriority = b.autoPriority?.[capability] ?? Number.MAX_SAFE_INTEGER;\n return aPriority - bPriority;\n });\n}\n\nfunction pickMethod(\n provider: MediaUnderstandingProvider,\n capability: MediaCapability,\n): ((req: never) => Promise<{ text: string; model?: string }>) | undefined {\n if (capability === 'audio') {\n return provider.transcribeAudio as (\n req: AudioTranscriptionRequest,\n ) => Promise<AudioTranscriptionResult>;\n }\n if (capability === 'image') {\n return provider.describeImage as (\n req: ImageDescriptionRequest,\n ) => Promise<ImageDescriptionResult>;\n }\n return provider.describeVideo as (\n req: VideoDescriptionRequest,\n ) => Promise<VideoDescriptionResult>;\n}\n\nexport async function runCapability(\n options: RunCapabilityOptions,\n): Promise<MediaUnderstandingResult> {\n const eligibleProviders = listProvidersForCapability(options.capability);\n\n // Capability outcome is 'disabled' when no registered provider implements it.\n if (eligibleProviders.length === 0) {\n log.debug(\n { capability: options.capability },\n `No providers registered for capability \"${options.capability}\"`,\n );\n return {\n decision: {\n capability: options.capability,\n outcome: 'disabled',\n attachments: options.attachments.map((a) => ({\n attachmentIndex: a.attachmentIndex,\n attempts: [],\n })),\n },\n outputs: [],\n };\n }\n\n if (options.attachments.length === 0) {\n return {\n decision: { capability: options.capability, outcome: 'no-attachment', attachments: [] },\n outputs: [],\n };\n }\n\n const providers = orderProviders(eligibleProviders, options.capability, options.providerOrder);\n const attachmentDecisions: MediaUnderstandingAttachmentDecision[] = [];\n const outputs: MediaUnderstandingOutput[] = [];\n\n for (const attachment of options.attachments) {\n options.signal?.throwIfAborted();\n\n const attempts: MediaUnderstandingModelDecision[] = [];\n let chosen: MediaUnderstandingModelDecision | undefined;\n\n for (const provider of providers) {\n const method = pickMethod(provider, options.capability);\n if (!method) {\n // Defensive: registry filter should already exclude these. Recorded as\n // skipped so callers can diagnose registry/method mismatches.\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'skipped',\n reason: `provider \"${provider.id}\" missing method for capability \"${options.capability}\"`,\n });\n continue;\n }\n const request = options.buildRequest({ provider, attachment });\n if (!request) {\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'skipped',\n reason: 'caller buildRequest returned undefined (likely missing config)',\n });\n continue;\n }\n try {\n // We narrow `method` by capability above; cast through `unknown` to keep\n // a single call site without per-capability ceremony.\n const callResult = await (method as (req: unknown) => Promise<{ text: string; model?: string }>)(\n request,\n );\n const text = callResult.text?.trim() ?? '';\n if (!text) {\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'failed',\n reason: 'empty transcription/description text',\n model: callResult.model,\n });\n continue;\n }\n chosen = {\n provider: provider.id,\n type: 'provider',\n outcome: 'success',\n model: callResult.model,\n };\n attempts.push(chosen);\n outputs.push({\n kind: KIND_BY_CAPABILITY[options.capability],\n attachmentIndex: attachment.attachmentIndex,\n text,\n provider: provider.id,\n model: callResult.model,\n });\n break;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n log.warn(\n {\n err: error,\n providerId: provider.id,\n capability: options.capability,\n attachmentIndex: attachment.attachmentIndex,\n },\n `Provider \"${provider.id}\" failed for ${options.capability}: ${reason}`,\n );\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'failed',\n reason,\n });\n }\n }\n\n attachmentDecisions.push({\n attachmentIndex: attachment.attachmentIndex,\n attempts,\n ...(chosen ? { chosen } : {}),\n });\n }\n\n const overallOutcome = attachmentDecisions.every((d) => d.chosen)\n ? 'success'\n : attachmentDecisions.some((d) => d.chosen)\n ? 'success' // partial success — at least one attachment succeeded\n : 'failed';\n\n return {\n decision: {\n capability: options.capability,\n outcome: overallOutcome,\n attachments: attachmentDecisions,\n },\n outputs,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runner.js","names":[],"sources":["../../../src/media-understanding/runner.ts"],"sourcesContent":["/**\n * MediaUnderstanding capability runner.\n *\n * Orchestrates per-attachment fallback across providers for one capability\n * (audio / image / video). For each attachment:\n * 1. Build the candidate provider list from the registry, ordered by\n * autoPriority (lower = first), with caller-supplied `providerOrder`\n * overriding when set.\n * 2. Try providers in order. A provider is SKIPPED when it does not declare\n * the requested capability OR does not implement the corresponding method.\n * 3. The first provider whose capability call resolves successfully wins;\n * its output is recorded as `chosen`. Failures are recorded as attempts\n * and the next provider is tried.\n * 4. When ALL providers for an attachment fail, the attachment outcome is\n * `failed`; when no provider was even eligible (capability not registered),\n * it's `disabled`.\n *\n * DECISION (per docs/voice-rearchitecture.md §5):\n * - This is a thin runner only. xopc v2.0 ships ONLY audio; image/video\n * branches exist as type-level fall-throughs (the runner happily handles\n * them once a provider registers describeImage / describeVideo, but no\n * image-specific helpers — like multi-image batching — live here yet).\n * - We deliberately do NOT port openclaw's `MediaAttachmentCache` (which\n * handles temp-file extraction, SSRF re-validation on remote URLs, etc.).\n * For xopc v2.0 the caller is expected to pre-load the buffer; remote URL\n * fetching is the caller's responsibility (typically the channel adapter\n * does this before invoking the runner).\n */\n\nimport { createLogger } from '../utils/logger.js';\n\nimport { listProvidersForCapability } from './registry.js';\nimport type {\n AudioTranscriptionRequest,\n AudioTranscriptionResult,\n CapabilityAttachmentInput,\n ImageDescriptionRequest,\n ImageDescriptionResult,\n MediaCapability,\n MediaUnderstandingAttachmentDecision,\n MediaUnderstandingKind,\n MediaUnderstandingModelDecision,\n MediaUnderstandingOutput,\n MediaUnderstandingProvider,\n MediaUnderstandingResult,\n VideoDescriptionRequest,\n VideoDescriptionResult,\n} from './types.js';\n\nconst log = createLogger('MediaUnderstandingRunner');\n\nexport type { CapabilityAttachmentInput } from './types.js';\n\nexport interface RunCapabilityOptions {\n capability: MediaCapability;\n attachments: readonly CapabilityAttachmentInput[];\n /** Caller-supplied ordering. When set, overrides registry autoPriority. */\n providerOrder?: readonly string[];\n /**\n * Per-provider request builder. Returned shape MUST match the capability:\n * audio → AudioTranscriptionRequest\n * image → ImageDescriptionRequest\n * video → VideoDescriptionRequest\n * The runner does not synthesize api keys / urls — that's the caller's job\n * (typically reads from cfg.tools.media.audio.providers[providerId]).\n */\n buildRequest: (params: {\n provider: MediaUnderstandingProvider;\n attachment: CapabilityAttachmentInput;\n }) =>\n | AudioTranscriptionRequest\n | ImageDescriptionRequest\n | VideoDescriptionRequest\n | undefined;\n /** Optional global timeout signal (e.g. from agent-level cancellation). */\n signal?: AbortSignal;\n}\n\nconst KIND_BY_CAPABILITY: Record<MediaCapability, MediaUnderstandingKind> = {\n audio: 'audio.transcription',\n image: 'image.description',\n video: 'video.description',\n};\n\nfunction orderProviders(\n providers: MediaUnderstandingProvider[],\n capability: MediaCapability,\n override?: readonly string[],\n): MediaUnderstandingProvider[] {\n if (override && override.length > 0) {\n const lookup = new Map(providers.map((p) => [p.id.toLowerCase(), p]));\n const ordered: MediaUnderstandingProvider[] = [];\n const used = new Set<string>();\n for (const id of override) {\n const provider = lookup.get(id.toLowerCase());\n if (provider && !used.has(provider.id)) {\n ordered.push(provider);\n used.add(provider.id);\n }\n }\n // Append any registered providers not in the override (preserves capability\n // availability when the override list is incomplete).\n for (const provider of providers) {\n if (!used.has(provider.id)) {\n ordered.push(provider);\n }\n }\n return ordered;\n }\n return [...providers].sort((a, b) => {\n const aPriority = a.autoPriority?.[capability] ?? Number.MAX_SAFE_INTEGER;\n const bPriority = b.autoPriority?.[capability] ?? Number.MAX_SAFE_INTEGER;\n return aPriority - bPriority;\n });\n}\n\nfunction pickMethod(\n provider: MediaUnderstandingProvider,\n capability: MediaCapability,\n): ((req: never) => Promise<{ text: string; model?: string }>) | undefined {\n if (capability === 'audio') {\n return provider.transcribeAudio as (\n req: AudioTranscriptionRequest,\n ) => Promise<AudioTranscriptionResult>;\n }\n if (capability === 'image') {\n return provider.describeImage as (\n req: ImageDescriptionRequest,\n ) => Promise<ImageDescriptionResult>;\n }\n return provider.describeVideo as (\n req: VideoDescriptionRequest,\n ) => Promise<VideoDescriptionResult>;\n}\n\nexport async function runCapability(\n options: RunCapabilityOptions,\n): Promise<MediaUnderstandingResult> {\n const eligibleProviders = listProvidersForCapability(options.capability);\n\n // Capability outcome is 'disabled' when no registered provider implements it.\n if (eligibleProviders.length === 0) {\n log.debug(\n { capability: options.capability },\n `No providers registered for capability \"${options.capability}\"`,\n );\n return {\n decision: {\n capability: options.capability,\n outcome: 'disabled',\n attachments: options.attachments.map((a) => ({\n attachmentIndex: a.attachmentIndex,\n attempts: [],\n })),\n },\n outputs: [],\n };\n }\n\n if (options.attachments.length === 0) {\n return {\n decision: { capability: options.capability, outcome: 'no-attachment', attachments: [] },\n outputs: [],\n };\n }\n\n const providers = orderProviders(eligibleProviders, options.capability, options.providerOrder);\n const attachmentDecisions: MediaUnderstandingAttachmentDecision[] = [];\n const outputs: MediaUnderstandingOutput[] = [];\n\n for (const attachment of options.attachments) {\n options.signal?.throwIfAborted();\n\n const attempts: MediaUnderstandingModelDecision[] = [];\n let chosen: MediaUnderstandingModelDecision | undefined;\n\n for (const provider of providers) {\n const method = pickMethod(provider, options.capability);\n if (!method) {\n // Defensive: registry filter should already exclude these. Recorded as\n // skipped so callers can diagnose registry/method mismatches.\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'skipped',\n reason: `provider \"${provider.id}\" missing method for capability \"${options.capability}\"`,\n });\n continue;\n }\n const request = options.buildRequest({ provider, attachment });\n if (!request) {\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'skipped',\n reason: 'caller buildRequest returned undefined (likely missing config)',\n });\n continue;\n }\n const startedAt = Date.now();\n try {\n // We narrow `method` by capability above; cast through `unknown` to keep\n // a single call site without per-capability ceremony.\n const callResult = await (method as (req: unknown) => Promise<{ text: string; model?: string }>)(\n request,\n );\n const text = callResult.text?.trim() ?? '';\n if (!text) {\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'failed',\n reason: 'empty transcription/description text',\n model: callResult.model,\n latencyMs: Date.now() - startedAt,\n });\n continue;\n }\n chosen = {\n provider: provider.id,\n type: 'provider',\n outcome: 'success',\n model: callResult.model,\n latencyMs: Date.now() - startedAt,\n };\n attempts.push(chosen);\n outputs.push({\n kind: KIND_BY_CAPABILITY[options.capability],\n attachmentIndex: attachment.attachmentIndex,\n text,\n provider: provider.id,\n model: callResult.model,\n });\n break;\n } catch (error) {\n if (options.signal?.aborted) {\n throw error;\n }\n const reason = error instanceof Error ? error.message : String(error);\n log.warn(\n {\n err: error,\n providerId: provider.id,\n capability: options.capability,\n attachmentIndex: attachment.attachmentIndex,\n },\n `Provider \"${provider.id}\" failed for ${options.capability}: ${reason}`,\n );\n attempts.push({\n provider: provider.id,\n type: 'provider',\n outcome: 'failed',\n reason,\n latencyMs: Date.now() - startedAt,\n });\n }\n }\n\n attachmentDecisions.push({\n attachmentIndex: attachment.attachmentIndex,\n attempts,\n ...(chosen ? { chosen } : {}),\n });\n }\n\n const overallOutcome = attachmentDecisions.every((d) => d.chosen)\n ? 'success'\n : attachmentDecisions.some((d) => d.chosen)\n ? 'success' // partial success — at least one attachment succeeded\n : 'failed';\n\n return {\n decision: {\n capability: options.capability,\n outcome: overallOutcome,\n attachments: attachmentDecisions,\n },\n outputs,\n };\n}\n"],"mappings":"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"}
|
|
@@ -70,6 +70,8 @@ export interface MediaUnderstandingModelDecision {
|
|
|
70
70
|
type: 'provider' | 'cli';
|
|
71
71
|
outcome: MediaUnderstandingAttemptOutcome;
|
|
72
72
|
reason?: string;
|
|
73
|
+
/** Wall-clock time spent in this provider attempt. */
|
|
74
|
+
latencyMs?: number;
|
|
73
75
|
}
|
|
74
76
|
export interface MediaUnderstandingAttachmentDecision {
|
|
75
77
|
attachmentIndex: number;
|
|
@@ -98,6 +100,7 @@ export interface AudioTranscriptionRequest {
|
|
|
98
100
|
prompt?: string;
|
|
99
101
|
query?: Record<string, string | number | boolean>;
|
|
100
102
|
timeoutMs: number;
|
|
103
|
+
signal?: AbortSignal;
|
|
101
104
|
}
|
|
102
105
|
export interface AudioTranscriptionResult {
|
|
103
106
|
text: string;
|
|
@@ -156,6 +159,10 @@ export interface MediaUnderstandingProvider {
|
|
|
156
159
|
* Defaults to true.
|
|
157
160
|
*/
|
|
158
161
|
requiresApiKey?: boolean;
|
|
162
|
+
/** Optional runtime readiness check for bundled providers such as local STT. */
|
|
163
|
+
isConfigured?: (options?: {
|
|
164
|
+
model?: string;
|
|
165
|
+
}) => boolean;
|
|
159
166
|
/** Lower number = higher priority in auto-selection. */
|
|
160
167
|
autoPriority?: Partial<Record<MediaCapability, number>>;
|
|
161
168
|
/** True when the provider can natively ingest PDF (no OCR pre-step needed). */
|
|
@@ -13,7 +13,7 @@ export { deleteNoteRecord, getNoteRecord, listNoteRecords, upsertNoteRecord, } f
|
|
|
13
13
|
export { deleteNoteAgentContextRecord, getNoteAgentContextRecord, upsertNoteAgentContextRecord, type NoteAgentContextRecord, } from './note-agent-context-repository.js';
|
|
14
14
|
export { resolveAgentIdFromMemoriesDir, searchMemoryIndex, syncMemoryIndex, type MemorySearchHit, } from './memory-index-repository.js';
|
|
15
15
|
export { appendMemorySignal, appendMemoryTraceEvent, deleteMemoryRecord, findLatestMemoryInjectTrace, getMemoryProviderState, getMemoryRecord, listMemorySignals, listMemoryTraceEvents, listMemoryRecords, searchMemoryRecords, setMemoryProviderState, setMemoryTraceFeedback, setLatestMemoryInjectFeedback, summarizeMemoryRecallFeedback, summarizeUserUnderstandingQuality, USER_UNDERSTANDING_REMEDIATION_POLICY, upsertMemoryRecord, type AppendMemorySignalInput, type AppendMemoryTraceEventInput, type FindLatestMemoryInjectTraceInput, type ListMemoryRecordsOptions, type MemoryRecallFeedbackSummary, type MemoryFeedbackRemediationResult, type MemorySignalRowPayload, type MemoryTraceFeedback, type MemoryTraceEventPayload, type UserUnderstandingQualityMetrics, type SearchMemoryRecordsOptions, type SetMemoryTraceFeedbackInput, type SetLatestMemoryInjectFeedbackInput, type UpsertMemoryRecordInput, } from './memory-records-repository.js';
|
|
16
|
-
export { attachMemoryEvidence, finishKnowledgeSyncRun, getKnowledgeSourceCursor, getKnowledgeSourceItem, listKnowledgeSourceItems, listKnowledgeSyncRuns, listMemoryEvidence, setKnowledgeSourceItemSynthesisStatus, setKnowledgeSourceCursor, startKnowledgeSyncRun, upsertKnowledgeSourceItems, } from './knowledge-repository.js';
|
|
16
|
+
export { attachMemoryEvidence, claimKnowledgeSourceItems, completeKnowledgeSourceItemSynthesis, deleteMemoryEvidenceForRecord, finishKnowledgeSyncRun, getKnowledgeConsumerWatermark, getKnowledgeSourceCursor, getKnowledgeSourceItem, listKnowledgeSourceItems, listKnowledgeSourceChanges, listKnowledgeSyncRuns, listMemoryEvidence, setKnowledgeSourceItemSynthesisStatus, setKnowledgeConsumerWatermark, setKnowledgeSourceCursor, startKnowledgeSyncRun, upsertKnowledgeSourceItems, } from './knowledge-repository.js';
|
|
17
17
|
export { deleteSessionConfig, getSessionConfig, hasSessionConfig, setSessionConfig, updateSessionConfig, } from './config-repository.js';
|
|
18
18
|
export { createObjectLinkRecord, getActivityEventRecord, listActivityRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, recordActivityEvent, } from './activity-repository.js';
|
|
19
19
|
export { appendConnectorExecutionAudit, claimConnectorWebhookDelivery, completeConnectorWebhookDelivery, consumeConnectorApproval, createConnectorApproval, decideConnectorApproval, deleteConnectorConnection, deleteConnectorInstallation, getConnectorConnection, getConnectorApproval, getConnectorInstallation, getCachedConnectorCatalogEntry, listCachedConnectorCatalogEntries, listConnectorActionMetadata, listConnectorApprovals, listConnectorConnections, listConnectorExecutionAudit, listConnectorInstallations, releaseConnectorWebhookDelivery, upsertConnectorActionMetadata, upsertConnectorCatalogEntry, upsertConnectorConnection, upsertConnectorInstallation, type CachedConnectorCatalogEntry, } from './connector-repository.js';
|
|
@@ -14,10 +14,10 @@ import { deleteNoteRecord, getNoteRecord, listNoteRecords, upsertNoteRecord } fr
|
|
|
14
14
|
import { deleteNoteAgentContextRecord, getNoteAgentContextRecord, upsertNoteAgentContextRecord } from "./note-agent-context-repository.js";
|
|
15
15
|
import { resolveAgentIdFromMemoriesDir, searchMemoryIndex, syncMemoryIndex } from "./memory-index-repository.js";
|
|
16
16
|
import { USER_UNDERSTANDING_REMEDIATION_POLICY, appendMemorySignal, appendMemoryTraceEvent, deleteMemoryRecord, findLatestMemoryInjectTrace, getMemoryProviderState, getMemoryRecord, listMemoryRecords, listMemorySignals, listMemoryTraceEvents, searchMemoryRecords, setLatestMemoryInjectFeedback, setMemoryProviderState, setMemoryTraceFeedback, summarizeMemoryRecallFeedback, summarizeUserUnderstandingQuality, upsertMemoryRecord } from "./memory-records-repository.js";
|
|
17
|
-
import { attachMemoryEvidence, finishKnowledgeSyncRun, getKnowledgeSourceCursor, getKnowledgeSourceItem, listKnowledgeSourceItems, listKnowledgeSyncRuns, listMemoryEvidence, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, startKnowledgeSyncRun, upsertKnowledgeSourceItems } from "./knowledge-repository.js";
|
|
17
|
+
import { attachMemoryEvidence, claimKnowledgeSourceItems, completeKnowledgeSourceItemSynthesis, deleteMemoryEvidenceForRecord, finishKnowledgeSyncRun, getKnowledgeConsumerWatermark, getKnowledgeSourceCursor, getKnowledgeSourceItem, listKnowledgeSourceChanges, listKnowledgeSourceItems, listKnowledgeSyncRuns, listMemoryEvidence, setKnowledgeConsumerWatermark, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, startKnowledgeSyncRun, upsertKnowledgeSourceItems } from "./knowledge-repository.js";
|
|
18
18
|
import { deleteSessionConfig, getSessionConfig, hasSessionConfig, setSessionConfig, updateSessionConfig } from "./config-repository.js";
|
|
19
19
|
import { createObjectLinkRecord, getActivityEventRecord, listActivityRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, recordActivityEvent } from "./activity-repository.js";
|
|
20
20
|
import { appendConnectorExecutionAudit, claimConnectorWebhookDelivery, completeConnectorWebhookDelivery, consumeConnectorApproval, createConnectorApproval, decideConnectorApproval, deleteConnectorConnection, deleteConnectorInstallation, getCachedConnectorCatalogEntry, getConnectorApproval, getConnectorConnection, getConnectorInstallation, listCachedConnectorCatalogEntries, listConnectorActionMetadata, listConnectorApprovals, listConnectorConnections, listConnectorExecutionAudit, listConnectorInstallations, releaseConnectorWebhookDelivery, upsertConnectorActionMetadata, upsertConnectorCatalogEntry, upsertConnectorConnection, upsertConnectorInstallation } from "./connector-repository.js";
|
|
21
21
|
import { getUserTrustPolicy, setUserTrustPolicy } from "./user-trust-repository.js";
|
|
22
22
|
import { getUserProfilePromptState, setUserProfilePromptState } from "./user-profile-setup-repository.js";
|
|
23
|
-
export { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, SCHEMA_META_SCHEMA_VERSION_KEY, USER_UNDERSTANDING_REMEDIATION_POLICY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, appendConnectorExecutionAudit, appendMemorySignal, appendMemoryTraceEvent, appendTranscriptEntry, applyPendingMigrations, attachMemoryEvidence, buildDefaultSessionMetadata, buildGlobalSessionStats, captureCompactionCheckpoint, claimConnectorWebhookDelivery, classifyStoredRow, closeXopcDatabase, completeConnectorWebhookDelivery, consumeConnectorApproval, createConnectorApproval, createObjectLinkRecord, decideConnectorApproval, deleteConnectorConnection, deleteConnectorInstallation, deleteMemoryRecord, deleteNoteAgentContextRecord, deleteNoteRecord, deleteSessionConfig, deleteSessionRecord, discoverSqlMigrations, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, findLatestMemoryInjectTrace, findSessionKeyBySessionId, finishKnowledgeSyncRun, getActivityEventRecord, getCachedConnectorCatalogEntry, getCompactionCheckpointDetail, getConnectorApproval, getConnectorConnection, getConnectorInstallation, getCurrentSessionId, getGlobalSessionStats, getKnowledgeSourceCursor, getKnowledgeSourceItem, getMemoryProviderState, getMemoryRecord, getNoteAgentContextRecord, getNoteRecord, getSessionConfig, getSessionMetadata, getSessionPersistedLevels, getSqliteDatabase, getUserProfilePromptState, getUserTrustPolicy, getXopcDatabase, hasSessionConfig, incrementSessionStatsOnAppend, inspectSchemaMigrationStatus, isXopcDatabaseOpen, listActivityRecords, listCachedConnectorCatalogEntries, listCompactionCheckpoints, listConnectorActionMetadata, listConnectorApprovals, listConnectorConnections, listConnectorExecutionAudit, listConnectorInstallations, listKnowledgeSourceItems, listKnowledgeSyncRuns, listMemoryEvidence, listMemoryRecords, listMemorySignals, listMemoryTraceEvents, listNoteRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, listRegisteredMigrationTargets, listSessionMetadata, listSessionsByAgent, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, metadataToSessionInsert, openXopcDatabase, paginateTranscriptMessages, patchSessionMetadata, readSchemaVersion, readSchemaVersionForTest, recordActivityEvent, releaseConnectorWebhookDelivery, replaceTranscriptRows, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveAgentIdFromMemoriesDir, resolveMigrationsDir, resolveSessionAgentId, resolveXopcDatabasePath, restoreCompactionCheckpoint, runSqliteWriteTransaction, searchMemoryIndex, searchMemoryRecords, sessionConfigRowToConfig, sessionRowToMetadata, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, setLatestMemoryInjectFeedback, setMemoryProviderState, setMemoryTraceFeedback, setSessionConfig, setUserProfilePromptState, setUserTrustPolicy, startKnowledgeSyncRun, summarizeMemoryRecallFeedback, summarizeUserUnderstandingQuality, syncMemoryIndex, transcriptEntryRowToStoredRow, updateSessionConfig, updateSessionStats, upsertConnectorActionMetadata, upsertConnectorCatalogEntry, upsertConnectorConnection, upsertConnectorInstallation, upsertKnowledgeSourceItems, upsertMemoryRecord, upsertNoteAgentContextRecord, upsertNoteRecord };
|
|
23
|
+
export { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, SCHEMA_META_SCHEMA_VERSION_KEY, USER_UNDERSTANDING_REMEDIATION_POLICY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, appendConnectorExecutionAudit, appendMemorySignal, appendMemoryTraceEvent, appendTranscriptEntry, applyPendingMigrations, attachMemoryEvidence, buildDefaultSessionMetadata, buildGlobalSessionStats, captureCompactionCheckpoint, claimConnectorWebhookDelivery, claimKnowledgeSourceItems, classifyStoredRow, closeXopcDatabase, completeConnectorWebhookDelivery, completeKnowledgeSourceItemSynthesis, consumeConnectorApproval, createConnectorApproval, createObjectLinkRecord, decideConnectorApproval, deleteConnectorConnection, deleteConnectorInstallation, deleteMemoryEvidenceForRecord, deleteMemoryRecord, deleteNoteAgentContextRecord, deleteNoteRecord, deleteSessionConfig, deleteSessionRecord, discoverSqlMigrations, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, findLatestMemoryInjectTrace, findSessionKeyBySessionId, finishKnowledgeSyncRun, getActivityEventRecord, getCachedConnectorCatalogEntry, getCompactionCheckpointDetail, getConnectorApproval, getConnectorConnection, getConnectorInstallation, getCurrentSessionId, getGlobalSessionStats, getKnowledgeConsumerWatermark, getKnowledgeSourceCursor, getKnowledgeSourceItem, getMemoryProviderState, getMemoryRecord, getNoteAgentContextRecord, getNoteRecord, getSessionConfig, getSessionMetadata, getSessionPersistedLevels, getSqliteDatabase, getUserProfilePromptState, getUserTrustPolicy, getXopcDatabase, hasSessionConfig, incrementSessionStatsOnAppend, inspectSchemaMigrationStatus, isXopcDatabaseOpen, listActivityRecords, listCachedConnectorCatalogEntries, listCompactionCheckpoints, listConnectorActionMetadata, listConnectorApprovals, listConnectorConnections, listConnectorExecutionAudit, listConnectorInstallations, listKnowledgeSourceChanges, listKnowledgeSourceItems, listKnowledgeSyncRuns, listMemoryEvidence, listMemoryRecords, listMemorySignals, listMemoryTraceEvents, listNoteRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, listRegisteredMigrationTargets, listSessionMetadata, listSessionsByAgent, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, metadataToSessionInsert, openXopcDatabase, paginateTranscriptMessages, patchSessionMetadata, readSchemaVersion, readSchemaVersionForTest, recordActivityEvent, releaseConnectorWebhookDelivery, replaceTranscriptRows, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveAgentIdFromMemoriesDir, resolveMigrationsDir, resolveSessionAgentId, resolveXopcDatabasePath, restoreCompactionCheckpoint, runSqliteWriteTransaction, searchMemoryIndex, searchMemoryRecords, sessionConfigRowToConfig, sessionRowToMetadata, setKnowledgeConsumerWatermark, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, setLatestMemoryInjectFeedback, setMemoryProviderState, setMemoryTraceFeedback, setSessionConfig, setUserProfilePromptState, setUserTrustPolicy, startKnowledgeSyncRun, summarizeMemoryRecallFeedback, summarizeUserUnderstandingQuality, syncMemoryIndex, transcriptEntryRowToStoredRow, updateSessionConfig, updateSessionStats, upsertConnectorActionMetadata, upsertConnectorCatalogEntry, upsertConnectorConnection, upsertConnectorInstallation, upsertKnowledgeSourceItems, upsertMemoryRecord, upsertNoteAgentContextRecord, upsertNoteRecord };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KnowledgeSourceItem, KnowledgeSourceItemInput, KnowledgeSyncRun, KnowledgeSyncRunStatus, KnowledgeSynthesisStatus } from '../../knowledge/types.js';
|
|
1
|
+
import type { KnowledgeSourceChange, KnowledgeSourceItem, KnowledgeSourceItemInput, KnowledgeSyncRun, KnowledgeSyncRunStatus, KnowledgeSynthesisPipeline, KnowledgeSynthesisStatus } from '../../knowledge/types.js';
|
|
2
2
|
import type { MemoryEvidence, MemoryEvidenceRelation } from '../../agent/memory/types.js';
|
|
3
3
|
export declare function startKnowledgeSyncRun(input: {
|
|
4
4
|
sourceInstanceId: string;
|
|
@@ -30,6 +30,13 @@ export declare function upsertKnowledgeSourceItems(inputs: KnowledgeSourceItemIn
|
|
|
30
30
|
unchanged: number;
|
|
31
31
|
};
|
|
32
32
|
export declare function getKnowledgeSourceItem(itemId: string): KnowledgeSourceItem | null;
|
|
33
|
+
export declare function listKnowledgeSourceChanges(options?: {
|
|
34
|
+
sourceInstanceId?: string;
|
|
35
|
+
afterSequence?: number;
|
|
36
|
+
limit?: number;
|
|
37
|
+
}): KnowledgeSourceChange[];
|
|
38
|
+
export declare function getKnowledgeConsumerWatermark(consumerId: string, sourceInstanceId: string): number;
|
|
39
|
+
export declare function setKnowledgeConsumerWatermark(consumerId: string, sourceInstanceId: string, sequence: number, nowMs?: number): void;
|
|
33
40
|
export declare function listKnowledgeSourceItems(options?: {
|
|
34
41
|
sourceInstanceId?: string;
|
|
35
42
|
synthesisStatus?: KnowledgeSynthesisStatus;
|
|
@@ -38,6 +45,23 @@ export declare function listKnowledgeSourceItems(options?: {
|
|
|
38
45
|
offset?: number;
|
|
39
46
|
}): KnowledgeSourceItem[];
|
|
40
47
|
export declare function setKnowledgeSourceItemSynthesisStatus(itemIds: string[], status: KnowledgeSynthesisStatus, nowMs?: number): number;
|
|
48
|
+
export declare function claimKnowledgeSourceItems(options: {
|
|
49
|
+
workerId: string;
|
|
50
|
+
sourceInstanceId?: string;
|
|
51
|
+
synthesisPipeline?: KnowledgeSynthesisPipeline;
|
|
52
|
+
limit?: number;
|
|
53
|
+
leaseMs?: number;
|
|
54
|
+
retryDelayMs?: number;
|
|
55
|
+
maxAttempts?: number;
|
|
56
|
+
nowMs?: number;
|
|
57
|
+
}): KnowledgeSourceItem[];
|
|
58
|
+
export declare function completeKnowledgeSourceItemSynthesis(input: {
|
|
59
|
+
itemId: string;
|
|
60
|
+
workerId: string;
|
|
61
|
+
status: 'completed' | 'failed' | 'ignored';
|
|
62
|
+
error?: string;
|
|
63
|
+
nowMs?: number;
|
|
64
|
+
}): boolean;
|
|
41
65
|
export declare function attachMemoryEvidence(input: {
|
|
42
66
|
recordId: string;
|
|
43
67
|
sourceItemId?: string;
|
|
@@ -47,4 +71,5 @@ export declare function attachMemoryEvidence(input: {
|
|
|
47
71
|
observedAt?: string;
|
|
48
72
|
nowMs?: number;
|
|
49
73
|
}): MemoryEvidence;
|
|
74
|
+
export declare function deleteMemoryEvidenceForRecord(recordId: string, relation?: MemoryEvidenceRelation): number;
|
|
50
75
|
export declare function listMemoryEvidence(recordId: string): MemoryEvidence[];
|
|
@@ -40,12 +40,29 @@ function sourceItemFromRow(row) {
|
|
|
40
40
|
metadata: parseRecord(row.metadata_json),
|
|
41
41
|
sensitivity: row.sensitivity,
|
|
42
42
|
retentionClass: row.retention_class,
|
|
43
|
+
synthesisPipeline: row.synthesis_pipeline,
|
|
43
44
|
synthesisStatus: row.synthesis_status,
|
|
45
|
+
synthesisAttempts: row.synthesis_attempts,
|
|
46
|
+
...timestampToIso(row.synthesis_claimed_at) ? { synthesisClaimedAt: timestampToIso(row.synthesis_claimed_at) } : {},
|
|
47
|
+
...row.synthesis_claimed_by ? { synthesisClaimedBy: row.synthesis_claimed_by } : {},
|
|
48
|
+
...row.synthesis_error ? { synthesisError: row.synthesis_error } : {},
|
|
44
49
|
...timestampToIso(row.deleted_at) ? { deletedAt: timestampToIso(row.deleted_at) } : {},
|
|
45
50
|
createdAt: new Date(row.created_at).toISOString(),
|
|
46
51
|
updatedAt: new Date(row.updated_at).toISOString()
|
|
47
52
|
};
|
|
48
53
|
}
|
|
54
|
+
function sourceChangeFromRow(row) {
|
|
55
|
+
return {
|
|
56
|
+
sequence: row.sequence,
|
|
57
|
+
id: row.change_id,
|
|
58
|
+
sourceInstanceId: row.source_instance_id,
|
|
59
|
+
sourceItemId: row.source_item_id,
|
|
60
|
+
kind: row.change_kind,
|
|
61
|
+
...row.old_hash ? { oldHash: row.old_hash } : {},
|
|
62
|
+
...row.new_hash ? { newHash: row.new_hash } : {},
|
|
63
|
+
changedAt: new Date(row.changed_at).toISOString()
|
|
64
|
+
};
|
|
65
|
+
}
|
|
49
66
|
function syncRunFromRow(row) {
|
|
50
67
|
return {
|
|
51
68
|
id: row.run_id,
|
|
@@ -141,8 +158,8 @@ function upsertKnowledgeSourceItems(inputs, nowMs = Date.now()) {
|
|
|
141
158
|
item_id, source_instance_id, external_id, item_type, author_role,
|
|
142
159
|
occurred_at, source_updated_at, content_hash, normalized_text,
|
|
143
160
|
payload_ref, metadata_json, sensitivity, retention_class,
|
|
144
|
-
synthesis_status, deleted_at, created_at, updated_at
|
|
145
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
161
|
+
synthesis_pipeline, synthesis_status, deleted_at, created_at, updated_at
|
|
162
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
146
163
|
ON CONFLICT(source_instance_id, external_id) DO UPDATE SET
|
|
147
164
|
item_type = excluded.item_type,
|
|
148
165
|
author_role = excluded.author_role,
|
|
@@ -154,11 +171,21 @@ function upsertKnowledgeSourceItems(inputs, nowMs = Date.now()) {
|
|
|
154
171
|
metadata_json = excluded.metadata_json,
|
|
155
172
|
sensitivity = excluded.sensitivity,
|
|
156
173
|
retention_class = excluded.retention_class,
|
|
174
|
+
synthesis_pipeline = excluded.synthesis_pipeline,
|
|
157
175
|
synthesis_status = excluded.synthesis_status,
|
|
176
|
+
synthesis_attempts = 0,
|
|
177
|
+
synthesis_claimed_at = NULL,
|
|
178
|
+
synthesis_claimed_by = NULL,
|
|
179
|
+
synthesis_error = NULL,
|
|
158
180
|
deleted_at = excluded.deleted_at,
|
|
159
|
-
updated_at = excluded.updated_at`).run(id, input.sourceInstanceId, input.externalId, input.itemType, input.authorRole ?? null, parseTimestamp(input.occurredAt), parseTimestamp(input.sourceUpdatedAt), input.contentHash, input.normalizedText ?? null, input.payloadRef ?? null, JSON.stringify(input.metadata ?? {}), input.sensitivity ?? "normal", input.retentionClass ?? "bounded", input.synthesisStatus ?? "pending", parseTimestamp(input.deletedAt), existing?.created_at ?? nowMs, nowMs);
|
|
181
|
+
updated_at = excluded.updated_at`).run(id, input.sourceInstanceId, input.externalId, input.itemType, input.authorRole ?? null, parseTimestamp(input.occurredAt), parseTimestamp(input.sourceUpdatedAt), input.contentHash, input.normalizedText ?? null, input.payloadRef ?? null, JSON.stringify(input.metadata ?? {}), input.sensitivity ?? "normal", input.retentionClass ?? "bounded", input.synthesisPipeline ?? "user_understanding", input.synthesisStatus ?? "pending", parseTimestamp(input.deletedAt), existing?.created_at ?? nowMs, nowMs);
|
|
160
182
|
const row = db.prepare(`SELECT * FROM knowledge_source_items WHERE item_id = ?`).get(id);
|
|
161
183
|
items.push(sourceItemFromRow(row));
|
|
184
|
+
const changeKind = input.deletedAt ? "deleted" : existing ? "modified" : "added";
|
|
185
|
+
db.prepare(`INSERT INTO knowledge_source_changes (
|
|
186
|
+
change_id, source_instance_id, source_item_id, change_kind,
|
|
187
|
+
old_hash, new_hash, changed_at
|
|
188
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), input.sourceInstanceId, id, changeKind, existing?.content_hash ?? null, input.deletedAt ? null : input.contentHash, nowMs);
|
|
162
189
|
if (existing) updated += 1;
|
|
163
190
|
else created += 1;
|
|
164
191
|
}
|
|
@@ -174,6 +201,36 @@ function getKnowledgeSourceItem(itemId) {
|
|
|
174
201
|
const row = getSqliteDatabase().prepare(`SELECT * FROM knowledge_source_items WHERE item_id = ?`).get(itemId);
|
|
175
202
|
return row ? sourceItemFromRow(row) : null;
|
|
176
203
|
}
|
|
204
|
+
function listKnowledgeSourceChanges(options = {}) {
|
|
205
|
+
const where = [];
|
|
206
|
+
const params = [];
|
|
207
|
+
if (options.sourceInstanceId) {
|
|
208
|
+
where.push("source_instance_id = ?");
|
|
209
|
+
params.push(options.sourceInstanceId);
|
|
210
|
+
}
|
|
211
|
+
if (options.afterSequence != null) {
|
|
212
|
+
where.push("sequence > ?");
|
|
213
|
+
params.push(Math.max(0, options.afterSequence));
|
|
214
|
+
}
|
|
215
|
+
const limit = Math.max(1, Math.min(1e3, options.limit ?? 100));
|
|
216
|
+
return getSqliteDatabase().prepare(`SELECT * FROM knowledge_source_changes
|
|
217
|
+
${where.length > 0 ? `WHERE ${where.join(" AND ")}` : ""}
|
|
218
|
+
ORDER BY sequence ASC LIMIT ?`).all(...params, limit).map(sourceChangeFromRow);
|
|
219
|
+
}
|
|
220
|
+
function getKnowledgeConsumerWatermark(consumerId, sourceInstanceId) {
|
|
221
|
+
return getSqliteDatabase().prepare(`SELECT last_sequence FROM knowledge_consumer_watermarks
|
|
222
|
+
WHERE consumer_id = ? AND source_instance_id = ?`).get(consumerId, sourceInstanceId)?.last_sequence ?? 0;
|
|
223
|
+
}
|
|
224
|
+
function setKnowledgeConsumerWatermark(consumerId, sourceInstanceId, sequence, nowMs = Date.now()) {
|
|
225
|
+
runSqliteWriteTransaction((db) => {
|
|
226
|
+
db.prepare(`INSERT INTO knowledge_consumer_watermarks (
|
|
227
|
+
consumer_id, source_instance_id, last_sequence, updated_at
|
|
228
|
+
) VALUES (?, ?, ?, ?)
|
|
229
|
+
ON CONFLICT(consumer_id, source_instance_id) DO UPDATE SET
|
|
230
|
+
last_sequence = MAX(last_sequence, excluded.last_sequence),
|
|
231
|
+
updated_at = excluded.updated_at`).run(consumerId, sourceInstanceId, Math.max(0, sequence), nowMs);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
177
234
|
function listKnowledgeSourceItems(options = {}) {
|
|
178
235
|
const where = [];
|
|
179
236
|
const params = [];
|
|
@@ -202,15 +259,66 @@ function setKnowledgeSourceItemSynthesisStatus(itemIds, status, nowMs = Date.now
|
|
|
202
259
|
return Number(result.changes);
|
|
203
260
|
});
|
|
204
261
|
}
|
|
262
|
+
function claimKnowledgeSourceItems(options) {
|
|
263
|
+
const now = options.nowMs ?? Date.now();
|
|
264
|
+
const leaseCutoff = now - Math.max(1e3, options.leaseMs ?? 5 * 6e4);
|
|
265
|
+
const retryCutoff = now - Math.max(1e3, options.retryDelayMs ?? 3e4);
|
|
266
|
+
const limit = Math.max(1, Math.min(100, options.limit ?? 20));
|
|
267
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
|
|
268
|
+
return runSqliteWriteTransaction((db) => {
|
|
269
|
+
const rows = db.prepare(`SELECT * FROM knowledge_source_items
|
|
270
|
+
WHERE synthesis_attempts < ?
|
|
271
|
+
${options.sourceInstanceId ? "AND source_instance_id = ?" : ""}
|
|
272
|
+
${options.synthesisPipeline ? "AND synthesis_pipeline = ?" : ""}
|
|
273
|
+
AND (
|
|
274
|
+
synthesis_status = 'pending'
|
|
275
|
+
OR (synthesis_status = 'failed' AND updated_at < ?)
|
|
276
|
+
OR (synthesis_status = 'processing' AND synthesis_claimed_at < ?)
|
|
277
|
+
)
|
|
278
|
+
ORDER BY COALESCE(source_updated_at, occurred_at, updated_at) ASC
|
|
279
|
+
LIMIT ?`).all(maxAttempts, ...options.sourceInstanceId ? [options.sourceInstanceId] : [], ...options.synthesisPipeline ? [options.synthesisPipeline] : [], retryCutoff, leaseCutoff, limit);
|
|
280
|
+
if (rows.length === 0) return [];
|
|
281
|
+
const ids = rows.map((row) => row.item_id);
|
|
282
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
283
|
+
db.prepare(`UPDATE knowledge_source_items SET
|
|
284
|
+
synthesis_status = 'processing',
|
|
285
|
+
synthesis_attempts = synthesis_attempts + 1,
|
|
286
|
+
synthesis_claimed_at = ?,
|
|
287
|
+
synthesis_claimed_by = ?,
|
|
288
|
+
synthesis_error = NULL,
|
|
289
|
+
updated_at = ?
|
|
290
|
+
WHERE item_id IN (${placeholders})`).run(now, options.workerId, now, ...ids);
|
|
291
|
+
return db.prepare(`SELECT * FROM knowledge_source_items WHERE item_id IN (${placeholders})`).all(...ids).map(sourceItemFromRow);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function completeKnowledgeSourceItemSynthesis(input) {
|
|
295
|
+
const now = input.nowMs ?? Date.now();
|
|
296
|
+
return runSqliteWriteTransaction((db) => {
|
|
297
|
+
const result = db.prepare(`UPDATE knowledge_source_items SET
|
|
298
|
+
synthesis_status = ?,
|
|
299
|
+
synthesis_claimed_at = NULL,
|
|
300
|
+
synthesis_claimed_by = NULL,
|
|
301
|
+
synthesis_error = ?,
|
|
302
|
+
updated_at = ?
|
|
303
|
+
WHERE item_id = ? AND synthesis_status = 'processing' AND synthesis_claimed_by = ?`).run(input.status, input.error ?? null, now, input.itemId, input.workerId);
|
|
304
|
+
return Number(result.changes) > 0;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
205
307
|
function attachMemoryEvidence(input) {
|
|
206
|
-
|
|
308
|
+
let evidenceId = randomUUID();
|
|
207
309
|
const now = input.nowMs ?? Date.now();
|
|
208
310
|
const observedAt = parseTimestamp(input.observedAt);
|
|
209
311
|
runSqliteWriteTransaction((db) => {
|
|
210
312
|
db.prepare(`INSERT INTO memory_evidence (
|
|
211
313
|
evidence_id, record_id, source_item_id, relation, excerpt,
|
|
212
314
|
confidence, observed_at, created_at
|
|
213
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
315
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
316
|
+
ON CONFLICT DO UPDATE SET
|
|
317
|
+
excerpt = COALESCE(excluded.excerpt, memory_evidence.excerpt),
|
|
318
|
+
confidence = MAX(COALESCE(memory_evidence.confidence, 0), COALESCE(excluded.confidence, 0)),
|
|
319
|
+
observed_at = MAX(COALESCE(memory_evidence.observed_at, 0), COALESCE(excluded.observed_at, 0))`).run(evidenceId, input.recordId, input.sourceItemId ?? null, input.relation ?? "supports", input.excerpt ?? null, input.confidence ?? null, observedAt, now);
|
|
320
|
+
if (input.sourceItemId) evidenceId = db.prepare(`SELECT evidence_id FROM memory_evidence
|
|
321
|
+
WHERE record_id = ? AND source_item_id = ? AND relation = ?`).get(input.recordId, input.sourceItemId, input.relation ?? "supports")?.evidence_id ?? evidenceId;
|
|
214
322
|
});
|
|
215
323
|
return {
|
|
216
324
|
evidenceId,
|
|
@@ -221,6 +329,12 @@ function attachMemoryEvidence(input) {
|
|
|
221
329
|
...observedAt != null ? { observedAt: new Date(observedAt).toISOString() } : {}
|
|
222
330
|
};
|
|
223
331
|
}
|
|
332
|
+
function deleteMemoryEvidenceForRecord(recordId, relation) {
|
|
333
|
+
return runSqliteWriteTransaction((db) => {
|
|
334
|
+
const result = relation ? db.prepare(`DELETE FROM memory_evidence WHERE record_id = ? AND relation = ?`).run(recordId, relation) : db.prepare(`DELETE FROM memory_evidence WHERE record_id = ?`).run(recordId);
|
|
335
|
+
return Number(result.changes);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
224
338
|
function listMemoryEvidence(recordId) {
|
|
225
339
|
return getSqliteDatabase().prepare(`SELECT * FROM memory_evidence WHERE record_id = ? ORDER BY created_at ASC`).all(recordId).map((row) => ({
|
|
226
340
|
evidenceId: row.evidence_id,
|
|
@@ -232,6 +346,6 @@ function listMemoryEvidence(recordId) {
|
|
|
232
346
|
}));
|
|
233
347
|
}
|
|
234
348
|
//#endregion
|
|
235
|
-
export { attachMemoryEvidence, finishKnowledgeSyncRun, getKnowledgeSourceCursor, getKnowledgeSourceItem, listKnowledgeSourceItems, listKnowledgeSyncRuns, listMemoryEvidence, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, startKnowledgeSyncRun, upsertKnowledgeSourceItems };
|
|
349
|
+
export { attachMemoryEvidence, claimKnowledgeSourceItems, completeKnowledgeSourceItemSynthesis, deleteMemoryEvidenceForRecord, finishKnowledgeSyncRun, getKnowledgeConsumerWatermark, getKnowledgeSourceCursor, getKnowledgeSourceItem, listKnowledgeSourceChanges, listKnowledgeSourceItems, listKnowledgeSyncRuns, listMemoryEvidence, setKnowledgeConsumerWatermark, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, startKnowledgeSyncRun, upsertKnowledgeSourceItems };
|
|
236
350
|
|
|
237
351
|
//# sourceMappingURL=knowledge-repository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knowledge-repository.js","names":[],"sources":["../../../../src/storage/sqlite/knowledge-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type {\n KnowledgeSourceItem,\n KnowledgeSourceItemInput,\n KnowledgeSyncRun,\n KnowledgeSyncRunStatus,\n KnowledgeSynthesisStatus,\n} from '../../knowledge/types.js';\nimport type { MemoryEvidence, MemoryEvidenceRelation } from '../../agent/memory/types.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype KnowledgeSourceItemRow = {\n item_id: string;\n source_instance_id: string;\n external_id: string;\n item_type: string;\n author_role: string | null;\n occurred_at: number | null;\n source_updated_at: number | null;\n content_hash: string;\n normalized_text: string | null;\n payload_ref: string | null;\n metadata_json: string;\n sensitivity: string;\n retention_class: string;\n synthesis_status: string;\n deleted_at: number | null;\n created_at: number;\n updated_at: number;\n};\n\ntype KnowledgeSyncRunRow = {\n run_id: string;\n source_instance_id: string;\n status: string;\n cursor_before: string | null;\n cursor_after: string | null;\n items_seen: number;\n items_created: number;\n items_updated: number;\n warnings_json: string;\n error: string | null;\n started_at: number;\n finished_at: number | null;\n};\n\ntype MemoryEvidenceRow = {\n evidence_id: string;\n record_id: string;\n source_item_id: string | null;\n relation: string;\n excerpt: string | null;\n confidence: number | null;\n observed_at: number | null;\n created_at: number;\n};\n\nfunction parseTimestamp(value: string | undefined): number | null {\n if (!value) 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 parseRecord(json: string): Record<string, unknown> {\n try {\n const parsed = JSON.parse(json) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : {};\n } catch {\n return {};\n }\n}\n\nfunction parseStrings(json: string): string[] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch {\n return [];\n }\n}\n\nfunction sourceItemFromRow(row: KnowledgeSourceItemRow): KnowledgeSourceItem {\n return {\n id: row.item_id,\n sourceInstanceId: row.source_instance_id,\n externalId: row.external_id,\n itemType: row.item_type,\n ...(row.author_role ? { authorRole: row.author_role as KnowledgeSourceItem['authorRole'] } : {}),\n ...(timestampToIso(row.occurred_at) ? { occurredAt: timestampToIso(row.occurred_at) } : {}),\n ...(timestampToIso(row.source_updated_at) ? { sourceUpdatedAt: timestampToIso(row.source_updated_at) } : {}),\n contentHash: row.content_hash,\n ...(row.normalized_text ? { normalizedText: row.normalized_text } : {}),\n ...(row.payload_ref ? { payloadRef: row.payload_ref } : {}),\n metadata: parseRecord(row.metadata_json),\n sensitivity: row.sensitivity as KnowledgeSourceItem['sensitivity'],\n retentionClass: row.retention_class as KnowledgeSourceItem['retentionClass'],\n synthesisStatus: row.synthesis_status as KnowledgeSourceItem['synthesisStatus'],\n ...(timestampToIso(row.deleted_at) ? { deletedAt: timestampToIso(row.deleted_at) } : {}),\n createdAt: new Date(row.created_at).toISOString(),\n updatedAt: new Date(row.updated_at).toISOString(),\n };\n}\n\nfunction syncRunFromRow(row: KnowledgeSyncRunRow): KnowledgeSyncRun {\n return {\n id: row.run_id,\n sourceInstanceId: row.source_instance_id,\n status: row.status as KnowledgeSyncRunStatus,\n ...(row.cursor_before ? { cursorBefore: row.cursor_before } : {}),\n ...(row.cursor_after ? { cursorAfter: row.cursor_after } : {}),\n itemsSeen: row.items_seen,\n itemsCreated: row.items_created,\n itemsUpdated: row.items_updated,\n warnings: parseStrings(row.warnings_json),\n ...(row.error ? { error: row.error } : {}),\n startedAt: new Date(row.started_at).toISOString(),\n ...(timestampToIso(row.finished_at) ? { finishedAt: timestampToIso(row.finished_at) } : {}),\n };\n}\n\nexport function startKnowledgeSyncRun(input: {\n sourceInstanceId: string;\n cursorBefore?: string;\n nowMs?: number;\n}): KnowledgeSyncRun {\n const id = randomUUID();\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO knowledge_sync_runs (\n run_id, source_instance_id, status, cursor_before, started_at\n ) VALUES (?, ?, 'running', ?, ?)`,\n ).run(id, input.sourceInstanceId, input.cursorBefore ?? null, now);\n });\n return {\n id,\n sourceInstanceId: input.sourceInstanceId,\n status: 'running',\n ...(input.cursorBefore ? { cursorBefore: input.cursorBefore } : {}),\n itemsSeen: 0,\n itemsCreated: 0,\n itemsUpdated: 0,\n warnings: [],\n startedAt: new Date(now).toISOString(),\n };\n}\n\nexport function finishKnowledgeSyncRun(input: {\n runId: string;\n status: Exclude<KnowledgeSyncRunStatus, 'running'>;\n cursorAfter?: string;\n itemsSeen?: number;\n itemsCreated?: number;\n itemsUpdated?: number;\n warnings?: string[];\n error?: string;\n nowMs?: number;\n}): KnowledgeSyncRun {\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE knowledge_sync_runs SET\n status = ?, cursor_after = ?, items_seen = ?, items_created = ?,\n items_updated = ?, warnings_json = ?, error = ?, finished_at = ?\n WHERE run_id = ?`,\n ).run(\n input.status,\n input.cursorAfter ?? null,\n input.itemsSeen ?? 0,\n input.itemsCreated ?? 0,\n input.itemsUpdated ?? 0,\n JSON.stringify(input.warnings ?? []),\n input.error ?? null,\n now,\n input.runId,\n );\n });\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM knowledge_sync_runs WHERE run_id = ?`)\n .get(input.runId) as KnowledgeSyncRunRow | undefined;\n if (!row) throw new Error(`Knowledge sync run not found: ${input.runId}`);\n return syncRunFromRow(row);\n}\n\nexport function listKnowledgeSyncRuns(options: {\n sourceInstanceId?: string;\n status?: KnowledgeSyncRunStatus;\n limit?: number;\n} = {}): KnowledgeSyncRun[] {\n const where: string[] = [];\n const params: Array<string | number> = [];\n if (options.sourceInstanceId) {\n where.push('source_instance_id = ?');\n params.push(options.sourceInstanceId);\n }\n if (options.status) {\n where.push('status = ?');\n params.push(options.status);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM knowledge_sync_runs\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY started_at DESC LIMIT ?`,\n )\n .all(...params, limit) as KnowledgeSyncRunRow[];\n return rows.map(syncRunFromRow);\n}\n\nexport function getKnowledgeSourceCursor(sourceInstanceId: string): string | undefined {\n const row = getSqliteDatabase()\n .prepare(`SELECT cursor FROM knowledge_source_state WHERE source_instance_id = ?`)\n .get(sourceInstanceId) as { cursor: string | null } | undefined;\n return row?.cursor ?? undefined;\n}\n\nexport function setKnowledgeSourceCursor(sourceInstanceId: string, cursor: string | undefined): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO knowledge_source_state (source_instance_id, cursor, updated_at)\n VALUES (?, ?, ?)\n ON CONFLICT(source_instance_id) DO UPDATE SET\n cursor = excluded.cursor,\n updated_at = excluded.updated_at`,\n ).run(sourceInstanceId, cursor ?? null, Date.now());\n });\n}\n\nexport function upsertKnowledgeSourceItems(\n inputs: KnowledgeSourceItemInput[],\n nowMs = Date.now(),\n): { items: KnowledgeSourceItem[]; created: number; updated: number; unchanged: number } {\n return runSqliteWriteTransaction((db) => {\n const items: KnowledgeSourceItem[] = [];\n let created = 0;\n let updated = 0;\n let unchanged = 0;\n for (const input of inputs) {\n const existing = db.prepare(\n `SELECT * FROM knowledge_source_items\n WHERE source_instance_id = ? AND external_id = ?`,\n ).get(input.sourceInstanceId, input.externalId) as KnowledgeSourceItemRow | undefined;\n const id = existing?.item_id ?? input.id ?? randomUUID();\n if (existing?.content_hash === input.contentHash && existing.deleted_at === parseTimestamp(input.deletedAt)) {\n unchanged += 1;\n items.push(sourceItemFromRow(existing));\n continue;\n }\n db.prepare(\n `INSERT INTO knowledge_source_items (\n item_id, source_instance_id, external_id, item_type, author_role,\n occurred_at, source_updated_at, content_hash, normalized_text,\n payload_ref, metadata_json, sensitivity, retention_class,\n synthesis_status, deleted_at, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(source_instance_id, external_id) DO UPDATE SET\n item_type = excluded.item_type,\n author_role = excluded.author_role,\n occurred_at = excluded.occurred_at,\n source_updated_at = excluded.source_updated_at,\n content_hash = excluded.content_hash,\n normalized_text = excluded.normalized_text,\n payload_ref = excluded.payload_ref,\n metadata_json = excluded.metadata_json,\n sensitivity = excluded.sensitivity,\n retention_class = excluded.retention_class,\n synthesis_status = excluded.synthesis_status,\n deleted_at = excluded.deleted_at,\n updated_at = excluded.updated_at`,\n ).run(\n id,\n input.sourceInstanceId,\n input.externalId,\n input.itemType,\n input.authorRole ?? null,\n parseTimestamp(input.occurredAt),\n parseTimestamp(input.sourceUpdatedAt),\n input.contentHash,\n input.normalizedText ?? null,\n input.payloadRef ?? null,\n JSON.stringify(input.metadata ?? {}),\n input.sensitivity ?? 'normal',\n input.retentionClass ?? 'bounded',\n input.synthesisStatus ?? 'pending',\n parseTimestamp(input.deletedAt),\n existing?.created_at ?? nowMs,\n nowMs,\n );\n const row = db.prepare(`SELECT * FROM knowledge_source_items WHERE item_id = ?`).get(id) as KnowledgeSourceItemRow;\n items.push(sourceItemFromRow(row));\n if (existing) updated += 1;\n else created += 1;\n }\n return { items, created, updated, unchanged };\n });\n}\n\nexport function getKnowledgeSourceItem(itemId: string): KnowledgeSourceItem | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM knowledge_source_items WHERE item_id = ?`)\n .get(itemId) as KnowledgeSourceItemRow | undefined;\n return row ? sourceItemFromRow(row) : null;\n}\n\nexport function listKnowledgeSourceItems(options: {\n sourceInstanceId?: string;\n synthesisStatus?: KnowledgeSynthesisStatus;\n includeDeleted?: boolean;\n limit?: number;\n offset?: number;\n} = {}): KnowledgeSourceItem[] {\n const where: string[] = [];\n const params: Array<string | number> = [];\n if (options.sourceInstanceId) {\n where.push('source_instance_id = ?');\n params.push(options.sourceInstanceId);\n }\n if (options.synthesisStatus) {\n where.push('synthesis_status = ?');\n params.push(options.synthesisStatus);\n }\n if (!options.includeDeleted) where.push('deleted_at IS NULL');\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 knowledge_source_items\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY COALESCE(source_updated_at, occurred_at, updated_at) DESC\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as KnowledgeSourceItemRow[];\n return rows.map(sourceItemFromRow);\n}\n\nexport function setKnowledgeSourceItemSynthesisStatus(\n itemIds: string[],\n status: KnowledgeSynthesisStatus,\n nowMs = Date.now(),\n): number {\n if (itemIds.length === 0) return 0;\n return runSqliteWriteTransaction((db) => {\n const placeholders = itemIds.map(() => '?').join(', ');\n const result = db.prepare(\n `UPDATE knowledge_source_items SET synthesis_status = ?, updated_at = ?\n WHERE item_id IN (${placeholders})`,\n ).run(status, nowMs, ...itemIds);\n return Number(result.changes);\n });\n}\n\nexport function attachMemoryEvidence(input: {\n recordId: string;\n sourceItemId?: string;\n relation?: MemoryEvidenceRelation;\n excerpt?: string;\n confidence?: number;\n observedAt?: string;\n nowMs?: number;\n}): MemoryEvidence {\n const evidenceId = randomUUID();\n const now = input.nowMs ?? Date.now();\n const observedAt = parseTimestamp(input.observedAt);\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_evidence (\n evidence_id, record_id, source_item_id, relation, excerpt,\n confidence, observed_at, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n evidenceId,\n input.recordId,\n input.sourceItemId ?? null,\n input.relation ?? 'supports',\n input.excerpt ?? null,\n input.confidence ?? null,\n observedAt,\n now,\n );\n });\n return {\n evidenceId,\n ...(input.sourceItemId ? { sourceItemId: input.sourceItemId } : {}),\n relation: input.relation ?? 'supports',\n ...(input.excerpt ? { sourceText: input.excerpt } : {}),\n ...(input.confidence != null ? { confidence: input.confidence } : {}),\n ...(observedAt != null ? { observedAt: new Date(observedAt).toISOString() } : {}),\n };\n}\n\nexport function listMemoryEvidence(recordId: string): MemoryEvidence[] {\n const rows = getSqliteDatabase()\n .prepare(`SELECT * FROM memory_evidence WHERE record_id = ? ORDER BY created_at ASC`)\n .all(recordId) as MemoryEvidenceRow[];\n return rows.map((row) => ({\n evidenceId: row.evidence_id,\n ...(row.source_item_id ? { sourceItemId: row.source_item_id } : {}),\n relation: row.relation as MemoryEvidenceRelation,\n ...(row.excerpt ? { sourceText: row.excerpt } : {}),\n ...(row.confidence != null ? { confidence: row.confidence } : {}),\n ...(timestampToIso(row.observed_at) ? { observedAt: timestampToIso(row.observed_at) } : {}),\n }));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"knowledge-repository.js","names":[],"sources":["../../../../src/storage/sqlite/knowledge-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type {\n KnowledgeSourceChange,\n KnowledgeSourceItem,\n KnowledgeSourceItemInput,\n KnowledgeSyncRun,\n KnowledgeSyncRunStatus,\n KnowledgeSynthesisPipeline,\n KnowledgeSynthesisStatus,\n} from '../../knowledge/types.js';\nimport type { MemoryEvidence, MemoryEvidenceRelation } from '../../agent/memory/types.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype KnowledgeSourceItemRow = {\n item_id: string;\n source_instance_id: string;\n external_id: string;\n item_type: string;\n author_role: string | null;\n occurred_at: number | null;\n source_updated_at: number | null;\n content_hash: string;\n normalized_text: string | null;\n payload_ref: string | null;\n metadata_json: string;\n sensitivity: string;\n retention_class: string;\n synthesis_pipeline: string;\n synthesis_status: string;\n synthesis_attempts: number;\n synthesis_claimed_at: number | null;\n synthesis_claimed_by: string | null;\n synthesis_error: string | null;\n deleted_at: number | null;\n created_at: number;\n updated_at: number;\n};\n\ntype KnowledgeSourceChangeRow = {\n sequence: number;\n change_id: string;\n source_instance_id: string;\n source_item_id: string;\n change_kind: string;\n old_hash: string | null;\n new_hash: string | null;\n changed_at: number;\n};\n\ntype KnowledgeSyncRunRow = {\n run_id: string;\n source_instance_id: string;\n status: string;\n cursor_before: string | null;\n cursor_after: string | null;\n items_seen: number;\n items_created: number;\n items_updated: number;\n warnings_json: string;\n error: string | null;\n started_at: number;\n finished_at: number | null;\n};\n\ntype MemoryEvidenceRow = {\n evidence_id: string;\n record_id: string;\n source_item_id: string | null;\n relation: string;\n excerpt: string | null;\n confidence: number | null;\n observed_at: number | null;\n created_at: number;\n};\n\nfunction parseTimestamp(value: string | undefined): number | null {\n if (!value) 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 parseRecord(json: string): Record<string, unknown> {\n try {\n const parsed = JSON.parse(json) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : {};\n } catch {\n return {};\n }\n}\n\nfunction parseStrings(json: string): string[] {\n try {\n const parsed = JSON.parse(json) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch {\n return [];\n }\n}\n\nfunction sourceItemFromRow(row: KnowledgeSourceItemRow): KnowledgeSourceItem {\n return {\n id: row.item_id,\n sourceInstanceId: row.source_instance_id,\n externalId: row.external_id,\n itemType: row.item_type,\n ...(row.author_role ? { authorRole: row.author_role as KnowledgeSourceItem['authorRole'] } : {}),\n ...(timestampToIso(row.occurred_at) ? { occurredAt: timestampToIso(row.occurred_at) } : {}),\n ...(timestampToIso(row.source_updated_at) ? { sourceUpdatedAt: timestampToIso(row.source_updated_at) } : {}),\n contentHash: row.content_hash,\n ...(row.normalized_text ? { normalizedText: row.normalized_text } : {}),\n ...(row.payload_ref ? { payloadRef: row.payload_ref } : {}),\n metadata: parseRecord(row.metadata_json),\n sensitivity: row.sensitivity as KnowledgeSourceItem['sensitivity'],\n retentionClass: row.retention_class as KnowledgeSourceItem['retentionClass'],\n synthesisPipeline: row.synthesis_pipeline as KnowledgeSourceItem['synthesisPipeline'],\n synthesisStatus: row.synthesis_status as KnowledgeSourceItem['synthesisStatus'],\n synthesisAttempts: row.synthesis_attempts,\n ...(timestampToIso(row.synthesis_claimed_at) ? { synthesisClaimedAt: timestampToIso(row.synthesis_claimed_at) } : {}),\n ...(row.synthesis_claimed_by ? { synthesisClaimedBy: row.synthesis_claimed_by } : {}),\n ...(row.synthesis_error ? { synthesisError: row.synthesis_error } : {}),\n ...(timestampToIso(row.deleted_at) ? { deletedAt: timestampToIso(row.deleted_at) } : {}),\n createdAt: new Date(row.created_at).toISOString(),\n updatedAt: new Date(row.updated_at).toISOString(),\n };\n}\n\nfunction sourceChangeFromRow(row: KnowledgeSourceChangeRow): KnowledgeSourceChange {\n return {\n sequence: row.sequence,\n id: row.change_id,\n sourceInstanceId: row.source_instance_id,\n sourceItemId: row.source_item_id,\n kind: row.change_kind as KnowledgeSourceChange['kind'],\n ...(row.old_hash ? { oldHash: row.old_hash } : {}),\n ...(row.new_hash ? { newHash: row.new_hash } : {}),\n changedAt: new Date(row.changed_at).toISOString(),\n };\n}\n\nfunction syncRunFromRow(row: KnowledgeSyncRunRow): KnowledgeSyncRun {\n return {\n id: row.run_id,\n sourceInstanceId: row.source_instance_id,\n status: row.status as KnowledgeSyncRunStatus,\n ...(row.cursor_before ? { cursorBefore: row.cursor_before } : {}),\n ...(row.cursor_after ? { cursorAfter: row.cursor_after } : {}),\n itemsSeen: row.items_seen,\n itemsCreated: row.items_created,\n itemsUpdated: row.items_updated,\n warnings: parseStrings(row.warnings_json),\n ...(row.error ? { error: row.error } : {}),\n startedAt: new Date(row.started_at).toISOString(),\n ...(timestampToIso(row.finished_at) ? { finishedAt: timestampToIso(row.finished_at) } : {}),\n };\n}\n\nexport function startKnowledgeSyncRun(input: {\n sourceInstanceId: string;\n cursorBefore?: string;\n nowMs?: number;\n}): KnowledgeSyncRun {\n const id = randomUUID();\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO knowledge_sync_runs (\n run_id, source_instance_id, status, cursor_before, started_at\n ) VALUES (?, ?, 'running', ?, ?)`,\n ).run(id, input.sourceInstanceId, input.cursorBefore ?? null, now);\n });\n return {\n id,\n sourceInstanceId: input.sourceInstanceId,\n status: 'running',\n ...(input.cursorBefore ? { cursorBefore: input.cursorBefore } : {}),\n itemsSeen: 0,\n itemsCreated: 0,\n itemsUpdated: 0,\n warnings: [],\n startedAt: new Date(now).toISOString(),\n };\n}\n\nexport function finishKnowledgeSyncRun(input: {\n runId: string;\n status: Exclude<KnowledgeSyncRunStatus, 'running'>;\n cursorAfter?: string;\n itemsSeen?: number;\n itemsCreated?: number;\n itemsUpdated?: number;\n warnings?: string[];\n error?: string;\n nowMs?: number;\n}): KnowledgeSyncRun {\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE knowledge_sync_runs SET\n status = ?, cursor_after = ?, items_seen = ?, items_created = ?,\n items_updated = ?, warnings_json = ?, error = ?, finished_at = ?\n WHERE run_id = ?`,\n ).run(\n input.status,\n input.cursorAfter ?? null,\n input.itemsSeen ?? 0,\n input.itemsCreated ?? 0,\n input.itemsUpdated ?? 0,\n JSON.stringify(input.warnings ?? []),\n input.error ?? null,\n now,\n input.runId,\n );\n });\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM knowledge_sync_runs WHERE run_id = ?`)\n .get(input.runId) as KnowledgeSyncRunRow | undefined;\n if (!row) throw new Error(`Knowledge sync run not found: ${input.runId}`);\n return syncRunFromRow(row);\n}\n\nexport function listKnowledgeSyncRuns(options: {\n sourceInstanceId?: string;\n status?: KnowledgeSyncRunStatus;\n limit?: number;\n} = {}): KnowledgeSyncRun[] {\n const where: string[] = [];\n const params: Array<string | number> = [];\n if (options.sourceInstanceId) {\n where.push('source_instance_id = ?');\n params.push(options.sourceInstanceId);\n }\n if (options.status) {\n where.push('status = ?');\n params.push(options.status);\n }\n const limit = Math.max(1, Math.min(500, options.limit ?? 100));\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT * FROM knowledge_sync_runs\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY started_at DESC LIMIT ?`,\n )\n .all(...params, limit) as KnowledgeSyncRunRow[];\n return rows.map(syncRunFromRow);\n}\n\nexport function getKnowledgeSourceCursor(sourceInstanceId: string): string | undefined {\n const row = getSqliteDatabase()\n .prepare(`SELECT cursor FROM knowledge_source_state WHERE source_instance_id = ?`)\n .get(sourceInstanceId) as { cursor: string | null } | undefined;\n return row?.cursor ?? undefined;\n}\n\nexport function setKnowledgeSourceCursor(sourceInstanceId: string, cursor: string | undefined): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO knowledge_source_state (source_instance_id, cursor, updated_at)\n VALUES (?, ?, ?)\n ON CONFLICT(source_instance_id) DO UPDATE SET\n cursor = excluded.cursor,\n updated_at = excluded.updated_at`,\n ).run(sourceInstanceId, cursor ?? null, Date.now());\n });\n}\n\nexport function upsertKnowledgeSourceItems(\n inputs: KnowledgeSourceItemInput[],\n nowMs = Date.now(),\n): { items: KnowledgeSourceItem[]; created: number; updated: number; unchanged: number } {\n return runSqliteWriteTransaction((db) => {\n const items: KnowledgeSourceItem[] = [];\n let created = 0;\n let updated = 0;\n let unchanged = 0;\n for (const input of inputs) {\n const existing = db.prepare(\n `SELECT * FROM knowledge_source_items\n WHERE source_instance_id = ? AND external_id = ?`,\n ).get(input.sourceInstanceId, input.externalId) as KnowledgeSourceItemRow | undefined;\n const id = existing?.item_id ?? input.id ?? randomUUID();\n if (existing?.content_hash === input.contentHash && existing.deleted_at === parseTimestamp(input.deletedAt)) {\n unchanged += 1;\n items.push(sourceItemFromRow(existing));\n continue;\n }\n db.prepare(\n `INSERT INTO knowledge_source_items (\n item_id, source_instance_id, external_id, item_type, author_role,\n occurred_at, source_updated_at, content_hash, normalized_text,\n payload_ref, metadata_json, sensitivity, retention_class,\n synthesis_pipeline, synthesis_status, deleted_at, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(source_instance_id, external_id) DO UPDATE SET\n item_type = excluded.item_type,\n author_role = excluded.author_role,\n occurred_at = excluded.occurred_at,\n source_updated_at = excluded.source_updated_at,\n content_hash = excluded.content_hash,\n normalized_text = excluded.normalized_text,\n payload_ref = excluded.payload_ref,\n metadata_json = excluded.metadata_json,\n sensitivity = excluded.sensitivity,\n retention_class = excluded.retention_class,\n synthesis_pipeline = excluded.synthesis_pipeline,\n synthesis_status = excluded.synthesis_status,\n synthesis_attempts = 0,\n synthesis_claimed_at = NULL,\n synthesis_claimed_by = NULL,\n synthesis_error = NULL,\n deleted_at = excluded.deleted_at,\n updated_at = excluded.updated_at`,\n ).run(\n id,\n input.sourceInstanceId,\n input.externalId,\n input.itemType,\n input.authorRole ?? null,\n parseTimestamp(input.occurredAt),\n parseTimestamp(input.sourceUpdatedAt),\n input.contentHash,\n input.normalizedText ?? null,\n input.payloadRef ?? null,\n JSON.stringify(input.metadata ?? {}),\n input.sensitivity ?? 'normal',\n input.retentionClass ?? 'bounded',\n input.synthesisPipeline ?? 'user_understanding',\n input.synthesisStatus ?? 'pending',\n parseTimestamp(input.deletedAt),\n existing?.created_at ?? nowMs,\n nowMs,\n );\n const row = db.prepare(`SELECT * FROM knowledge_source_items WHERE item_id = ?`).get(id) as KnowledgeSourceItemRow;\n items.push(sourceItemFromRow(row));\n const changeKind = input.deletedAt ? 'deleted' : existing ? 'modified' : 'added';\n db.prepare(\n `INSERT INTO knowledge_source_changes (\n change_id, source_instance_id, source_item_id, change_kind,\n old_hash, new_hash, changed_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n randomUUID(),\n input.sourceInstanceId,\n id,\n changeKind,\n existing?.content_hash ?? null,\n input.deletedAt ? null : input.contentHash,\n nowMs,\n );\n if (existing) updated += 1;\n else created += 1;\n }\n return { items, created, updated, unchanged };\n });\n}\n\nexport function getKnowledgeSourceItem(itemId: string): KnowledgeSourceItem | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM knowledge_source_items WHERE item_id = ?`)\n .get(itemId) as KnowledgeSourceItemRow | undefined;\n return row ? sourceItemFromRow(row) : null;\n}\n\nexport function listKnowledgeSourceChanges(options: {\n sourceInstanceId?: string;\n afterSequence?: number;\n limit?: number;\n} = {}): KnowledgeSourceChange[] {\n const where: string[] = [];\n const params: Array<string | number> = [];\n if (options.sourceInstanceId) {\n where.push('source_instance_id = ?');\n params.push(options.sourceInstanceId);\n }\n if (options.afterSequence != null) {\n where.push('sequence > ?');\n params.push(Math.max(0, options.afterSequence));\n }\n const limit = Math.max(1, Math.min(1_000, options.limit ?? 100));\n const rows = getSqliteDatabase().prepare(\n `SELECT * FROM knowledge_source_changes\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY sequence ASC LIMIT ?`,\n ).all(...params, limit) as KnowledgeSourceChangeRow[];\n return rows.map(sourceChangeFromRow);\n}\n\nexport function getKnowledgeConsumerWatermark(consumerId: string, sourceInstanceId: string): number {\n const row = getSqliteDatabase().prepare(\n `SELECT last_sequence FROM knowledge_consumer_watermarks\n WHERE consumer_id = ? AND source_instance_id = ?`,\n ).get(consumerId, sourceInstanceId) as { last_sequence: number } | undefined;\n return row?.last_sequence ?? 0;\n}\n\nexport function setKnowledgeConsumerWatermark(\n consumerId: string,\n sourceInstanceId: string,\n sequence: number,\n nowMs = Date.now(),\n): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO knowledge_consumer_watermarks (\n consumer_id, source_instance_id, last_sequence, updated_at\n ) VALUES (?, ?, ?, ?)\n ON CONFLICT(consumer_id, source_instance_id) DO UPDATE SET\n last_sequence = MAX(last_sequence, excluded.last_sequence),\n updated_at = excluded.updated_at`,\n ).run(consumerId, sourceInstanceId, Math.max(0, sequence), nowMs);\n });\n}\n\nexport function listKnowledgeSourceItems(options: {\n sourceInstanceId?: string;\n synthesisStatus?: KnowledgeSynthesisStatus;\n includeDeleted?: boolean;\n limit?: number;\n offset?: number;\n} = {}): KnowledgeSourceItem[] {\n const where: string[] = [];\n const params: Array<string | number> = [];\n if (options.sourceInstanceId) {\n where.push('source_instance_id = ?');\n params.push(options.sourceInstanceId);\n }\n if (options.synthesisStatus) {\n where.push('synthesis_status = ?');\n params.push(options.synthesisStatus);\n }\n if (!options.includeDeleted) where.push('deleted_at IS NULL');\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 knowledge_source_items\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY COALESCE(source_updated_at, occurred_at, updated_at) DESC\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as KnowledgeSourceItemRow[];\n return rows.map(sourceItemFromRow);\n}\n\nexport function setKnowledgeSourceItemSynthesisStatus(\n itemIds: string[],\n status: KnowledgeSynthesisStatus,\n nowMs = Date.now(),\n): number {\n if (itemIds.length === 0) return 0;\n return runSqliteWriteTransaction((db) => {\n const placeholders = itemIds.map(() => '?').join(', ');\n const result = db.prepare(\n `UPDATE knowledge_source_items SET synthesis_status = ?, updated_at = ?\n WHERE item_id IN (${placeholders})`,\n ).run(status, nowMs, ...itemIds);\n return Number(result.changes);\n });\n}\n\nexport function claimKnowledgeSourceItems(options: {\n workerId: string;\n sourceInstanceId?: string;\n synthesisPipeline?: KnowledgeSynthesisPipeline;\n limit?: number;\n leaseMs?: number;\n retryDelayMs?: number;\n maxAttempts?: number;\n nowMs?: number;\n}): KnowledgeSourceItem[] {\n const now = options.nowMs ?? Date.now();\n const leaseCutoff = now - Math.max(1_000, options.leaseMs ?? 5 * 60_000);\n const retryCutoff = now - Math.max(1_000, options.retryDelayMs ?? 30_000);\n const limit = Math.max(1, Math.min(100, options.limit ?? 20));\n const maxAttempts = Math.max(1, options.maxAttempts ?? 3);\n return runSqliteWriteTransaction((db) => {\n const rows = db.prepare(\n `SELECT * FROM knowledge_source_items\n WHERE synthesis_attempts < ?\n ${options.sourceInstanceId ? 'AND source_instance_id = ?' : ''}\n ${options.synthesisPipeline ? 'AND synthesis_pipeline = ?' : ''}\n AND (\n synthesis_status = 'pending'\n OR (synthesis_status = 'failed' AND updated_at < ?)\n OR (synthesis_status = 'processing' AND synthesis_claimed_at < ?)\n )\n ORDER BY COALESCE(source_updated_at, occurred_at, updated_at) ASC\n LIMIT ?`,\n ).all(\n maxAttempts,\n ...(options.sourceInstanceId ? [options.sourceInstanceId] : []),\n ...(options.synthesisPipeline ? [options.synthesisPipeline] : []),\n retryCutoff,\n leaseCutoff,\n limit,\n ) as KnowledgeSourceItemRow[];\n if (rows.length === 0) return [];\n const ids = rows.map((row) => row.item_id);\n const placeholders = ids.map(() => '?').join(', ');\n db.prepare(\n `UPDATE knowledge_source_items SET\n synthesis_status = 'processing',\n synthesis_attempts = synthesis_attempts + 1,\n synthesis_claimed_at = ?,\n synthesis_claimed_by = ?,\n synthesis_error = NULL,\n updated_at = ?\n WHERE item_id IN (${placeholders})`,\n ).run(now, options.workerId, now, ...ids);\n const claimed = db.prepare(\n `SELECT * FROM knowledge_source_items WHERE item_id IN (${placeholders})`,\n ).all(...ids) as KnowledgeSourceItemRow[];\n return claimed.map(sourceItemFromRow);\n });\n}\n\nexport function completeKnowledgeSourceItemSynthesis(input: {\n itemId: string;\n workerId: string;\n status: 'completed' | 'failed' | 'ignored';\n error?: string;\n nowMs?: number;\n}): boolean {\n const now = input.nowMs ?? Date.now();\n return runSqliteWriteTransaction((db) => {\n const result = db.prepare(\n `UPDATE knowledge_source_items SET\n synthesis_status = ?,\n synthesis_claimed_at = NULL,\n synthesis_claimed_by = NULL,\n synthesis_error = ?,\n updated_at = ?\n WHERE item_id = ? AND synthesis_status = 'processing' AND synthesis_claimed_by = ?`,\n ).run(input.status, input.error ?? null, now, input.itemId, input.workerId);\n return Number(result.changes) > 0;\n });\n}\n\nexport function attachMemoryEvidence(input: {\n recordId: string;\n sourceItemId?: string;\n relation?: MemoryEvidenceRelation;\n excerpt?: string;\n confidence?: number;\n observedAt?: string;\n nowMs?: number;\n}): MemoryEvidence {\n let evidenceId: string = randomUUID();\n const now = input.nowMs ?? Date.now();\n const observedAt = parseTimestamp(input.observedAt);\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO memory_evidence (\n evidence_id, record_id, source_item_id, relation, excerpt,\n confidence, observed_at, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT DO UPDATE SET\n excerpt = COALESCE(excluded.excerpt, memory_evidence.excerpt),\n confidence = MAX(COALESCE(memory_evidence.confidence, 0), COALESCE(excluded.confidence, 0)),\n observed_at = MAX(COALESCE(memory_evidence.observed_at, 0), COALESCE(excluded.observed_at, 0))`,\n ).run(\n evidenceId,\n input.recordId,\n input.sourceItemId ?? null,\n input.relation ?? 'supports',\n input.excerpt ?? null,\n input.confidence ?? null,\n observedAt,\n now,\n );\n if (input.sourceItemId) {\n const stored = db.prepare(\n `SELECT evidence_id FROM memory_evidence\n WHERE record_id = ? AND source_item_id = ? AND relation = ?`,\n ).get(input.recordId, input.sourceItemId, input.relation ?? 'supports') as { evidence_id: string } | undefined;\n evidenceId = stored?.evidence_id ?? evidenceId;\n }\n });\n return {\n evidenceId,\n ...(input.sourceItemId ? { sourceItemId: input.sourceItemId } : {}),\n relation: input.relation ?? 'supports',\n ...(input.excerpt ? { sourceText: input.excerpt } : {}),\n ...(input.confidence != null ? { confidence: input.confidence } : {}),\n ...(observedAt != null ? { observedAt: new Date(observedAt).toISOString() } : {}),\n };\n}\n\nexport function deleteMemoryEvidenceForRecord(\n recordId: string,\n relation?: MemoryEvidenceRelation,\n): number {\n return runSqliteWriteTransaction((db) => {\n const result = relation\n ? db.prepare(`DELETE FROM memory_evidence WHERE record_id = ? AND relation = ?`).run(recordId, relation)\n : db.prepare(`DELETE FROM memory_evidence WHERE record_id = ?`).run(recordId);\n return Number(result.changes);\n });\n}\n\nexport function listMemoryEvidence(recordId: string): MemoryEvidence[] {\n const rows = getSqliteDatabase()\n .prepare(`SELECT * FROM memory_evidence WHERE record_id = ? ORDER BY created_at ASC`)\n .all(recordId) as MemoryEvidenceRow[];\n return rows.map((row) => ({\n evidenceId: row.evidence_id,\n ...(row.source_item_id ? { sourceItemId: row.source_item_id } : {}),\n relation: row.relation as MemoryEvidenceRelation,\n ...(row.excerpt ? { sourceText: row.excerpt } : {}),\n ...(row.confidence != null ? { confidence: row.confidence } : {}),\n ...(timestampToIso(row.observed_at) ? { observedAt: timestampToIso(row.observed_at) } : {}),\n }));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS knowledge_source_changes (
|
|
2
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3
|
+
change_id TEXT NOT NULL UNIQUE,
|
|
4
|
+
source_instance_id TEXT NOT NULL,
|
|
5
|
+
source_item_id TEXT NOT NULL,
|
|
6
|
+
change_kind TEXT NOT NULL CHECK(change_kind IN ('added', 'modified', 'deleted')),
|
|
7
|
+
old_hash TEXT,
|
|
8
|
+
new_hash TEXT,
|
|
9
|
+
changed_at INTEGER NOT NULL,
|
|
10
|
+
FOREIGN KEY(source_item_id) REFERENCES knowledge_source_items(item_id) ON DELETE CASCADE
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_source_changes_source_sequence
|
|
14
|
+
ON knowledge_source_changes(source_instance_id, sequence);
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_source_changes_changed_at
|
|
16
|
+
ON knowledge_source_changes(changed_at DESC);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE IF NOT EXISTS knowledge_consumer_watermarks (
|
|
19
|
+
consumer_id TEXT NOT NULL,
|
|
20
|
+
source_instance_id TEXT NOT NULL,
|
|
21
|
+
last_sequence INTEGER NOT NULL DEFAULT 0,
|
|
22
|
+
updated_at INTEGER NOT NULL,
|
|
23
|
+
PRIMARY KEY(consumer_id, source_instance_id)
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
ALTER TABLE knowledge_source_items ADD COLUMN synthesis_pipeline TEXT NOT NULL DEFAULT 'user_understanding'
|
|
27
|
+
CHECK(synthesis_pipeline IN ('user_understanding', 'connected_knowledge'));
|
|
28
|
+
ALTER TABLE knowledge_source_items ADD COLUMN synthesis_attempts INTEGER NOT NULL DEFAULT 0;
|
|
29
|
+
ALTER TABLE knowledge_source_items ADD COLUMN synthesis_claimed_at INTEGER;
|
|
30
|
+
ALTER TABLE knowledge_source_items ADD COLUMN synthesis_claimed_by TEXT;
|
|
31
|
+
ALTER TABLE knowledge_source_items ADD COLUMN synthesis_error TEXT;
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_source_items_synthesis_lease
|
|
34
|
+
ON knowledge_source_items(synthesis_pipeline, synthesis_status, synthesis_claimed_at, updated_at);
|
|
35
|
+
|
|
36
|
+
DELETE FROM memory_evidence
|
|
37
|
+
WHERE source_item_id IS NOT NULL
|
|
38
|
+
AND rowid NOT IN (
|
|
39
|
+
SELECT MIN(rowid)
|
|
40
|
+
FROM memory_evidence
|
|
41
|
+
WHERE source_item_id IS NOT NULL
|
|
42
|
+
GROUP BY record_id, source_item_id, relation
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_memory_evidence_record_source_relation
|
|
46
|
+
ON memory_evidence(record_id, source_item_id, relation)
|
|
47
|
+
WHERE source_item_id IS NOT NULL;
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 37;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
|
|
|
12
12
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
13
13
|
const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
14
14
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
15
|
-
const XOPC_DB_SCHEMA_VERSION =
|
|
15
|
+
const XOPC_DB_SCHEMA_VERSION = 37;
|
|
16
16
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
function resolveMigrationsDir(override) {
|
|
18
18
|
if (override) return override;
|
|
@@ -46,7 +46,7 @@ function applySingleMigration(db, migration) {
|
|
|
46
46
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
47
47
|
*/
|
|
48
48
|
function applyPendingMigrations(db, options = {}) {
|
|
49
|
-
const targetVersion = options.targetVersion ??
|
|
49
|
+
const targetVersion = options.targetVersion ?? 37;
|
|
50
50
|
let currentVersion = readSchemaVersion(db);
|
|
51
51
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
52
52
|
if (currentVersion >= targetVersion) return currentVersion;
|
|
@@ -62,7 +62,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
62
62
|
}
|
|
63
63
|
/** Inspect schema version without mutating the database. */
|
|
64
64
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
65
|
-
const appVersion = options.targetVersion ??
|
|
65
|
+
const appVersion = options.targetVersion ?? 37;
|
|
66
66
|
const dbVersion = readSchemaVersion(db);
|
|
67
67
|
if (dbVersion > appVersion) return {
|
|
68
68
|
dbVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION =
|
|
1
|
+
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION = 37;\n\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nexport function resolveMigrationsDir(override?: string): string {\n if (override) {\n return override;\n }\n // Packaged Electron gateway bundle: `out/server/index.js` + `out/server/migrations/`.\n const siblingDir = join(MODULE_DIR, 'migrations');\n if (existsSync(siblingDir)) {\n return siblingDir;\n }\n // Dev / dist: SQL files live next to `migrations/runner.js`.\n return MODULE_DIR;\n}\n\nfunction migrationByTarget(\n migrations: SqlMigration[],\n targetVersion: number,\n): SqlMigration | undefined {\n return migrations.find((migration) => migration.targetVersion === targetVersion);\n}\n\nfunction applySingleMigration(db: DatabaseSync, migration: SqlMigration): void {\n log.info({ targetVersion: migration.targetVersion, file: migration.filename }, 'Applying SQLite migration');\n db.exec('BEGIN IMMEDIATE');\n try {\n db.exec(migration.sql);\n setSchemaVersion(db, migration.targetVersion);\n db.exec('COMMIT');\n } catch (error) {\n try {\n db.exec('ROLLBACK');\n } catch {\n /* preserve original error */\n }\n const em = error instanceof Error ? error.message : String(error);\n throw new Error(\n `SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`,\n { cause: error },\n );\n }\n}\n\n/**\n * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.\n * Each step runs in its own transaction and updates schema_meta on success.\n */\nexport function applyPendingMigrations(\n db: DatabaseSync,\n options: ApplyMigrationsOptions = {},\n): number {\n const targetVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n let currentVersion = readSchemaVersion(db);\n\n if (currentVersion > targetVersion) {\n throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);\n }\n\n if (currentVersion >= targetVersion) {\n return currentVersion;\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n\n while (currentVersion < targetVersion) {\n const nextVersion = currentVersion + 1;\n const migration = migrationByTarget(migrations, nextVersion);\n if (!migration) {\n throw new DatabaseSchemaMigrationGapError(currentVersion, targetVersion, nextVersion);\n }\n applySingleMigration(db, migration);\n currentVersion = nextVersion;\n }\n\n return currentVersion;\n}\n\nexport type SchemaMigrationStatus = {\n dbVersion: number;\n appVersion: number;\n pendingVersions: number[];\n isTooNew: boolean;\n hasMigrationGap: boolean;\n missingVersion: number | null;\n};\n\n/** Inspect schema version without mutating the database. */\nexport function inspectSchemaMigrationStatus(\n db: DatabaseSync,\n options: Pick<ApplyMigrationsOptions, 'migrationsDir' | 'targetVersion'> = {},\n): SchemaMigrationStatus {\n const appVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n const dbVersion = readSchemaVersion(db);\n const isTooNew = dbVersion > appVersion;\n\n if (isTooNew) {\n return {\n dbVersion,\n appVersion,\n pendingVersions: [],\n isTooNew: true,\n hasMigrationGap: false,\n missingVersion: null,\n };\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n const pendingVersions: number[] = [];\n let hasMigrationGap = false;\n let missingVersion: number | null = null;\n\n for (let version = dbVersion + 1; version <= appVersion; version++) {\n if (migrationByTarget(migrations, version)) {\n pendingVersions.push(version);\n } else {\n hasMigrationGap = true;\n missingVersion = version;\n break;\n }\n }\n\n return {\n dbVersion,\n appVersion,\n pendingVersions,\n isTooNew: false,\n hasMigrationGap,\n missingVersion,\n };\n}\n"],"mappings":";;;;;;;;;aAMwD;AASxD,MAAM,MAAM,aAAa,oBAAoB;;AAG7C,MAAa,kCAAkC;;AAG/C,MAAa,yBAAyB;AAEtC,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE1D,SAAgB,qBAAqB,UAA2B;AAC9D,KAAI,SACF,QAAO;CAGT,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,KAAI,WAAW,WAAW,CACxB,QAAO;AAGT,QAAO;;AAGT,SAAS,kBACP,YACA,eAC0B;AAC1B,QAAO,WAAW,MAAM,cAAc,UAAU,kBAAkB,cAAc;;AAGlF,SAAS,qBAAqB,IAAkB,WAA+B;AAC7E,KAAI,KAAK;EAAE,eAAe,UAAU;EAAe,MAAM,UAAU;EAAU,EAAE,4BAA4B;AAC3G,IAAG,KAAK,kBAAkB;AAC1B,KAAI;AACF,KAAG,KAAK,UAAU,IAAI;AACtB,mBAAiB,IAAI,UAAU,cAAc;AAC7C,KAAG,KAAK,SAAS;UACV,OAAO;AACd,MAAI;AACF,MAAG,KAAK,WAAW;UACb;EAGR,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACjE,QAAM,IAAI,MACR,wBAAwB,UAAU,cAAc,IAAI,UAAU,SAAS,YAAY,MACnF,EAAE,OAAO,OAAO,CACjB;;;;;;;AAQL,SAAgB,uBACd,IACA,UAAkC,EAAE,EAC5B;CACR,MAAM,gBAAgB,QAAQ,iBAAA;CAC9B,IAAI,iBAAiB,kBAAkB,GAAG;AAE1C,KAAI,iBAAiB,cACnB,OAAM,IAAI,0BAA0B,gBAAgB,cAAc;AAGpE,KAAI,kBAAkB,cACpB,QAAO;CAGT,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;AAErF,QAAO,iBAAiB,eAAe;EACrC,MAAM,cAAc,iBAAiB;EACrC,MAAM,YAAY,kBAAkB,YAAY,YAAY;AAC5D,MAAI,CAAC,UACH,OAAM,IAAI,gCAAgC,gBAAgB,eAAe,YAAY;AAEvF,uBAAqB,IAAI,UAAU;AACnC,mBAAiB;;AAGnB,QAAO;;;AAaT,SAAgB,6BACd,IACA,UAA2E,EAAE,EACtD;CACvB,MAAM,aAAa,QAAQ,iBAAA;CAC3B,MAAM,YAAY,kBAAkB,GAAG;AAGvC,KAFiB,YAAY,WAG3B,QAAO;EACL;EACA;EACA,iBAAiB,EAAE;EACnB,UAAU;EACV,iBAAiB;EACjB,gBAAgB;EACjB;CAGH,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;CACrF,MAAM,kBAA4B,EAAE;CACpC,IAAI,kBAAkB;CACtB,IAAI,iBAAgC;AAEpC,MAAK,IAAI,UAAU,YAAY,GAAG,WAAW,YAAY,UACvD,KAAI,kBAAkB,YAAY,QAAQ,CACxC,iBAAgB,KAAK,QAAQ;MACxB;AACL,oBAAkB;AAClB,mBAAiB;AACjB;;AAIJ,QAAO;EACL;EACA;EACA;EACA,UAAU;EACV;EACA;EACD"}
|