@xopcai/xopc 0.0.366 → 0.0.367
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/{activity-store-SVmnh__i.js → activity-store-Cuf3JFFJ.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CS9lg5h2.js → agent-avatar-dicebear-DyATPNTe.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-gT9_j4tr.js +1 -0
- package/dist/gateway/static/root/assets/agent-plugin-dialog-CLXCJ6JN.js +1 -0
- package/dist/gateway/static/root/assets/agents-BAUfOMsq.js +66 -0
- package/dist/gateway/static/root/assets/{agents-admin-api-CVdFV-cU.js → agents-admin-api-D842OcNo.js} +1 -1
- package/dist/gateway/static/root/assets/app-link-4gpFbdsr.js +1 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CaxnUxvy.js → app-management-settings-panel-CoIFxQKH.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-6NKquG3v.js → appearance-settings-DQr0o6Lm.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BoB3MbsB.js → apps-page-CltMV4Xp.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-BWtLZtK1.js → archive-plugin-DT8Nio-r.js} +1 -1
- package/dist/gateway/static/root/assets/automation-display-DCkjVzA0.js +2 -0
- package/dist/gateway/static/root/assets/{automations-page-CNngHw2c.js → automations-page-BIfWS_3Y.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BCGPjsdU.js +6 -0
- package/dist/gateway/static/root/assets/binary-plugins-7qs3qcW9.js +2 -0
- package/dist/gateway/static/root/assets/{block-editor-BhstSZNE.js → block-editor-BFPUlxJ6.js} +28 -28
- package/dist/gateway/static/root/assets/{browser-automation-inputs-B6sOBxVA.js → browser-automation-inputs-BUAIKIX0.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-HZAKkVVt.js → browser-automations-page-CSwu1U1F.js} +2 -2
- package/dist/gateway/static/root/assets/browser-settings-page-C1ReMNqX.js +3 -0
- package/dist/gateway/static/root/assets/capabilities-BzWFO_x_.js +1 -0
- package/dist/gateway/static/root/assets/{capabilities-CAIQmCrH.js → capabilities-K0Zb_lH0.js} +1 -1
- package/dist/gateway/static/root/assets/capabilities-page-BdTSqloI.js +2 -0
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-BP2roWcM.js → capabilities-settings-panel-BUblBMZC.js} +4 -4
- package/dist/gateway/static/root/assets/{capability-header-actions-BA1M7RRP.js → capability-header-actions-AMn_vTNv.js} +1 -1
- package/dist/gateway/static/root/assets/channels-CXCf5qO6.js +1 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-KS_yk1H7.js → chat-terminal-dock-C7nAROhF.js} +2 -2
- package/dist/gateway/static/root/assets/chevron-left-DqFhpbvK.js +1 -0
- package/dist/gateway/static/root/assets/computer-settings-page-B5SuSZ3G.js +1 -0
- package/dist/gateway/static/root/assets/{connector-service-page-CY_3H_l9.js → connector-service-page-CTx1B8wx.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-DfnK1crf.js +2 -0
- package/dist/gateway/static/root/assets/date-formatters-CUZAyDEg.js +1 -0
- package/dist/gateway/static/root/assets/date-picker-Cx_xIKvN.js +1 -0
- package/dist/gateway/static/root/assets/{dependency-picker-CyNH4m20.js → dependency-picker-DYm96PDu.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-BsZQsQ2_.js → desktop-pet-D90OzgeR.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DMgkUy6g.js → desktop-pet-settings-CmdCecjm.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-JjbUN8y-.js → device-pairing-wizard-s2ogSa4l.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CBTI3tKh.js → directory-picker-path-field-DZpaoeNa.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-C7nSpOKj.js → extension-debug-page-Bv6QE8kC.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BYUjAvsZ.js → extension-page-CEsq71Cm.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-DaLt3bC-.js → extension-provider-CX-sK0Tw.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CRasspq0.js → extension-settings-page-UyOHF39P.js} +1 -1
- package/dist/gateway/static/root/assets/{fallback-models-BLJFYASE.js → fallback-models-CKv_Wqg8.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-DIIzvDLQ.js → gateway-config-swr-Cjnkle5J.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-realtime-DD70L42G.js → gateway-realtime-DCCLdyQD.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-C_Th8kGg.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-CZ73IpbP.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-D_EwU7-6.js → gateway-startup-retry-CXKmdVzN.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-BQWq4kYi.js +2 -0
- package/dist/gateway/static/root/assets/{image-generation-api-BkK22JsL.js → image-generation-api-NLHzm4Nc.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-YJKcTKKr.js → imports-page-C7qvlBTQ.js} +1 -1
- package/dist/gateway/static/root/assets/index-BQgqzhQj.js +106 -0
- package/dist/gateway/static/root/assets/index-COB4QIJp.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BG_kM_Rm.js → keyboard-shortcuts-settings-pkgFWJb-.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-dMDhZXAi.js +1 -0
- package/dist/gateway/static/root/assets/{local-apps-page-jqthjcQO.js → local-apps-page-TpUAfOn2.js} +1 -1
- package/dist/gateway/static/root/assets/{local-session-drafts-C4et6msJ.js → local-session-drafts-Cvv7I0fw.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-CvNO-hbt.js → locale-store-C8TMD2gn.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-B-Y9oZLh.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-B2lzsw6X.js +1 -0
- package/dist/gateway/static/root/assets/markdown-view-Dse7rkio.js +201 -0
- package/dist/gateway/static/root/assets/{media-plugins-C25mOgs2.js → media-plugins-BZrnzaBM.js} +1 -1
- package/dist/gateway/static/root/assets/messages-Dy26npMy.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-cache-B1C86nRp.js +1 -0
- package/dist/gateway/static/root/assets/note-detail-panel-DL__qP6U.js +5 -0
- package/dist/gateway/static/root/assets/{notes-api-CKLw9vjk.js → notes-api-DKiVeQKG.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-DTBbg9Hs.js +8 -0
- package/dist/gateway/static/root/assets/{oauth-api-CC5KkSgG.js → oauth-api-BwpzpRwC.js} +1 -1
- package/dist/gateway/static/root/assets/open-route-MyZ2sB9C.js +1 -0
- package/dist/gateway/static/root/assets/{page-context-capture-button-KX9mrcJW.js → page-context-capture-button-Cxzd4QPB.js} +1 -1
- package/dist/gateway/static/root/assets/personal-page-CzaTnHeD.js +1 -0
- package/dist/gateway/static/root/assets/product-open-page-D4hoEWiu.js +1 -0
- package/dist/gateway/static/root/assets/product-preview-content-aR38El2E.js +1 -0
- package/dist/gateway/static/root/assets/project-detail-page-D20QH4YB.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-Bf3BIcT6.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-CiN49wvk.js → registry-api-AdnxowbH.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CKxqncqW.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-wZjlSBiM.js → runtime-tools-settings-panel-CBN3raz1.js} +1 -1
- package/dist/gateway/static/root/assets/scenes-page-Cv_5ozfX.js +2 -0
- package/dist/gateway/static/root/assets/{schema-form-CMB61TV1.js → schema-form-CxrMGsT9.js} +1 -1
- package/dist/gateway/static/root/assets/{send-session-input-jH6MQwdZ.js → send-session-input-CgOzhDZ0.js} +1 -1
- package/dist/gateway/static/root/assets/{session-api-CW2X7g_U.js → session-api-_HIN8or3.js} +1 -1
- package/dist/gateway/static/root/assets/session-manager-CzCto5zT.js +16 -0
- package/dist/gateway/static/root/assets/sessions-page-DZcu6NLJ.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-DazqeDQ7.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-Ch71KQjb.js → settings-page-D8Q3H3-o.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DvLMMiuS.js +1 -0
- package/dist/gateway/static/root/assets/{share-link-dialog-DCSmx-Rl.js → share-link-dialog-Dg5os066.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-C93IM3QK.js → share-preview-page-DMvohAyr.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-api-B4rLfG3l.js → shares-api-C96IO4qJ.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-C_6idV8e.js → shares-settings-CsDATuCw.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-sKX8SSlQ.js → skill-api-C10ML0CH.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-Ro-s69NW.js → skill-reload-api-DNbJzSfB.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-DObMKvA6.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DIjaUlla.js → system-settings-panel-Bn3khdzb.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-BR2L1L8l.js +2 -0
- package/dist/gateway/static/root/assets/task-expandable-content-DL2Q2gre.js +1 -0
- package/dist/gateway/static/root/assets/{tasks-CH6KoBeY.js → tasks-DwKXgZ-I.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-DbRLBHVY.js → theme-store-DQ6KJa4l.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-IgY26AYH.js → tracing-settings-page-Ed8_GYUT.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-CgARwbus.js → tunnel-api-D3X8jDwo.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-CKju62Cm.js → usage-page-BqzRyyb9.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-C889OTsP.js → use-autosave-CRFefpzO.js} +1 -1
- package/dist/gateway/static/root/assets/use-composer-attachments-OLrvSjn5.js +2 -0
- package/dist/gateway/static/root/assets/{use-file-preview-expanded-BOpcSApw.js → use-file-preview-expanded-CJvzfQvw.js} +2 -2
- package/dist/gateway/static/root/assets/user-model-page-CG6wqIKP.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-evWflGY6.js → voice-api-key-field-R9zNWYR_.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-CZoV2Lt4.js → voice-config-api-DLudE9Rc.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-B2-2F39_.js → work-discovery-overlay-DVmQrolV.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-cOGM8Nug.js → workflow-run-setup-panel-DEkmuD3T.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-pqCB3sh4.js +6 -0
- package/dist/gateway/static/root/assets/workspace-api-CdVUhyPo.js +2 -0
- package/dist/gateway/static/root/index.html +32 -30
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/automations.js +1 -0
- package/dist/packages/gateway-contract/src/home.js +14 -0
- package/dist/packages/gateway-contract/src/index.js +2 -0
- package/dist/packages/gateway-contract/src/notifications.js +2 -1
- package/dist/packages/gateway-contract/src/personal-proactivity.js +139 -0
- package/dist/packages/image-providers/src/catalog.js +373 -0
- package/dist/packages/image-providers/src/execute.js +372 -0
- package/dist/packages/image-providers/src/http.js +94 -0
- package/dist/packages/image-providers/src/index.js +5 -0
- package/dist/packages/image-providers/src/types.d.ts +95 -0
- package/dist/packages/image-providers/src/validation.js +52 -0
- package/dist/src/agent/commands/command-registry.js +1 -1
- package/dist/src/agent/failover-error.d.ts +1 -1
- package/dist/src/agent/failover-error.js +7 -0
- package/dist/src/agent/image/generation/native-provider.d.ts +2 -0
- package/dist/src/agent/image/generation/native-provider.js +118 -0
- package/dist/src/agent/image/generation/openai-images-provider.d.ts +1 -1
- package/dist/src/agent/image/generation/openai-images-provider.js +48 -32
- package/dist/src/agent/image/generation/provider-registry.js +4 -13
- package/dist/src/agent/image/generation/providers/xopc-cloud.js +5 -34
- package/dist/src/agent/image/generation/runtime-types.d.ts +2 -4
- package/dist/src/agent/image/generation/runtime.js +28 -23
- package/dist/src/agent/image/generation/types.d.ts +3 -50
- package/dist/src/agent/image/generation/validation.d.ts +6 -0
- package/dist/src/agent/image/generation/validation.js +16 -0
- package/dist/src/agent/tools/factory.js +5 -0
- package/dist/src/agent/tools/image-generate-tool.js +5 -19
- package/dist/src/agent/tools/personal-attention-tool.d.ts +21 -0
- package/dist/src/agent/tools/personal-attention-tool.js +191 -0
- package/dist/src/automations/capabilities/write.d.ts +2 -0
- package/dist/src/automations/domain/types.d.ts +1 -1
- package/dist/src/automations/domain/validation.d.ts +4 -0
- package/dist/src/automations/domain/validation.js +1 -0
- package/dist/src/automations/draft/automation-draft-validator.d.ts +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/models.js +3 -8
- package/dist/src/config/models-json.d.ts +170 -0
- package/dist/src/config/models-json.js +23 -8
- package/dist/src/files/file-service.d.ts +6 -0
- package/dist/src/files/file-service.js +38 -1
- package/dist/src/gateway/custom-image-providers.d.ts +34 -0
- package/dist/src/gateway/file-path-classifier.js +12 -3
- package/dist/src/gateway/hono/routes/files.js +13 -2
- package/dist/src/gateway/hono/routes/personal-agent.js +41 -0
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +50 -11
- package/dist/src/home-intelligence/host.js +2 -2
- package/dist/src/notifications/planner.js +32 -0
- package/dist/src/notifications/service.d.ts +2 -0
- package/dist/src/personal-agent/notifications.d.ts +4 -0
- package/dist/src/personal-agent/notifications.js +40 -0
- package/dist/src/personal-agent/policy.d.ts +1 -1
- package/dist/src/personal-agent/policy.js +2 -0
- package/dist/src/personal-agent/proactivity/automation.d.ts +3 -0
- package/dist/src/personal-agent/proactivity/automation.js +41 -0
- package/dist/src/personal-agent/proactivity/follow-up-request.d.ts +5 -0
- package/dist/src/personal-agent/proactivity/follow-up-request.js +44 -0
- package/dist/src/personal-agent/proactivity/repository.d.ts +128 -0
- package/dist/src/personal-agent/proactivity/repository.js +403 -0
- package/dist/src/personal-agent/proactivity/routes.d.ts +3 -0
- package/dist/src/personal-agent/proactivity/routes.js +182 -0
- package/dist/src/personal-agent/proactivity/service.d.ts +32 -0
- package/dist/src/personal-agent/proactivity/service.js +496 -0
- package/dist/src/personal-agent/prompts.js +13 -2
- package/dist/src/personal-agent/reply-composer.js +2 -2
- package/dist/src/personal-agent/unread.d.ts +10 -0
- package/dist/src/personal-agent/unread.js +43 -0
- package/dist/src/providers/env-keys.js +6 -0
- package/dist/src/providers/model-catalog-persistence.js +3 -22
- package/dist/src/providers/model-catalog-store.js +1 -8
- package/dist/src/providers/model-catalog-types.d.ts +1 -9
- package/dist/src/providers/xopc-cloud-model-source.js +4 -25
- package/dist/src/session/client-history.d.ts +2 -0
- package/dist/src/session/client-history.js +26 -0
- package/dist/src/session/session-context-for-llm.js +28 -0
- package/dist/src/storage/sqlite/migrations/236_personal_proactivity.sql +53 -0
- package/dist/src/storage/sqlite/migrations/237_personal_strategy_versions.sql +18 -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/schema.js +1 -1
- package/dist/src/tasks/home-query-service.js +27 -1
- package/dist/src/voice/platform-catalog.d.ts +1 -9
- package/package.json +4 -3
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CvUPtxS4.js +0 -1
- package/dist/gateway/static/root/assets/agent-plugin-dialog-1tRxDfjI.js +0 -1
- package/dist/gateway/static/root/assets/agents-gdT0whx9.js +0 -66
- package/dist/gateway/static/root/assets/app-link-DRL0YnPs.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-BKHsdiQ8.js +0 -7
- package/dist/gateway/static/root/assets/binary-plugins-fIhH-wg6.js +0 -2
- package/dist/gateway/static/root/assets/browser-settings-page-D8IpVQI6.js +0 -3
- package/dist/gateway/static/root/assets/capabilities-C4grvUkw.js +0 -1
- package/dist/gateway/static/root/assets/capabilities-page-DK8FJRW4.js +0 -2
- package/dist/gateway/static/root/assets/channels-EQGCxr8W.js +0 -1
- package/dist/gateway/static/root/assets/computer-settings-page-CGBR0KFw.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CUOL6JX0.js +0 -2
- package/dist/gateway/static/root/assets/date-picker-CDSZ-PWV.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-DeUVEPPE.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-B9n5naCr.js +0 -1
- package/dist/gateway/static/root/assets/home-page-BP6qAgBt.js +0 -1
- package/dist/gateway/static/root/assets/index-6JNNrzCq.js +0 -106
- package/dist/gateway/static/root/assets/index-CbJNcWAn.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-lHi21M1T.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-CH7W-dm5.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-DVgI_KNm.js +0 -1
- package/dist/gateway/static/root/assets/markdown-view-DDJlD5jR.js +0 -201
- package/dist/gateway/static/root/assets/messages-pGa2qMzG.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-cache-DzFiL4xV.js +0 -1
- package/dist/gateway/static/root/assets/note-detail-panel-kyX-n1db.js +0 -5
- package/dist/gateway/static/root/assets/notes-page-CLZygZlm.js +0 -8
- package/dist/gateway/static/root/assets/personal-page-DnXlcJNJ.js +0 -1
- package/dist/gateway/static/root/assets/product-open-page-D7pknlCh.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-DhV9DPj8.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-D181xkFw.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CQnBARQW.js +0 -1
- package/dist/gateway/static/root/assets/scenes-page-DWt54veq.js +0 -2
- package/dist/gateway/static/root/assets/session-manager-BDpdi3lF.js +0 -16
- package/dist/gateway/static/root/assets/sessions-page-DPEdjUdR.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-B6WGnTLY.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-C7sLy8Hl.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-TsrjLYcI.js +0 -2
- package/dist/gateway/static/root/assets/task-detail-page-B4Lby289.js +0 -2
- package/dist/gateway/static/root/assets/use-composer-attachments-Dlm9EFTO.js +0 -2
- package/dist/gateway/static/root/assets/user-model-page-CTkCLRhE.js +0 -2
- package/dist/gateway/static/root/assets/workflows-page-Csk8xrRk.js +0 -6
- package/dist/gateway/static/root/assets/workspace-api-B9M9ECTB.js +0 -2
- package/dist/src/agent/image/generation/normalization.d.ts +0 -38
- package/dist/src/agent/image/generation/normalization.js +0 -181
- package/dist/src/agent/image/generation/providers/dashscope.d.ts +0 -35
- package/dist/src/agent/image/generation/providers/dashscope.js +0 -257
- package/dist/src/agent/image/generation/providers/fal.d.ts +0 -15
- package/dist/src/agent/image/generation/providers/fal.js +0 -323
- package/dist/src/agent/image/generation/providers/google.d.ts +0 -16
- package/dist/src/agent/image/generation/providers/google.js +0 -244
- package/dist/src/agent/image/generation/providers/minimax.d.ts +0 -23
- package/dist/src/agent/image/generation/providers/minimax.js +0 -215
- package/dist/src/agent/image/generation/providers/openai.d.ts +0 -5
- package/dist/src/agent/image/generation/providers/openai.js +0 -134
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
|
|
2
|
+
import { PersonalFeedbackSchema, PersonalProactivitySettingsSchema, PersonalStrategyRollbackSchema } from "../../../packages/gateway-contract/src/personal-proactivity.js";
|
|
3
|
+
import { init_src } from "../../../packages/gateway-contract/src/index.js";
|
|
4
|
+
import { createContextEvidence, init_context_evidence_repository } from "../../storage/sqlite/context-evidence-repository.js";
|
|
5
|
+
import { init_session_instance_repository, readCurrentTranscriptId } from "../../storage/sqlite/session-instance-repository.js";
|
|
6
|
+
import { getUserProfileSnapshot } from "../../user-model/profile.js";
|
|
7
|
+
import { getPersonalAgentByConversation } from "../repository.js";
|
|
8
|
+
import { DurableState } from "../../storage/sqlite/durable-state.js";
|
|
9
|
+
import { isExplicitFollowUp, requestedCheckTime } from "./follow-up-request.js";
|
|
10
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
+
//#region src/personal-agent/proactivity/repository.ts
|
|
12
|
+
init_src();
|
|
13
|
+
init_context_evidence_repository();
|
|
14
|
+
init_transaction();
|
|
15
|
+
init_session_instance_repository();
|
|
16
|
+
function getProactivitySettings(ownerId) {
|
|
17
|
+
const saved = new DurableState("personal-proactivity", ownerId).get("settings");
|
|
18
|
+
const timezone = getUserProfileSnapshot().timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
19
|
+
return PersonalProactivitySettingsSchema.parse(saved ?? { timezone });
|
|
20
|
+
}
|
|
21
|
+
function patchProactivitySettings(ownerId, input) {
|
|
22
|
+
return runSqliteWriteTransaction(() => {
|
|
23
|
+
const current = getProactivitySettings(ownerId);
|
|
24
|
+
if (input.revision !== current.revision) throw new Error("Proactivity settings changed");
|
|
25
|
+
const next = PersonalProactivitySettingsSchema.parse({
|
|
26
|
+
...input,
|
|
27
|
+
revision: current.revision + 1
|
|
28
|
+
});
|
|
29
|
+
new DurableState("personal-proactivity", ownerId).set("settings", next);
|
|
30
|
+
return next;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function localDay(now, timezone) {
|
|
34
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
35
|
+
timeZone: timezone,
|
|
36
|
+
year: "numeric",
|
|
37
|
+
month: "2-digit",
|
|
38
|
+
day: "2-digit"
|
|
39
|
+
}).format(now);
|
|
40
|
+
}
|
|
41
|
+
function isQuiet(now, settings) {
|
|
42
|
+
const hour = Number(new Intl.DateTimeFormat("en-GB", {
|
|
43
|
+
timeZone: settings.timezone,
|
|
44
|
+
hour: "2-digit",
|
|
45
|
+
hourCycle: "h23"
|
|
46
|
+
}).format(now));
|
|
47
|
+
const { quietStart: start, quietEnd: end } = settings;
|
|
48
|
+
return start === end ? false : start < end ? hour >= start && hour < end : hour >= start || hour < end;
|
|
49
|
+
}
|
|
50
|
+
function reserveModelCall(ownerId, now) {
|
|
51
|
+
return runSqliteWriteTransaction(() => {
|
|
52
|
+
const settings = getProactivitySettings(ownerId);
|
|
53
|
+
const state = new DurableState("personal-proactivity-budget", ownerId);
|
|
54
|
+
const key = localDay(now, settings.timezone);
|
|
55
|
+
const calls = state.get(key) ?? 0;
|
|
56
|
+
if (calls >= settings.dailyModelCalls) return false;
|
|
57
|
+
state.set(key, calls + 1);
|
|
58
|
+
for (const [old] of state.entries()) if (old !== key) state.delete(old);
|
|
59
|
+
return true;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function getAttention(id, ownerId) {
|
|
63
|
+
const row = getSqliteDatabase().prepare("SELECT * FROM personal_attention_threads WHERE id = ?").get(id);
|
|
64
|
+
return row && (!ownerId || row.owner_id === ownerId) ? row : void 0;
|
|
65
|
+
}
|
|
66
|
+
function listAttention(conversationId) {
|
|
67
|
+
return getSqliteDatabase().prepare("SELECT * FROM personal_attention_threads WHERE conversation_id = ? ORDER BY updated_at DESC LIMIT 30").all(conversationId);
|
|
68
|
+
}
|
|
69
|
+
function getAttentionSources(threadId, entryIds) {
|
|
70
|
+
if (entryIds && (!entryIds.length || entryIds.length > 12)) return [];
|
|
71
|
+
return getSqliteDatabase().prepare(`SELECT s.entry_id, s.transcript_id, s.conversation_id, t.payload_json, e.processing_policy
|
|
72
|
+
FROM personal_attention_sources s JOIN transcript_entries t ON t.entry_id = s.entry_id
|
|
73
|
+
JOIN context_evidence e ON e.evidence_id = s.evidence_id
|
|
74
|
+
JOIN sessions c ON c.conversation_id = s.conversation_id
|
|
75
|
+
WHERE s.thread_id = ? AND c.active_transcript_id = s.transcript_id AND t.role = 'user'
|
|
76
|
+
AND COALESCE(json_extract(t.payload_json, '$.metadata.hiddenFromClient'), 0) = 0
|
|
77
|
+
${entryIds ? `AND s.entry_id IN (${entryIds.map(() => "?").join(",")})` : ""} ORDER BY t.seq DESC LIMIT 12`).all(threadId, ...entryIds ?? []).map((row) => ({
|
|
78
|
+
entryId: row.entry_id,
|
|
79
|
+
transcriptId: row.transcript_id,
|
|
80
|
+
conversationId: row.conversation_id,
|
|
81
|
+
excerpt: userText(JSON.parse(row.payload_json)).slice(0, 600),
|
|
82
|
+
processingPolicy: row.processing_policy,
|
|
83
|
+
version: createHash("sha256").update(row.payload_json).digest("hex")
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
function userText(payload) {
|
|
87
|
+
if (typeof payload.content === "string") return payload.content;
|
|
88
|
+
return Array.isArray(payload.content) ? payload.content.map((part) => part?.type === "text" ? part.text : "").join("\n") : "";
|
|
89
|
+
}
|
|
90
|
+
function sourceFingerprint(sources) {
|
|
91
|
+
return createHash("sha256").update(JSON.stringify(sources)).digest("hex");
|
|
92
|
+
}
|
|
93
|
+
/** Only user transcript rows can establish attention; inferred attention never grants work authorization. */
|
|
94
|
+
function saveAttention(input, now = Date.now()) {
|
|
95
|
+
return runSqliteWriteTransaction((db) => {
|
|
96
|
+
const personal = getPersonalAgentByConversation(input.conversationId);
|
|
97
|
+
if (!personal || personal.state !== "ready") throw new Error("Personal Agent is unavailable");
|
|
98
|
+
const transcriptId = readCurrentTranscriptId(db, input.conversationId);
|
|
99
|
+
const subject = input.subject.trim().slice(0, 160);
|
|
100
|
+
if (!subject || !input.entryIds.length || input.entryIds.length > 12) throw new Error("Attention needs user evidence");
|
|
101
|
+
const sources = [...new Set(input.entryIds)].map((id) => {
|
|
102
|
+
const row = db.prepare("SELECT payload_json, created_at FROM transcript_entries WHERE entry_id = ? AND transcript_id = ? AND role = 'user' AND COALESCE(json_extract(payload_json, '$.metadata.hiddenFromClient'), 0) = 0").get(id, transcriptId);
|
|
103
|
+
if (!row) throw new Error("Attention source is unavailable");
|
|
104
|
+
const text = userText(JSON.parse(row.payload_json));
|
|
105
|
+
if (!text.trim()) throw new Error("Attention source is empty");
|
|
106
|
+
return {
|
|
107
|
+
id,
|
|
108
|
+
text,
|
|
109
|
+
observedAt: row.created_at
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
const explicitSource = sources.filter((source) => isExplicitFollowUp(source.text)).sort((a, b) => b.observedAt - a.observedAt)[0];
|
|
113
|
+
const explicit = input.explicit && Boolean(explicitSource);
|
|
114
|
+
if (input.explicit && !explicit) throw new Error("Follow-up requires an explicit user request");
|
|
115
|
+
if (!Number.isFinite(input.nextCheckAt)) throw new Error("Invalid attention time");
|
|
116
|
+
const existing = db.prepare("SELECT * FROM personal_attention_threads WHERE conversation_id = ? AND subject = ? COLLATE NOCASE LIMIT 1").get(input.conversationId, subject);
|
|
117
|
+
if (existing && [
|
|
118
|
+
"completed",
|
|
119
|
+
"expired",
|
|
120
|
+
"paused"
|
|
121
|
+
].includes(existing.status)) return existing;
|
|
122
|
+
const id = existing?.id ?? randomUUID();
|
|
123
|
+
const settings = getProactivitySettings(personal.ownerId);
|
|
124
|
+
if (input.kind === "interest" && existing?.authority === "user_explicit") return existing;
|
|
125
|
+
const kind = existing && !(existing.kind === "interest" && explicit) ? existing.kind : input.kind;
|
|
126
|
+
if (kind === "interest" && (explicit || sources.some((source) => isExplicitFollowUp(source.text)))) throw new Error("Follow-up requests are not interest evidence");
|
|
127
|
+
const authority = existing?.authority === "user_explicit" || explicit ? "user_explicit" : "inferred";
|
|
128
|
+
const normalize = (text) => text.slice(0, 600).trim().replace(/\s+/g, " ").toLocaleLowerCase();
|
|
129
|
+
const oldCount = existing ? getAttentionSources(id).map((source) => normalize(source.excerpt)) : [];
|
|
130
|
+
const count = new Set([...oldCount, ...sources.map((source) => normalize(source.text))]).size;
|
|
131
|
+
if (kind === "interest") {
|
|
132
|
+
const evidence = interestSupport([...existing ? interestSources(existing.id) : [], ...sources.map((source) => ({
|
|
133
|
+
entryId: source.id,
|
|
134
|
+
excerpt: source.text,
|
|
135
|
+
observedAt: source.observedAt
|
|
136
|
+
}))], settings.timezone, now);
|
|
137
|
+
if (evidence.independentStatements < 2 || evidence.independentDays < 2) throw new Error("Interest needs independent user evidence across days");
|
|
138
|
+
}
|
|
139
|
+
const status = kind === "interest" ? "candidate" : authority === "user_explicit" || settings.mode === "balanced" && count >= 2 ? "active" : "candidate";
|
|
140
|
+
if (input.taskId && !db.prepare(`SELECT 1 FROM task_origin_links WHERE task_id = ? AND conversation_id = ?`).get(input.taskId, input.conversationId)) throw new Error("Task is not linked to this conversation");
|
|
141
|
+
if (input.projectId && !db.prepare("SELECT 1 FROM sessions WHERE conversation_id = ? AND project_id = ?").get(input.conversationId, input.projectId)) throw new Error("Project is not linked to this conversation");
|
|
142
|
+
const requestedAt = explicit && explicitSource ? requestedCheckTime(explicitSource.text, explicitSource.observedAt, settings.timezone) : void 0;
|
|
143
|
+
const nextAt = Math.max(now + 6e4, Math.min(requestedAt ?? input.nextCheckAt, now + 30 * 864e5));
|
|
144
|
+
const observedAt = Math.min(now, Math.max(...sources.map((source) => source.observedAt), existing?.last_meaningful_at ?? 0));
|
|
145
|
+
const expiresAt = observedAt + 30 * 864e5;
|
|
146
|
+
if (existing) db.prepare(`UPDATE personal_attention_threads SET summary = ?, authority = ?, status = ?, kind = ?,
|
|
147
|
+
task_id = COALESCE(?, task_id), project_id = COALESCE(?, project_id),
|
|
148
|
+
next_check_at = ?, expires_at = ?, last_meaningful_at = ?, revision = revision + 1, updated_at = ? WHERE id = ?`).run(input.summary.slice(0, 1600), authority, status, kind, input.taskId ?? null, input.projectId ?? null, kind === "interest" ? null : Math.min(nextAt, expiresAt), expiresAt, observedAt, now, id);
|
|
149
|
+
else db.prepare(`INSERT INTO personal_attention_threads
|
|
150
|
+
(id, owner_id, agent_id, conversation_id, subject, summary, kind, status, authority, task_id, project_id,
|
|
151
|
+
next_check_at, expires_at, last_meaningful_at, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(id, personal.ownerId, personal.agentId, input.conversationId, subject, input.summary.slice(0, 1600), kind, status, authority, input.taskId ?? null, input.projectId ?? null, kind === "interest" ? null : Math.min(nextAt, expiresAt), expiresAt, observedAt, now, now);
|
|
152
|
+
for (const source of sources) {
|
|
153
|
+
const evidence = createContextEvidence({
|
|
154
|
+
sourceType: "conversation",
|
|
155
|
+
sourceRef: `personal-attention:${source.id}`,
|
|
156
|
+
conversationId: input.conversationId,
|
|
157
|
+
messageId: source.id,
|
|
158
|
+
trustLevel: "owner",
|
|
159
|
+
observedAt: source.observedAt
|
|
160
|
+
});
|
|
161
|
+
db.prepare("INSERT OR IGNORE INTO personal_attention_sources(thread_id,evidence_id,entry_id,transcript_id,conversation_id) VALUES (?,?,?,?,?)").run(id, evidence.id, source.id, transcriptId, input.conversationId);
|
|
162
|
+
}
|
|
163
|
+
return getAttention(id);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function patchAttention(ownerId, id, revision, patch, now = Date.now()) {
|
|
167
|
+
return runSqliteWriteTransaction((db) => {
|
|
168
|
+
const current = getAttention(id, ownerId);
|
|
169
|
+
if (!current) throw new Error("Attention is unavailable");
|
|
170
|
+
if (current.revision !== revision) throw new Error("Attention changed");
|
|
171
|
+
const status = patch.status ?? current.status;
|
|
172
|
+
if (current.kind === "interest" && current.authority === "inferred" && ["active", "paused"].includes(status)) throw new Error("Interest discovery is silent; explicit follow-up is required");
|
|
173
|
+
db.prepare(`UPDATE personal_attention_threads SET status = ?, next_check_at = ?, pause_until = ?, revision = revision + 1, updated_at = ? WHERE id = ?`).run(status, patch.nextCheckAt === void 0 ? current.next_check_at : patch.nextCheckAt, status === "paused" ? patch.nextCheckAt ?? null : null, now, id);
|
|
174
|
+
db.prepare("UPDATE personal_wakeups SET status = 'cancelled' WHERE thread_id = ? AND status IN ('pending','evaluating')").run(id);
|
|
175
|
+
db.prepare("UPDATE personal_outreach SET state = 'cancelled' WHERE thread_id = ? AND state = 'ready'").run(id);
|
|
176
|
+
return getAttention(id);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function enqueueWakeup(thread, kind, identity, now = Date.now()) {
|
|
180
|
+
getSqliteDatabase().prepare(`INSERT OR IGNORE INTO personal_wakeups
|
|
181
|
+
(id,thread_id,dedupe_key,trigger_kind,source_event_id,thread_revision,not_before,expires_at,created_at) VALUES (?,?,?,?,?,?,?,?,?)`).run(randomUUID(), thread.id, `${thread.id}:${identity}`, kind, kind === "change" ? identity : null, thread.revision, now, Math.min(thread.expires_at, now + 864e5), now);
|
|
182
|
+
}
|
|
183
|
+
function enqueueDue(now = Date.now()) {
|
|
184
|
+
runSqliteWriteTransaction((db) => {
|
|
185
|
+
db.prepare("UPDATE personal_attention_threads SET status = 'expired', revision = revision + 1 WHERE expires_at <= ? AND status IN ('candidate','active','paused')").run(now);
|
|
186
|
+
db.prepare("UPDATE personal_attention_threads SET status = 'active', pause_until = NULL, revision = revision + 1 WHERE status = 'paused' AND pause_until <= ? AND expires_at > ?").run(now, now);
|
|
187
|
+
db.prepare(`UPDATE personal_attention_threads SET revision = revision + 1, next_check_at = ?
|
|
188
|
+
WHERE status = 'active' AND next_check_at <= ? AND EXISTS
|
|
189
|
+
(SELECT 1 FROM personal_wakeups w WHERE w.thread_id = personal_attention_threads.id
|
|
190
|
+
AND w.thread_revision = personal_attention_threads.revision AND w.expires_at <= ?
|
|
191
|
+
AND w.status IN ('pending','evaluating','cancelled','dead_letter'))`).run(now, now, now);
|
|
192
|
+
const threads = db.prepare("SELECT * FROM personal_attention_threads WHERE status = 'active' AND next_check_at <= ? ORDER BY next_check_at LIMIT 30").all(now);
|
|
193
|
+
for (const thread of threads) enqueueWakeup(thread, thread.authority === "user_explicit" ? "follow_up" : "discussion", `due:${thread.revision}:${thread.next_check_at}`, now);
|
|
194
|
+
db.prepare("UPDATE personal_wakeups SET status = 'cancelled' WHERE expires_at <= ? AND status IN ('pending','evaluating')").run(now);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function getOutreach(id, ownerId) {
|
|
198
|
+
return getSqliteDatabase().prepare(`SELECT o.* FROM personal_outreach o JOIN personal_attention_threads t ON t.id = o.thread_id
|
|
199
|
+
WHERE o.id = ? AND (? IS NULL OR t.owner_id = ?)`).get(id, ownerId ?? null, ownerId ?? null);
|
|
200
|
+
}
|
|
201
|
+
function provenanceDetail(ownerId, id) {
|
|
202
|
+
const outreach = getOutreach(id, ownerId);
|
|
203
|
+
if (!outreach || outreach.state !== "published") return void 0;
|
|
204
|
+
const thread = getAttention(outreach.thread_id, ownerId);
|
|
205
|
+
const snapshot = JSON.parse(outreach.provenance_json);
|
|
206
|
+
const currentSources = getAttentionSources(thread.id, snapshot.sourceIds);
|
|
207
|
+
const sources = sourceFingerprint(currentSources) === snapshot.fingerprint ? currentSources : [];
|
|
208
|
+
return {
|
|
209
|
+
provenance: snapshot.display,
|
|
210
|
+
thread: {
|
|
211
|
+
id: thread.id,
|
|
212
|
+
subject: sources.length ? thread.subject : "",
|
|
213
|
+
status: thread.status,
|
|
214
|
+
revision: thread.revision,
|
|
215
|
+
nextCheckAt: thread.next_check_at
|
|
216
|
+
},
|
|
217
|
+
whyNow: sources.length ? outreach.reason : "",
|
|
218
|
+
sourcesAvailable: sources.length > 0,
|
|
219
|
+
sources: sources.map(({ processingPolicy: _policy, version: _version, ...source }) => source),
|
|
220
|
+
strategy: strategyState(thread.id)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function recordFeedback(ownerId, id, raw, now = Date.now()) {
|
|
224
|
+
const input = PersonalFeedbackSchema.parse(raw);
|
|
225
|
+
runSqliteWriteTransaction((db) => {
|
|
226
|
+
const outreach = getOutreach(id, ownerId);
|
|
227
|
+
if (!outreach || outreach.state !== "published") throw new Error("Message is unavailable");
|
|
228
|
+
const previous = db.prepare("SELECT outreach_id, payload_json FROM personal_feedback WHERE owner_id = ? AND dedupe_key = ?").get(ownerId, input.idempotencyKey);
|
|
229
|
+
if (previous) {
|
|
230
|
+
if (previous.outreach_id !== id || previous.payload_json !== JSON.stringify(input)) throw new Error("Feedback key was already used");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (input.until && (input.until <= now || input.until > now + 30 * 864e5)) throw new Error("Invalid defer time");
|
|
234
|
+
const feedbackId = randomUUID();
|
|
235
|
+
db.prepare("INSERT INTO personal_feedback(id,outreach_id,thread_id,owner_id,kind,scope,payload_json,created_at,dedupe_key) VALUES (?,?,?,?,?,?,?,?,?)").run(feedbackId, id, outreach.thread_id, ownerId, input.kind, input.scope, JSON.stringify(input), now, input.idempotencyKey);
|
|
236
|
+
const thread = getAttention(outreach.thread_id, ownerId);
|
|
237
|
+
if ([
|
|
238
|
+
"stop",
|
|
239
|
+
"defer",
|
|
240
|
+
"adjust"
|
|
241
|
+
].includes(input.kind) && input.scope === "thread") applyThreadFeedback(ownerId, thread.id, thread.revision, input, { feedbackId }, now);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function interestSources(threadId) {
|
|
245
|
+
return getAttentionSources(threadId).map((source) => ({
|
|
246
|
+
entryId: source.entryId,
|
|
247
|
+
excerpt: source.excerpt,
|
|
248
|
+
observedAt: Number(getSqliteDatabase().prepare("SELECT created_at FROM transcript_entries WHERE entry_id = ?").get(source.entryId).created_at)
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
function interestSupport(sources, timezone, now) {
|
|
252
|
+
const independent = /* @__PURE__ */ new Map();
|
|
253
|
+
for (const source of sources) {
|
|
254
|
+
if (source.observedAt < now - 30 * 864e5 || source.observedAt > now) continue;
|
|
255
|
+
const normalized = source.excerpt.replace(/^\[[^\]]+\]\s*/u, "").slice(0, 500).trim().replace(/\s+/gu, " ").toLocaleLowerCase();
|
|
256
|
+
if (normalized && (!independent.has(normalized) || independent.get(normalized).observedAt < source.observedAt)) independent.set(normalized, source);
|
|
257
|
+
}
|
|
258
|
+
const independentStatements = independent.size;
|
|
259
|
+
const independentDays = new Set([...independent.values()].map((source) => localDay(source.observedAt, timezone))).size;
|
|
260
|
+
return {
|
|
261
|
+
independentStatements,
|
|
262
|
+
independentDays,
|
|
263
|
+
confidence: independentStatements === 0 ? 0 : Math.min(.85, Math.round((.25 + independentStatements * .08 + independentDays * .04) * 100) / 100)
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function listInterestCandidates(conversationId, now = Date.now()) {
|
|
267
|
+
const personal = getPersonalAgentByConversation(conversationId);
|
|
268
|
+
if (!personal) return [];
|
|
269
|
+
const timezone = getProactivitySettings(personal.ownerId).timezone;
|
|
270
|
+
return listAttention(conversationId).filter((thread) => thread.kind === "interest").map((thread) => {
|
|
271
|
+
const sources = interestSources(thread.id);
|
|
272
|
+
return {
|
|
273
|
+
id: thread.id,
|
|
274
|
+
subject: thread.subject,
|
|
275
|
+
summary: thread.summary,
|
|
276
|
+
status: thread.expires_at <= now && thread.status === "candidate" ? "expired" : thread.status,
|
|
277
|
+
...interestSupport(sources, timezone, now),
|
|
278
|
+
lastSupportedAt: thread.last_meaningful_at,
|
|
279
|
+
expiresAt: thread.expires_at,
|
|
280
|
+
sources: sources.map((source) => ({
|
|
281
|
+
...source,
|
|
282
|
+
excerpt: source.excerpt.slice(0, 600)
|
|
283
|
+
}))
|
|
284
|
+
};
|
|
285
|
+
}).filter((candidate) => candidate.sources.length > 0);
|
|
286
|
+
}
|
|
287
|
+
function latestStrategy(threadId) {
|
|
288
|
+
return getSqliteDatabase().prepare("SELECT * FROM personal_strategy_versions WHERE thread_id = ? ORDER BY revision DESC LIMIT 1").get(threadId);
|
|
289
|
+
}
|
|
290
|
+
function preparationStrategy(threadId, now = Date.now()) {
|
|
291
|
+
const latest = latestStrategy(threadId);
|
|
292
|
+
if (latest) {
|
|
293
|
+
const preparation = JSON.parse(latest.after_json).preparation;
|
|
294
|
+
return preparation && preparation.reviewAt > now ? preparation.value : null;
|
|
295
|
+
}
|
|
296
|
+
return getSqliteDatabase().prepare("SELECT value FROM personal_adaptive_strategies WHERE thread_id = ? AND review_at > ? ORDER BY created_at DESC, rowid DESC LIMIT 1").get(threadId, now)?.value ?? null;
|
|
297
|
+
}
|
|
298
|
+
function strategyState(threadId, now = Date.now()) {
|
|
299
|
+
const versions = getSqliteDatabase().prepare("SELECT * FROM personal_strategy_versions WHERE thread_id = ? ORDER BY revision DESC LIMIT 5").all(threadId);
|
|
300
|
+
return {
|
|
301
|
+
preparation: preparationStrategy(threadId, now),
|
|
302
|
+
versions: versions.map((version) => ({
|
|
303
|
+
id: version.id,
|
|
304
|
+
revision: version.revision,
|
|
305
|
+
kind: version.kind,
|
|
306
|
+
createdAt: version.created_at,
|
|
307
|
+
preparation: JSON.parse(version.after_json).preparation?.value ?? null
|
|
308
|
+
}))
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function strategySnapshot(thread, now) {
|
|
312
|
+
const latest = latestStrategy(thread.id);
|
|
313
|
+
const legacy = getSqliteDatabase().prepare("SELECT value,review_at FROM personal_adaptive_strategies WHERE thread_id = ? AND review_at > ? ORDER BY created_at DESC, rowid DESC LIMIT 1").get(thread.id, now);
|
|
314
|
+
const preparation = latest ? JSON.parse(latest.after_json).preparation : legacy ? {
|
|
315
|
+
value: legacy.value,
|
|
316
|
+
reviewAt: Number(legacy.review_at)
|
|
317
|
+
} : null;
|
|
318
|
+
return {
|
|
319
|
+
status: thread.status,
|
|
320
|
+
nextCheckAt: thread.next_check_at,
|
|
321
|
+
pauseUntil: thread.pause_until,
|
|
322
|
+
preparation: preparation && preparation.reviewAt > now ? preparation : null
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function existingStrategyOperation(ownerId, key, input) {
|
|
326
|
+
const existing = getSqliteDatabase().prepare("SELECT * FROM personal_strategy_versions WHERE owner_id = ? AND dedupe_key = ?").get(ownerId, key);
|
|
327
|
+
if (existing && existing.input_json !== input) throw new Error("Strategy key was already used");
|
|
328
|
+
return existing;
|
|
329
|
+
}
|
|
330
|
+
function applyThreadFeedback(ownerId, threadId, revision, raw, source = {}, now = Date.now()) {
|
|
331
|
+
const input = PersonalFeedbackSchema.parse(raw);
|
|
332
|
+
if (input.scope !== "thread" || ![
|
|
333
|
+
"stop",
|
|
334
|
+
"defer",
|
|
335
|
+
"adjust"
|
|
336
|
+
].includes(input.kind)) throw new Error("A lasting strategy requires explicit thread feedback");
|
|
337
|
+
return runSqliteWriteTransaction((db) => {
|
|
338
|
+
const operation = JSON.stringify({
|
|
339
|
+
threadId,
|
|
340
|
+
input
|
|
341
|
+
});
|
|
342
|
+
const key = `feedback:${input.idempotencyKey}`;
|
|
343
|
+
if (existingStrategyOperation(ownerId, key, operation)) return getAttention(threadId, ownerId);
|
|
344
|
+
const thread = getAttention(threadId, ownerId);
|
|
345
|
+
if (!thread || thread.revision !== revision) throw new Error("Attention changed");
|
|
346
|
+
if (input.kind !== "stop" && ["completed", "expired"].includes(thread.status)) throw new Error("Attention has ended");
|
|
347
|
+
if (input.until && (input.until <= now || input.until > now + 30 * 864e5)) throw new Error("Invalid defer time");
|
|
348
|
+
const before = strategySnapshot(thread, now);
|
|
349
|
+
const next = patchAttention(ownerId, threadId, revision, input.kind === "stop" ? { status: "completed" } : input.kind === "defer" ? {
|
|
350
|
+
status: "paused",
|
|
351
|
+
nextCheckAt: input.until
|
|
352
|
+
} : {}, now);
|
|
353
|
+
const after = strategySnapshot(next, now);
|
|
354
|
+
if (input.kind === "adjust") {
|
|
355
|
+
after.preparation = {
|
|
356
|
+
value: input.preparation,
|
|
357
|
+
reviewAt: now + 30 * 864e5
|
|
358
|
+
};
|
|
359
|
+
if (source.feedbackId) db.prepare("INSERT INTO personal_adaptive_strategies(id,thread_id,feedback_id,dimension,value,authority,revision,created_at,review_at) VALUES (?,?,?,?,?,?,?,?,?)").run(randomUUID(), threadId, source.feedbackId, "preparation", input.preparation, "explicit", next.revision, now, after.preparation.reviewAt);
|
|
360
|
+
}
|
|
361
|
+
db.prepare(`INSERT INTO personal_strategy_versions(id,owner_id,thread_id,revision,kind,before_json,after_json,input_json,source_entry_id,feedback_id,dedupe_key,created_at)
|
|
362
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`).run(randomUUID(), ownerId, threadId, next.revision, input.kind, JSON.stringify(before), JSON.stringify(after), operation, source.entryId ?? null, source.feedbackId ?? null, key, now);
|
|
363
|
+
return next;
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
function rollbackStrategy(ownerId, threadId, raw, sourceEntryId, now = Date.now()) {
|
|
367
|
+
const input = PersonalStrategyRollbackSchema.parse(raw);
|
|
368
|
+
return runSqliteWriteTransaction((db) => {
|
|
369
|
+
const operation = JSON.stringify({
|
|
370
|
+
threadId,
|
|
371
|
+
input
|
|
372
|
+
});
|
|
373
|
+
const key = `rollback:${input.idempotencyKey}`;
|
|
374
|
+
if (existingStrategyOperation(ownerId, key, operation)) return getAttention(threadId, ownerId);
|
|
375
|
+
const thread = getAttention(threadId, ownerId);
|
|
376
|
+
const version = latestStrategy(threadId);
|
|
377
|
+
if (!thread || thread.revision !== input.revision || !version || version.id !== input.versionId) throw new Error("Strategy changed; reload before undoing");
|
|
378
|
+
const before = strategySnapshot(thread, now);
|
|
379
|
+
const previous = JSON.parse(version.before_json);
|
|
380
|
+
const applied = JSON.parse(version.after_json);
|
|
381
|
+
const schedulingChanged = previous.status !== applied.status || previous.nextCheckAt !== applied.nextCheckAt || previous.pauseUntil !== applied.pauseUntil;
|
|
382
|
+
if (schedulingChanged && (before.status !== applied.status || before.nextCheckAt !== applied.nextCheckAt || before.pauseUntil !== applied.pauseUntil)) throw new Error("Strategy changed; newer scheduling must be preserved");
|
|
383
|
+
const restored = {
|
|
384
|
+
...schedulingChanged ? previous : before,
|
|
385
|
+
preparation: previous.preparation
|
|
386
|
+
};
|
|
387
|
+
if (thread.expires_at <= now || !getAttentionSources(threadId).length) throw new Error("Attention sources have expired");
|
|
388
|
+
const next = patchAttention(ownerId, threadId, thread.revision, schedulingChanged ? {
|
|
389
|
+
status: restored.status === "paused" && restored.pauseUntil !== null && restored.pauseUntil <= now ? "active" : restored.status,
|
|
390
|
+
nextCheckAt: restored.nextCheckAt === null ? null : Math.max(now + 6e4, restored.nextCheckAt)
|
|
391
|
+
} : {}, now);
|
|
392
|
+
db.prepare(`INSERT INTO personal_strategy_versions(id,owner_id,thread_id,revision,kind,before_json,after_json,input_json,source_entry_id,restored_version_id,dedupe_key,created_at)
|
|
393
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`).run(randomUUID(), ownerId, threadId, next.revision, "rollback", JSON.stringify(before), JSON.stringify({
|
|
394
|
+
...restored,
|
|
395
|
+
status: next.status,
|
|
396
|
+
nextCheckAt: next.next_check_at,
|
|
397
|
+
pauseUntil: next.pause_until
|
|
398
|
+
}), operation, sourceEntryId ?? null, version.id, key, now);
|
|
399
|
+
return next;
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
//#endregion
|
|
403
|
+
export { applyThreadFeedback, enqueueDue, enqueueWakeup, getAttention, getAttentionSources, getOutreach, getProactivitySettings, isQuiet, listAttention, listInterestCandidates, localDay, patchAttention, patchProactivitySettings, preparationStrategy, provenanceDetail, recordFeedback, reserveModelCall, rollbackStrategy, saveAttention, sourceFingerprint, strategyState, userText };
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { PersonalFeedbackSchema, PersonalProactivitySettingsSchema, PersonalStrategyRollbackSchema } from "../../../packages/gateway-contract/src/personal-proactivity.js";
|
|
2
|
+
import { init_src } from "../../../packages/gateway-contract/src/index.js";
|
|
3
|
+
import { resolveUserContextSessionAccess } from "../../user-context/access-policy.js";
|
|
4
|
+
import { getPersonalAgent } from "../repository.js";
|
|
5
|
+
import { getProactivitySettings, listInterestCandidates, patchAttention, patchProactivitySettings, provenanceDetail, recordFeedback, rollbackStrategy, strategyState } from "./repository.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
//#region src/personal-agent/proactivity/routes.ts
|
|
8
|
+
init_src();
|
|
9
|
+
const AttentionPatch = z.object({
|
|
10
|
+
revision: z.number().int().positive(),
|
|
11
|
+
status: z.enum([
|
|
12
|
+
"active",
|
|
13
|
+
"paused",
|
|
14
|
+
"completed"
|
|
15
|
+
]).optional(),
|
|
16
|
+
nextCheckAt: z.number().int().positive().optional()
|
|
17
|
+
}).strict();
|
|
18
|
+
const SettingsPatch = z.object({
|
|
19
|
+
revision: z.number().int().positive(),
|
|
20
|
+
mode: z.enum([
|
|
21
|
+
"off",
|
|
22
|
+
"follow_up",
|
|
23
|
+
"balanced"
|
|
24
|
+
]).optional(),
|
|
25
|
+
timezone: z.string().max(100).optional(),
|
|
26
|
+
quietStart: z.number().int().min(0).max(23).optional(),
|
|
27
|
+
quietEnd: z.number().int().min(0).max(23).optional(),
|
|
28
|
+
dailyMessages: z.number().int().min(0).max(10).optional(),
|
|
29
|
+
dailyModelCalls: z.number().int().min(0).max(100).optional()
|
|
30
|
+
}).strict();
|
|
31
|
+
function registerProactivityRoutes(app, deps, owner) {
|
|
32
|
+
app.get("/api/personal-agent/proactivity", (c) => {
|
|
33
|
+
const ownerId = owner(c);
|
|
34
|
+
return ownerId ? c.json({
|
|
35
|
+
ok: true,
|
|
36
|
+
payload: getProactivitySettings(ownerId)
|
|
37
|
+
}) : c.json({
|
|
38
|
+
ok: false,
|
|
39
|
+
error: "Owner access is required"
|
|
40
|
+
}, 403);
|
|
41
|
+
});
|
|
42
|
+
app.get("/api/personal-agent/interests", (c) => {
|
|
43
|
+
const ownerId = owner(c);
|
|
44
|
+
if (!ownerId) return c.json({
|
|
45
|
+
ok: false,
|
|
46
|
+
error: "Owner access is required"
|
|
47
|
+
}, 403);
|
|
48
|
+
const personal = getPersonalAgent(ownerId);
|
|
49
|
+
if (!personal || !resolveUserContextSessionAccess(deps.service.currentConfig, personal.conversationId).userModel) return c.json({
|
|
50
|
+
ok: false,
|
|
51
|
+
error: "Interest access is unavailable"
|
|
52
|
+
}, 403);
|
|
53
|
+
return c.json({
|
|
54
|
+
ok: true,
|
|
55
|
+
payload: listInterestCandidates(personal.conversationId)
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
app.post("/api/personal-agent/attention/:threadId/strategy/rollback", deps.strictRateLimitMiddleware, async (c) => {
|
|
59
|
+
const ownerId = owner(c);
|
|
60
|
+
if (!ownerId) return c.json({
|
|
61
|
+
ok: false,
|
|
62
|
+
error: "Owner access is required"
|
|
63
|
+
}, 403);
|
|
64
|
+
const input = PersonalStrategyRollbackSchema.safeParse(await c.req.json().catch(() => null));
|
|
65
|
+
if (!input.success) return c.json({
|
|
66
|
+
ok: false,
|
|
67
|
+
error: "Invalid strategy rollback"
|
|
68
|
+
}, 400);
|
|
69
|
+
try {
|
|
70
|
+
const thread = rollbackStrategy(ownerId, c.req.param("threadId"), input.data);
|
|
71
|
+
return c.json({
|
|
72
|
+
ok: true,
|
|
73
|
+
payload: {
|
|
74
|
+
thread,
|
|
75
|
+
strategy: strategyState(thread.id)
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return c.json({
|
|
80
|
+
ok: false,
|
|
81
|
+
error: error instanceof Error ? error.message : "Strategy changed"
|
|
82
|
+
}, 409);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
app.patch("/api/personal-agent/proactivity", deps.strictRateLimitMiddleware, async (c) => {
|
|
86
|
+
const ownerId = owner(c);
|
|
87
|
+
if (!ownerId) return c.json({
|
|
88
|
+
ok: false,
|
|
89
|
+
error: "Owner access is required"
|
|
90
|
+
}, 403);
|
|
91
|
+
const input = SettingsPatch.safeParse(await c.req.json().catch(() => null));
|
|
92
|
+
if (!input.success) return c.json({
|
|
93
|
+
ok: false,
|
|
94
|
+
error: "Invalid proactivity settings"
|
|
95
|
+
}, 400);
|
|
96
|
+
const merged = PersonalProactivitySettingsSchema.safeParse({
|
|
97
|
+
...getProactivitySettings(ownerId),
|
|
98
|
+
...input.data
|
|
99
|
+
});
|
|
100
|
+
if (!merged.success) return c.json({
|
|
101
|
+
ok: false,
|
|
102
|
+
error: "Invalid proactivity settings"
|
|
103
|
+
}, 400);
|
|
104
|
+
try {
|
|
105
|
+
return c.json({
|
|
106
|
+
ok: true,
|
|
107
|
+
payload: patchProactivitySettings(ownerId, merged.data)
|
|
108
|
+
});
|
|
109
|
+
} catch {
|
|
110
|
+
return c.json({
|
|
111
|
+
ok: false,
|
|
112
|
+
error: "Settings changed; reload before saving"
|
|
113
|
+
}, 409);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
app.get("/api/personal-agent/outreach/:outreachId/provenance", (c) => {
|
|
117
|
+
const ownerId = owner(c);
|
|
118
|
+
if (!ownerId) return c.json({
|
|
119
|
+
ok: false,
|
|
120
|
+
error: "Owner access is required"
|
|
121
|
+
}, 403);
|
|
122
|
+
const personal = getPersonalAgent(ownerId);
|
|
123
|
+
if (!personal || !resolveUserContextSessionAccess(deps.service.currentConfig, personal.conversationId).userModel) return c.json({
|
|
124
|
+
ok: false,
|
|
125
|
+
error: "Source access is unavailable"
|
|
126
|
+
}, 403);
|
|
127
|
+
const detail = provenanceDetail(ownerId, c.req.param("outreachId"));
|
|
128
|
+
return detail ? c.json({
|
|
129
|
+
ok: true,
|
|
130
|
+
payload: detail
|
|
131
|
+
}) : c.json({
|
|
132
|
+
ok: false,
|
|
133
|
+
error: "Message is unavailable"
|
|
134
|
+
}, 404);
|
|
135
|
+
});
|
|
136
|
+
app.post("/api/personal-agent/outreach/:outreachId/feedback", deps.strictRateLimitMiddleware, async (c) => {
|
|
137
|
+
const ownerId = owner(c);
|
|
138
|
+
if (!ownerId) return c.json({
|
|
139
|
+
ok: false,
|
|
140
|
+
error: "Owner access is required"
|
|
141
|
+
}, 403);
|
|
142
|
+
const input = PersonalFeedbackSchema.safeParse(await c.req.json().catch(() => null));
|
|
143
|
+
if (!input.success) return c.json({
|
|
144
|
+
ok: false,
|
|
145
|
+
error: "Invalid feedback"
|
|
146
|
+
}, 400);
|
|
147
|
+
try {
|
|
148
|
+
recordFeedback(ownerId, c.req.param("outreachId"), input.data);
|
|
149
|
+
return c.json({ ok: true });
|
|
150
|
+
} catch (error) {
|
|
151
|
+
return c.json({
|
|
152
|
+
ok: false,
|
|
153
|
+
error: error instanceof Error ? error.message : "Feedback failed"
|
|
154
|
+
}, 409);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
app.patch("/api/personal-agent/attention/:threadId", deps.strictRateLimitMiddleware, async (c) => {
|
|
158
|
+
const ownerId = owner(c);
|
|
159
|
+
if (!ownerId) return c.json({
|
|
160
|
+
ok: false,
|
|
161
|
+
error: "Owner access is required"
|
|
162
|
+
}, 403);
|
|
163
|
+
const input = AttentionPatch.safeParse(await c.req.json().catch(() => null));
|
|
164
|
+
if (!input.success || input.data.nextCheckAt && (input.data.nextCheckAt <= Date.now() || input.data.nextCheckAt > Date.now() + 30 * 864e5)) return c.json({
|
|
165
|
+
ok: false,
|
|
166
|
+
error: "Invalid attention update"
|
|
167
|
+
}, 400);
|
|
168
|
+
try {
|
|
169
|
+
return c.json({
|
|
170
|
+
ok: true,
|
|
171
|
+
payload: patchAttention(ownerId, c.req.param("threadId"), input.data.revision, input.data)
|
|
172
|
+
});
|
|
173
|
+
} catch (error) {
|
|
174
|
+
return c.json({
|
|
175
|
+
ok: false,
|
|
176
|
+
error: error instanceof Error ? error.message : "Attention changed"
|
|
177
|
+
}, 409);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
export { registerProactivityRoutes };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Config } from '../../config/schema.js';
|
|
2
|
+
import type { AutomationEventEnvelope } from '../../automations/domain/types.js';
|
|
3
|
+
export interface ProactivityDeps {
|
|
4
|
+
getConfig: () => Config;
|
|
5
|
+
isAvailable: (conversationId: string) => boolean;
|
|
6
|
+
notify: (conversationId: string, outreachId: string) => void;
|
|
7
|
+
call?: (conversationId: string, operation: string, prompt: string, signal: AbortSignal) => Promise<string>;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
export declare class PersonalProactivityService {
|
|
11
|
+
private readonly deps;
|
|
12
|
+
private running;
|
|
13
|
+
private stopped;
|
|
14
|
+
private controller?;
|
|
15
|
+
private readonly stopWaiters;
|
|
16
|
+
constructor(deps: ProactivityDeps);
|
|
17
|
+
private now;
|
|
18
|
+
private accessible;
|
|
19
|
+
private available;
|
|
20
|
+
private hasDeliveredTaskResult;
|
|
21
|
+
private call;
|
|
22
|
+
stop(): Promise<void>;
|
|
23
|
+
start(): void;
|
|
24
|
+
/** SQL-only admission; the bounded worker runs outside the scheduler transaction. */
|
|
25
|
+
tick(): void;
|
|
26
|
+
/** The durable automation projection retries this admission before acknowledging its event. */
|
|
27
|
+
ingestEvent(event: AutomationEventEnvelope): void;
|
|
28
|
+
drain(): Promise<void>;
|
|
29
|
+
private extract;
|
|
30
|
+
private evaluate;
|
|
31
|
+
private publish;
|
|
32
|
+
}
|