@xopcai/xopc 0.0.268 → 0.0.270

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.
Files changed (240) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-Bhyy6ENz.js → activity-store-D_0FmdRj.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-B9WObxjr.js → agent-avatar-dicebear-IZvEXak_.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CE3ygr3l.js +1 -0
  6. package/dist/gateway/static/root/assets/{agents-BJuTIRPD.js → agents-DzG2qw-c.js} +1 -1
  7. package/dist/gateway/static/root/assets/{api-B2y7ZjZk.js → api-O3TTQ4qN.js} +1 -1
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-Bbn9mDfL.js → app-management-settings-panel-Cqqsbzri.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-Deo5EsIc.js → appearance-settings-DaJyf32s.js} +1 -1
  10. package/dist/gateway/static/root/assets/{apps-page-Cz4d7ErX.js → apps-page-B4lX6yoT.js} +1 -1
  11. package/dist/gateway/static/root/assets/{archive-plugin-CxJTU8MU.js → archive-plugin-kDPnDTJI.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-C71Cukgp.js → attachment-load-CJ3VceKS.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-page-_J7UZ55Q.js → automations-page-JwMtU8XB.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-workspace-BiNBewg0.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-CX8fC8Zt.js → binary-plugins-DzyZXLBX.js} +1 -1
  16. package/dist/gateway/static/root/assets/{block-editor-Ci9fjoQL.js → block-editor-kTt3gYIJ.js} +3 -3
  17. package/dist/gateway/static/root/assets/{browser-automation-inputs-s5I78Iko.js → browser-automation-inputs-BKohPYR4.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-automations-page-rDSx0ozx.js → browser-automations-page-ktQfFPk-.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-settings-page-B9RA6x4x.js → browser-settings-page-Dgdt5uVL.js} +3 -3
  20. package/dist/gateway/static/root/assets/capabilities-settings-panel-BpDW-R9f.js +7 -0
  21. package/dist/gateway/static/root/assets/{channels-settings-CEhVLJNa.js → channels-settings-DU4EQnR0.js} +1 -1
  22. package/dist/gateway/static/root/assets/{channels-status-swr-_OsWr_vS.js → channels-status-swr-DrqzYmDr.js} +1 -1
  23. package/dist/gateway/static/root/assets/{chat-terminal-dock-DMSl8KxS.js → chat-terminal-dock-CIFGGLGa.js} +2 -2
  24. package/dist/gateway/static/root/assets/connectors-page-CfJqy284.js +2 -0
  25. package/dist/gateway/static/root/assets/{cron-expression-BByq6Uh8.js → cron-expression-BNwbnrEb.js} +1 -1
  26. package/dist/gateway/static/root/assets/{date-picker-m6JZCUEW.js → date-picker-DKyirU4K.js} +1 -1
  27. package/dist/gateway/static/root/assets/{dependency-picker-CwCAjbaJ.js → dependency-picker-D3vNIjRI.js} +1 -1
  28. package/dist/gateway/static/root/assets/{desktop-pet-DSmeEREd.js → desktop-pet-fQwRb_3p.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-settings-MfKceMoe.js → desktop-pet-settings-DfOJ-GvT.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-debug-page-CwvWy26h.js → extension-debug-page-Cn53jB5-.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-page-DLrbVBHM.js → extension-page-IDpLCxpC.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-settings-page-BI6C4PM-.js → extension-settings-page-B1dds2bP.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-config-swr-OObCK8jM.js → gateway-config-swr-B7dcmP7Q.js} +1 -1
  34. package/dist/gateway/static/root/assets/gateway-settings-pp9jN0wl.js +1 -0
  35. package/dist/gateway/static/root/assets/{gateway-startup-retry-D_TxKN_A.js → gateway-startup-retry-JGhGMhVZ.js} +1 -1
  36. package/dist/gateway/static/root/assets/{heartbeat-settings-CUl832Tb.js → heartbeat-settings-B8_zTyev.js} +1 -1
  37. package/dist/gateway/static/root/assets/home-page-g1KdAdLb.js +1 -0
  38. package/dist/gateway/static/root/assets/{image-generation-api-fPU9yGsw.js → image-generation-api-BJLX7Qrh.js} +1 -1
  39. package/dist/gateway/static/root/assets/index-1zRt56Zt.css +2 -0
  40. package/dist/gateway/static/root/assets/{index-wKE8Q2FX.js → index-OMnem2nr.js} +47 -47
  41. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-r4DCS19O.js → keyboard-shortcuts-settings-CBvMTFFx.js} +1 -1
  42. package/dist/gateway/static/root/assets/{local-app-workbench-page-qwLuir8A.js → local-app-workbench-page-bj75v2ZE.js} +2 -2
  43. package/dist/gateway/static/root/assets/{local-apps-page-DkfCRLUh.js → local-apps-page-APCMNDvU.js} +1 -1
  44. package/dist/gateway/static/root/assets/{locale-store-DFQX9kTl.js → locale-store-BThkG0W5.js} +1 -1
  45. package/dist/gateway/static/root/assets/logs-page-BX7n39zV.js +2 -0
  46. package/dist/gateway/static/root/assets/{management-settings-Cfx4ATTa.js → management-settings-CM62j8ZX.js} +1 -1
  47. package/dist/gateway/static/root/assets/{markdown-view-D-5jHRWg.js → markdown-view-BwjpJVPs.js} +1 -1
  48. package/dist/gateway/static/root/assets/{media-plugins-DOLjWd3y.js → media-plugins-C10byeaa.js} +1 -1
  49. package/dist/gateway/static/root/assets/{messages-Dj8QuS6e.js → messages-D0V_bgRP.js} +2 -2
  50. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-BGW9joAb.js → mobile-pairing-wizard-BCWfwVx3.js} +1 -1
  51. package/dist/gateway/static/root/assets/note-detail-panel-koOqb0gX.js +5 -0
  52. package/dist/gateway/static/root/assets/notes-page-BH54rY3B.js +8 -0
  53. package/dist/gateway/static/root/assets/{page-tabs-_SGXMT2O.js → page-tabs-qQ36zncF.js} +1 -1
  54. package/dist/gateway/static/root/assets/{preview-open-alternatives-lVetfCWk.js → preview-open-alternatives-BiAJvvCo.js} +1 -1
  55. package/dist/gateway/static/root/assets/proactive-card-CcNBqHoH.js +1 -0
  56. package/dist/gateway/static/root/assets/proactive-page-C3K61jve.js +1 -0
  57. package/dist/gateway/static/root/assets/{product-open-page-CIGLPFm7.js → product-open-page-CunnDP4w.js} +1 -1
  58. package/dist/gateway/static/root/assets/project-detail-page-SL2g8MZY.js +1 -0
  59. package/dist/gateway/static/root/assets/projects-page-BtxazT5J.js +1 -0
  60. package/dist/gateway/static/root/assets/{registry-api-CsZLatKo.js → registry-api-0JSegbNl.js} +1 -1
  61. package/dist/gateway/static/root/assets/remote-access-hub-BSb7b67A.js +1 -0
  62. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BDqVrUEj.js → runtime-tools-settings-panel-bu_mwBTr.js} +1 -1
  63. package/dist/gateway/static/root/assets/{schema-form-8RQgFJJO.js → schema-form-BE0sgZyz.js} +1 -1
  64. package/dist/gateway/static/root/assets/sessions-page-ljcS2KVz.js +1 -0
  65. package/dist/gateway/static/root/assets/settings-advanced-gate-CR9Y9LQm.js +1 -0
  66. package/dist/gateway/static/root/assets/{settings-form-section-j4UjGM-f.js → settings-form-section-CBo517lV.js} +1 -1
  67. package/dist/gateway/static/root/assets/{settings-page-lMi50B_s.js → settings-page-DBPimx93.js} +1 -1
  68. package/dist/gateway/static/root/assets/{setup-status-panel-Dx4Fy3rU.js → setup-status-panel-J3AnooPW.js} +1 -1
  69. package/dist/gateway/static/root/assets/{share-preview-page-aSyKRH5y.js → share-preview-page-DdV75VTe.js} +1 -1
  70. package/dist/gateway/static/root/assets/shares-settings-AAm4Iwcp.js +1 -0
  71. package/dist/gateway/static/root/assets/skill-reload-api-pRCjP5sm.js +1 -0
  72. package/dist/gateway/static/root/assets/{skills-page-C2_CAvxW.js → skills-page-Cn_-nvRo.js} +1 -1
  73. package/dist/gateway/static/root/assets/{skills-page.utils-B00e4Q30.js → skills-page.utils-CfVAc_lu.js} +1 -1
  74. package/dist/gateway/static/root/assets/{system-settings-panel-CulAkrsV.js → system-settings-panel-B-pzalp-.js} +1 -1
  75. package/dist/gateway/static/root/assets/task-detail-page-CmrLy3PO.js +1 -0
  76. package/dist/gateway/static/root/assets/{theme-store-zRT6vaZc.js → theme-store-1wryLgBQ.js} +1 -1
  77. package/dist/gateway/static/root/assets/{tunnel-api-C01xgCWR.js → tunnel-api-Cgpnm2fL.js} +1 -1
  78. package/dist/gateway/static/root/assets/{use-autosave-DfSJ3c2p.js → use-autosave-N_lm4FdD.js} +1 -1
  79. package/dist/gateway/static/root/assets/user-model-page-u3QLDX6L.js +2 -0
  80. package/dist/gateway/static/root/assets/{voice-api-key-field-9v-miN8o.js → voice-api-key-field-DMsyVfri.js} +1 -1
  81. package/dist/gateway/static/root/assets/{voice-config-api-DpJEu2RQ.js → voice-config-api-QWVmiAp3.js} +1 -1
  82. package/dist/gateway/static/root/assets/{work-discovery-overlay-CREYgh-1.js → work-discovery-overlay-BC0vAxp1.js} +1 -1
  83. package/dist/gateway/static/root/assets/workflow-run-setup-panel-Cz9jQIlP.js +3 -0
  84. package/dist/gateway/static/root/assets/workflows-page-DA6OOK6X.js +6 -0
  85. package/dist/gateway/static/root/index.html +10 -10
  86. package/dist/gateway/static/root/notification-sw.js +15 -0
  87. package/dist/package.js +1 -1
  88. package/dist/packages/gateway-contract/src/notifications.js +15 -1
  89. package/dist/packages/gateway-contract/src/proactive.js +120 -0
  90. package/dist/packages/realtime-protocol/src/voice.js +2 -1
  91. package/dist/src/agent/tool-manuals/xopc-use.d.ts +1 -1
  92. package/dist/src/agent/tool-manuals/xopc-use.js +19 -1
  93. package/dist/src/agent/tools/factory.js +1 -0
  94. package/dist/src/agent/tools/xopc-use-tool.d.ts +3 -2
  95. package/dist/src/agent/tools/xopc-use-tool.js +48 -4
  96. package/dist/src/agent/workflow/subagent-runner.d.ts +1 -0
  97. package/dist/src/agent/workflow/subagent-runner.js +2 -2
  98. package/dist/src/browser/automations/schema.d.ts +1 -1
  99. package/dist/src/browser/automations/service.d.ts +1 -1
  100. package/dist/src/cli/bin.js +2 -2
  101. package/dist/src/cli/commands/agent.js +2 -2
  102. package/dist/src/cli/commands/auth.js +1 -1
  103. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  104. package/dist/src/cli/commands/extension-dev.js +1 -1
  105. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  106. package/dist/src/cli/commands/extension.js +1 -1
  107. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  108. package/dist/src/cli/commands/gateway-ssh-tunnel.js +1 -1
  109. package/dist/src/cli/commands/models.js +2 -2
  110. package/dist/src/cli/commands/onboard.js +1 -1
  111. package/dist/src/cli/commands/platform.js +1 -1
  112. package/dist/src/cli/commands/profile.js +1 -1
  113. package/dist/src/cli/commands/tunnel.js +1 -1
  114. package/dist/src/cli/commands/update.js +1 -1
  115. package/dist/src/cli/extension-cli-register.js +1 -1
  116. package/dist/src/config/index.js +1 -1
  117. package/dist/src/connectors/connected-source-normalizers.js +3 -0
  118. package/dist/src/gateway/hono/routes/index.js +0 -2
  119. package/dist/src/gateway/hono/routes/lazy-bundles.js +12 -0
  120. package/dist/src/gateway/hono/routes/proactive-control.d.ts +3 -0
  121. package/dist/src/gateway/hono/routes/proactive-control.js +168 -0
  122. package/dist/src/gateway/hono/routes/proactive.d.ts +1 -1
  123. package/dist/src/gateway/hono/routes/proactive.js +12 -5
  124. package/dist/src/gateway/hono/routes/voice.js +1 -1
  125. package/dist/src/gateway/run-loop.js +1 -1
  126. package/dist/src/gateway/security/gateway-scopes.js +5 -0
  127. package/dist/src/gateway/server.js +1 -1
  128. package/dist/src/gateway/service.d.ts +0 -1
  129. package/dist/src/gateway/service.js +21 -23
  130. package/dist/src/notifications/planner.js +4 -3
  131. package/dist/src/notifications/proactive-channel.d.ts +8 -0
  132. package/dist/src/notifications/proactive-channel.js +57 -0
  133. package/dist/src/notifications/proactive-policy.d.ts +3 -0
  134. package/dist/src/notifications/proactive-policy.js +32 -0
  135. package/dist/src/notifications/service.d.ts +78 -0
  136. package/dist/src/notifications/service.js +71 -13
  137. package/dist/src/notifications/store.d.ts +2 -0
  138. package/dist/src/notifications/store.js +8 -1
  139. package/dist/src/notifications/web-push.d.ts +19 -0
  140. package/dist/src/notifications/web-push.js +182 -0
  141. package/dist/src/proactive/actions/service.js +12 -2
  142. package/dist/src/proactive/execution/agent-executor.d.ts +5 -0
  143. package/dist/src/proactive/execution/agent-executor.js +21 -0
  144. package/dist/src/proactive/execution/authorization.d.ts +5 -0
  145. package/dist/src/proactive/execution/authorization.js +61 -0
  146. package/dist/src/proactive/execution/context.d.ts +4 -0
  147. package/dist/src/proactive/execution/context.js +51 -14
  148. package/dist/src/proactive/execution/insight.d.ts +7 -0
  149. package/dist/src/proactive/execution/insight.js +36 -3
  150. package/dist/src/proactive/execution/repository.d.ts +2 -0
  151. package/dist/src/proactive/execution/repository.js +53 -9
  152. package/dist/src/proactive/execution/types.d.ts +7 -0
  153. package/dist/src/proactive/execution/worker.js +43 -7
  154. package/dist/src/proactive/experience.d.ts +69 -0
  155. package/dist/src/proactive/experience.js +90 -0
  156. package/dist/src/proactive/follow-ups.d.ts +123 -0
  157. package/dist/src/proactive/follow-ups.js +169 -0
  158. package/dist/src/proactive/inbox/cards.d.ts +21 -0
  159. package/dist/src/proactive/inbox/cards.js +266 -0
  160. package/dist/src/proactive/inbox/delivery.d.ts +6 -0
  161. package/dist/src/proactive/inbox/delivery.js +54 -0
  162. package/dist/src/proactive/inbox/digest.d.ts +8 -0
  163. package/dist/src/proactive/inbox/digest.js +112 -0
  164. package/dist/src/proactive/inbox/lifecycle.d.ts +4 -0
  165. package/dist/src/proactive/inbox/lifecycle.js +61 -0
  166. package/dist/src/proactive/inbox/notification-policy.d.ts +1 -0
  167. package/dist/src/proactive/inbox/notification-policy.js +22 -0
  168. package/dist/src/proactive/inbox/repository.d.ts +1 -0
  169. package/dist/src/proactive/inbox/repository.js +75 -12
  170. package/dist/src/proactive/inbox/types.d.ts +11 -1
  171. package/dist/src/proactive/inbox/worker.js +9 -3
  172. package/dist/src/proactive/index.js +2 -2
  173. package/dist/src/proactive/mail-thread.d.ts +13 -0
  174. package/dist/src/proactive/mail-thread.js +54 -0
  175. package/dist/src/proactive/maintenance.js +7 -0
  176. package/dist/src/proactive/metrics.d.ts +10 -0
  177. package/dist/src/proactive/metrics.js +27 -0
  178. package/dist/src/proactive/policy/presence.d.ts +2 -0
  179. package/dist/src/proactive/policy/presence.js +27 -0
  180. package/dist/src/proactive/policy/service.d.ts +51 -0
  181. package/dist/src/proactive/policy/service.js +120 -0
  182. package/dist/src/proactive/routing/matcher.js +3 -2
  183. package/dist/src/proactive/scenarios/control.d.ts +87 -0
  184. package/dist/src/proactive/scenarios/control.js +108 -0
  185. package/dist/src/proactive/scenarios/preview.d.ts +13 -0
  186. package/dist/src/proactive/scenarios/preview.js +81 -0
  187. package/dist/src/proactive/scenarios/prompt-composer.js +4 -2
  188. package/dist/src/proactive/scenarios/repository.js +5 -1
  189. package/dist/src/proactive/service.js +1 -1
  190. package/dist/src/proactive/temporal/schedule.d.ts +3 -0
  191. package/dist/src/proactive/temporal/schedule.js +67 -0
  192. package/dist/src/proactive/temporal/worker.d.ts +1 -0
  193. package/dist/src/proactive/temporal/worker.js +24 -6
  194. package/dist/src/storage/sqlite/migrations/159_proactive_controls.sql +68 -0
  195. package/dist/src/storage/sqlite/migrations/160_proactive_web_push.sql +27 -0
  196. package/dist/src/storage/sqlite/migrations/161_proactive_delivery_and_lifecycle.sql +51 -0
  197. package/dist/src/storage/sqlite/migrations/162_proactive_product.sql +7 -0
  198. package/dist/src/storage/sqlite/migrations/163_proactive_follow_ups.sql +29 -0
  199. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  200. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  201. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  202. package/dist/src/storage/sqlite/schema.js +1 -1
  203. package/dist/src/tasks/home-query-service.js +1 -1
  204. package/dist/src/tasks/index.js +4 -4
  205. package/dist/src/voice/dashscope/streaming-stt-session.js +1 -0
  206. package/dist/src/voice/dashscope/streaming-tts-stream.d.ts +1 -0
  207. package/dist/src/voice/dashscope/streaming-tts-stream.js +8 -0
  208. package/dist/src/voice/realtime/agentBroker.js +1 -1
  209. package/dist/src/voice/realtime/agentEngine.js +1 -1
  210. package/dist/src/voice/realtime/pcm-output-gain.d.ts +2 -0
  211. package/dist/src/voice/realtime/pcm-output-gain.js +16 -0
  212. package/dist/src/voice/realtime/runtime.d.ts +2 -1
  213. package/dist/src/voice/realtime/runtime.js +25 -7
  214. package/dist/src/workflows/domain/run.d.ts +1 -0
  215. package/dist/src/workflows/index.js +2 -2
  216. package/dist/src/workflows/service/workflow-run-service.d.ts +3 -0
  217. package/dist/src/workflows/service/workflow-run-service.js +27 -6
  218. package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -0
  219. package/package.json +7 -5
  220. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-J1YFNG26.js +0 -1
  221. package/dist/gateway/static/root/assets/automations-workspace-Bn8nwUOL.js +0 -6
  222. package/dist/gateway/static/root/assets/capabilities-settings-panel-C1GUih8m.js +0 -7
  223. package/dist/gateway/static/root/assets/connectors-page-IQAQDSmO.js +0 -2
  224. package/dist/gateway/static/root/assets/gateway-settings-B3H7Vfbh.js +0 -1
  225. package/dist/gateway/static/root/assets/home-page-CT4JL2jm.js +0 -1
  226. package/dist/gateway/static/root/assets/index-CRJrO4GI.css +0 -2
  227. package/dist/gateway/static/root/assets/logs-page-DrNaaPWc.js +0 -2
  228. package/dist/gateway/static/root/assets/note-detail-panel-Dbj5OvXE.js +0 -5
  229. package/dist/gateway/static/root/assets/notes-page-5hRB3WXc.js +0 -8
  230. package/dist/gateway/static/root/assets/project-detail-page-CblWLA8k.js +0 -1
  231. package/dist/gateway/static/root/assets/projects-page-CGk-kzqs.js +0 -1
  232. package/dist/gateway/static/root/assets/remote-access-hub-Du8PweyU.js +0 -1
  233. package/dist/gateway/static/root/assets/sessions-page-BFm94dJC.js +0 -1
  234. package/dist/gateway/static/root/assets/settings-advanced-gate-C0H-cToA.js +0 -1
  235. package/dist/gateway/static/root/assets/shares-settings-CqF9s0pl.js +0 -1
  236. package/dist/gateway/static/root/assets/skill-reload-api-BBL_ZUSC.js +0 -1
  237. package/dist/gateway/static/root/assets/task-detail-page-D1FmRFIn.js +0 -1
  238. package/dist/gateway/static/root/assets/user-model-page-BGC_5baJ.js +0 -2
  239. package/dist/gateway/static/root/assets/workflow-run-setup-panel-BfCWdw8F.js +0 -3
  240. package/dist/gateway/static/root/assets/workflows-page-CvmKQkD0.js +0 -6
@@ -0,0 +1,123 @@
1
+ import { ProactiveEventService } from './service.js';
2
+ export declare const COMMUNICATION_SCENARIO = "communication_follow_up";
3
+ export type MailFollowUp = {
4
+ id: string;
5
+ subscription_id: string;
6
+ workspace_id: string;
7
+ source_item_id: string;
8
+ instructions: string;
9
+ due_at: string;
10
+ status: 'watching' | 'paused' | 'completed';
11
+ revision: number;
12
+ last_fingerprint: string | null;
13
+ last_checked_at: string | null;
14
+ session_key: string | null;
15
+ };
16
+ export declare function requireMailFollowUp(workspace: string, id: string): MailFollowUp;
17
+ export declare function authorizedMailThread(follow: MailFollowUp): {
18
+ origin: import("../knowledge/types.js").KnowledgeSourceItem;
19
+ items: import("../knowledge/types.js").KnowledgeSourceItem[];
20
+ threadKey: string;
21
+ fingerprint: string;
22
+ };
23
+ export declare function mailFollowUpView(follow: MailFollowUp): {
24
+ id: string;
25
+ subscriptionId: string;
26
+ instructions: string;
27
+ dueAt: string;
28
+ status: "completed" | "paused" | "watching";
29
+ revision: number;
30
+ lastCheckedAt: string;
31
+ sessionKey: string;
32
+ sourceAvailable: boolean;
33
+ enabled: boolean;
34
+ subject: string;
35
+ latestMessageAt: string;
36
+ latestDirection: string;
37
+ lastSyncedAt: string;
38
+ syncFailed: boolean;
39
+ };
40
+ export declare function listMailFollowUps(workspace: string): {
41
+ id: string;
42
+ subscriptionId: string;
43
+ instructions: string;
44
+ dueAt: string;
45
+ status: "completed" | "paused" | "watching";
46
+ revision: number;
47
+ lastCheckedAt: string;
48
+ sessionKey: string;
49
+ sourceAvailable: boolean;
50
+ enabled: boolean;
51
+ subject: string;
52
+ latestMessageAt: string;
53
+ latestDirection: string;
54
+ lastSyncedAt: string;
55
+ syncFailed: boolean;
56
+ }[];
57
+ export declare function mailFollowUpSources(workspace: string): {
58
+ id: string;
59
+ subject: string;
60
+ sender: string;
61
+ occurredAt: string;
62
+ accountLabel: string;
63
+ }[];
64
+ export declare function startMailFollowUp(workspace: string, value: unknown): {
65
+ id: string;
66
+ subscriptionId: string;
67
+ instructions: string;
68
+ dueAt: string;
69
+ status: "completed" | "paused" | "watching";
70
+ revision: number;
71
+ lastCheckedAt: string;
72
+ sessionKey: string;
73
+ sourceAvailable: boolean;
74
+ enabled: boolean;
75
+ subject: string;
76
+ latestMessageAt: string;
77
+ latestDirection: string;
78
+ lastSyncedAt: string;
79
+ syncFailed: boolean;
80
+ };
81
+ export declare function updateMailFollowUp(workspace: string, id: string, value: unknown): {
82
+ id: string;
83
+ subscriptionId: string;
84
+ instructions: string;
85
+ dueAt: string;
86
+ status: "completed" | "paused" | "watching";
87
+ revision: number;
88
+ lastCheckedAt: string;
89
+ sessionKey: string;
90
+ sourceAvailable: boolean;
91
+ enabled: boolean;
92
+ subject: string;
93
+ latestMessageAt: string;
94
+ latestDirection: string;
95
+ lastSyncedAt: string;
96
+ syncFailed: boolean;
97
+ };
98
+ /** Keep the existing chat and its exact connector confirmation flow as the write boundary. */
99
+ export declare function continueMailFollowUp(workspace: string, id: string, sessionKey: string): {
100
+ followUp: {
101
+ id: string;
102
+ subscriptionId: string;
103
+ instructions: string;
104
+ dueAt: string;
105
+ status: "completed" | "paused" | "watching";
106
+ revision: number;
107
+ lastCheckedAt: string;
108
+ sessionKey: string;
109
+ sourceAvailable: boolean;
110
+ enabled: boolean;
111
+ subject: string;
112
+ latestMessageAt: string;
113
+ latestDirection: string;
114
+ lastSyncedAt: string;
115
+ syncFailed: boolean;
116
+ };
117
+ connectionId: unknown;
118
+ sourceItemId: string;
119
+ threadId: string;
120
+ instruction: string;
121
+ };
122
+ /** Poll only delegated threads; unchanged threads and passed deadlines produce no repeat signals. */
123
+ export declare function scanMailFollowUps(events: ProactiveEventService, now?: Date): number;
@@ -0,0 +1,169 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
+ import { listKnowledgeSourceItems } from "../storage/sqlite/knowledge-repository.js";
3
+ import { getConnectorConnection } from "../storage/sqlite/connector-repository.js";
4
+ import { emailFields, readMailThread } from "./mail-thread.js";
5
+ import { authorizedConnectedSource } from "./execution/authorization.js";
6
+ import { ProactiveConflict, effectiveProactivePolicy } from "./policy/service.js";
7
+ import { listSubscriptions } from "./scenarios/repository.js";
8
+ import { createControlledSubscription } from "./scenarios/control.js";
9
+ import { ProactiveScenarioService } from "./scenarios/service.js";
10
+ import { ProactiveEventService } from "./service.js";
11
+ import { randomUUID } from "node:crypto";
12
+ import { z } from "zod";
13
+ //#region src/proactive/follow-ups.ts
14
+ const COMMUNICATION_SCENARIO = "communication_follow_up";
15
+ const CreateSchema = z.object({
16
+ sourceItemId: z.string().min(1),
17
+ instructions: z.string().trim().min(1).max(12e3),
18
+ dueAt: z.string().datetime()
19
+ }).strict();
20
+ const UpdateSchema = z.object({
21
+ expectedRevision: z.number().int().positive(),
22
+ instructions: z.string().trim().min(1).max(12e3).optional(),
23
+ dueAt: z.string().datetime().optional(),
24
+ status: z.enum([
25
+ "watching",
26
+ "paused",
27
+ "completed"
28
+ ]).optional()
29
+ }).strict();
30
+ function requireMailFollowUp(workspace, id) {
31
+ const row = getSqliteDatabase().prepare("SELECT * FROM proactive_follow_ups WHERE id = ? AND workspace_id = ?").get(id, workspace);
32
+ if (!row) throw new Error("Follow-up not found");
33
+ return row;
34
+ }
35
+ function authorizedMailThread(follow) {
36
+ if (!authorizedConnectedSource(follow.source_item_id, follow.workspace_id, "communication_follow_up")) return null;
37
+ const thread = readMailThread(follow.source_item_id);
38
+ if (!thread || thread.items.some((item) => !authorizedConnectedSource(item.id, follow.workspace_id, "communication_follow_up") || item.metadata.connectionId !== thread.origin.metadata.connectionId)) return null;
39
+ return thread;
40
+ }
41
+ function mailFollowUpView(follow) {
42
+ const thread = authorizedMailThread(follow);
43
+ const latest = thread?.items.at(-1);
44
+ const sync = thread ? getSqliteDatabase().prepare("SELECT status, error, finished_at FROM knowledge_sync_runs WHERE source_instance_id = ? ORDER BY started_at DESC LIMIT 1").get(thread.origin.sourceInstanceId) : void 0;
45
+ return {
46
+ id: follow.id,
47
+ subscriptionId: follow.subscription_id,
48
+ instructions: follow.instructions,
49
+ dueAt: follow.due_at,
50
+ status: follow.status,
51
+ revision: follow.revision,
52
+ lastCheckedAt: follow.last_checked_at,
53
+ sessionKey: follow.session_key,
54
+ sourceAvailable: Boolean(thread),
55
+ enabled: effectiveProactivePolicy(follow.subscription_id).enabled,
56
+ subject: thread ? emailFields(thread.origin.normalizedText).subject ?? "Email follow-up" : null,
57
+ latestMessageAt: latest?.occurredAt ?? null,
58
+ latestDirection: latest ? emailFields(latest.normalizedText).labels?.includes("SENT") ? "sent" : emailFields(latest.normalizedText).labels?.includes("INBOX") ? "received" : "unknown" : null,
59
+ lastSyncedAt: sync?.finished_at ? new Date(sync.finished_at).toISOString() : null,
60
+ syncFailed: Boolean(sync?.error)
61
+ };
62
+ }
63
+ function listMailFollowUps(workspace) {
64
+ return getSqliteDatabase().prepare("SELECT * FROM proactive_follow_ups WHERE workspace_id = ? ORDER BY updated_at DESC").all(workspace).map(mailFollowUpView);
65
+ }
66
+ function mailFollowUpSources(workspace) {
67
+ const seen = /* @__PURE__ */ new Set();
68
+ return listKnowledgeSourceItems({
69
+ itemType: "email",
70
+ limit: 500
71
+ }).flatMap((item) => {
72
+ if (!authorizedConnectedSource(item.id, workspace, "communication_follow_up")) return [];
73
+ const thread = readMailThread(item.id);
74
+ if (!thread || seen.has(thread.threadKey)) return [];
75
+ seen.add(thread.threadKey);
76
+ const fields = emailFields(item.normalizedText);
77
+ const connection = getConnectorConnection(String(item.metadata.connectionId));
78
+ const account = connection?.alias ?? connection?.identity.email ?? connection?.identity.name;
79
+ return [{
80
+ id: item.id,
81
+ subject: fields.subject ?? "Email",
82
+ sender: fields.sender ?? "",
83
+ occurredAt: item.occurredAt,
84
+ accountLabel: typeof account === "string" ? account : "Connected email account"
85
+ }];
86
+ }).slice(0, 100);
87
+ }
88
+ function startMailFollowUp(workspace, value) {
89
+ const input = CreateSchema.parse(value);
90
+ if (Date.parse(input.dueAt) <= Date.now()) throw new Error("Choose a future follow-up time");
91
+ if (!authorizedConnectedSource(input.sourceItemId, workspace, "communication_follow_up")) throw new Error("Authorize this email account for proactive follow-up first");
92
+ const thread = readMailThread(input.sourceItemId);
93
+ if (!thread) throw new Error("This email has no available thread");
94
+ return runSqliteWriteTransaction((db) => {
95
+ if (db.prepare("SELECT 1 FROM proactive_follow_ups WHERE workspace_id = ? AND thread_key = ?").get(workspace, thread.threadKey)) throw new ProactiveConflict("This conversation is already delegated; update the existing follow-up");
96
+ const sub = listSubscriptions("communication_follow_up").find((item) => item.workspaceId === workspace) ?? createControlledSubscription(workspace, {
97
+ scenarioKey: "communication_follow_up",
98
+ scopeKind: "workspace",
99
+ scopeId: workspace
100
+ });
101
+ const id = randomUUID();
102
+ const now = (/* @__PURE__ */ new Date()).toISOString();
103
+ db.prepare(`INSERT INTO proactive_follow_ups(id, subscription_id, workspace_id, source_item_id, thread_key, instructions, due_at, created_at, updated_at)
104
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, sub.id, workspace, input.sourceItemId, thread.threadKey, input.instructions, input.dueAt, now, now);
105
+ scanMailFollowUps(new ProactiveEventService(() => new ProactiveScenarioService().routes()));
106
+ return mailFollowUpView(requireMailFollowUp(workspace, id));
107
+ });
108
+ }
109
+ function updateMailFollowUp(workspace, id, value) {
110
+ const input = UpdateSchema.parse(value);
111
+ return runSqliteWriteTransaction((db) => {
112
+ const row = requireMailFollowUp(workspace, id);
113
+ if (row.revision !== input.expectedRevision) throw new ProactiveConflict("Follow-up changed; refresh before editing");
114
+ if (input.dueAt && Date.parse(input.dueAt) <= Date.now()) throw new Error("Choose a future follow-up time");
115
+ db.prepare(`UPDATE proactive_follow_ups SET instructions = ?, due_at = ?, status = ?, revision = revision + 1,
116
+ last_fingerprint = NULL, updated_at = ? WHERE id = ?`).run(input.instructions ?? row.instructions, input.dueAt ?? row.due_at, input.status ?? row.status, (/* @__PURE__ */ new Date()).toISOString(), id);
117
+ scanMailFollowUps(new ProactiveEventService(() => new ProactiveScenarioService().routes()));
118
+ return mailFollowUpView(requireMailFollowUp(workspace, id));
119
+ });
120
+ }
121
+ /** Keep the existing chat and its exact connector confirmation flow as the write boundary. */
122
+ function continueMailFollowUp(workspace, id, sessionKey) {
123
+ const follow = requireMailFollowUp(workspace, id);
124
+ const thread = authorizedMailThread(follow);
125
+ if (!thread || follow.status !== "watching" || !effectiveProactivePolicy(follow.subscription_id).enabled) throw new Error("Resume and authorize this follow-up before continuing");
126
+ getSqliteDatabase().prepare("UPDATE proactive_follow_ups SET session_key = ? WHERE id = ?").run(sessionKey, id);
127
+ return {
128
+ followUp: mailFollowUpView(requireMailFollowUp(workspace, id)),
129
+ connectionId: thread.origin.metadata.connectionId,
130
+ sourceItemId: thread.origin.id,
131
+ threadId: emailFields(thread.origin.normalizedText).threadId,
132
+ instruction: "Read the current card and latest thread using this exact connection. Review the recipients and complete draft with the user before sending through the connector approval flow. Never infer successful delivery from a draft or approval. Use provider results, then continue watching synchronized replies."
133
+ };
134
+ }
135
+ /** Poll only delegated threads; unchanged threads and passed deadlines produce no repeat signals. */
136
+ function scanMailFollowUps(events, now = /* @__PURE__ */ new Date()) {
137
+ const db = getSqliteDatabase();
138
+ let published = 0;
139
+ for (const follow of db.prepare("SELECT * FROM proactive_follow_ups WHERE status = 'watching'").all()) {
140
+ if (!effectiveProactivePolicy(follow.subscription_id, now).enabled) continue;
141
+ const thread = authorizedMailThread(follow);
142
+ if (!thread) continue;
143
+ const fingerprint = `${follow.revision}:${thread.fingerprint}:${Date.parse(follow.due_at) <= now.getTime() ? "due" : "waiting"}`;
144
+ if (fingerprint !== follow.last_fingerprint) {
145
+ if (events.publish({
146
+ type: "proactive.follow_up.v1",
147
+ schemaVersion: 1,
148
+ source: {
149
+ kind: "connector",
150
+ id: String(thread.origin.metadata.connectionId)
151
+ },
152
+ subject: {
153
+ kind: "mail_follow_up",
154
+ id: follow.id
155
+ },
156
+ actor: { kind: "system" },
157
+ scope: { workspaceId: follow.workspace_id },
158
+ occurredAt: now.toISOString(),
159
+ dedupeKey: `mail-follow-up:${follow.id}:${fingerprint}`,
160
+ sensitivity: "personal",
161
+ payload: { followUpId: follow.id }
162
+ }, now).inserted) published++;
163
+ }
164
+ db.prepare("UPDATE proactive_follow_ups SET last_fingerprint = ?, last_checked_at = ? WHERE id = ?").run(fingerprint, now.toISOString(), follow.id);
165
+ }
166
+ return published;
167
+ }
168
+ //#endregion
169
+ export { COMMUNICATION_SCENARIO, authorizedMailThread, continueMailFollowUp, listMailFollowUps, mailFollowUpSources, mailFollowUpView, requireMailFollowUp, scanMailFollowUps, startMailFollowUp, updateMailFollowUp };
@@ -0,0 +1,21 @@
1
+ import { type ProactiveCard } from '@xopcai/gateway-contract';
2
+ export declare function requireCardScope(id: string, workspaceId: string): void;
3
+ export declare function getCard(id: string, workspaceId: string): ProactiveCard;
4
+ export declare function listCards(workspaceId: string, input?: {
5
+ status?: string;
6
+ before?: string;
7
+ limit?: number;
8
+ }): {
9
+ cards: ProactiveCard[];
10
+ nextCursor: string;
11
+ };
12
+ export declare function cardChanges(workspaceId: string, cursor?: number): {
13
+ items: {
14
+ id: string;
15
+ deleted: boolean;
16
+ card?: ProactiveCard;
17
+ }[];
18
+ nextCursor: number;
19
+ hasMore: boolean;
20
+ };
21
+ export declare function performCardAction(id: string, workspaceId: string, value: unknown): ProactiveCard;
@@ -0,0 +1,266 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
2
+ import { ProactiveCardActionSchema } from "../../../packages/gateway-contract/src/proactive.js";
3
+ import { getKnowledgeSourceItem } from "../../storage/sqlite/knowledge-repository.js";
4
+ import { insightSourcesAuthorized, insightSourcesChanged } from "../execution/authorization.js";
5
+ import { ProactiveConflict, effectiveProactivePolicy, subscriptionSettings } from "../policy/service.js";
6
+ import { updateControlledSubscription } from "../scenarios/control.js";
7
+ import { withdrawCard } from "./lifecycle.js";
8
+ import { executePendingProactiveActions } from "../actions/service.js";
9
+ import { getInboxItem } from "./repository.js";
10
+ import { ProactiveInboxService } from "./service.js";
11
+ //#region src/proactive/inbox/cards.ts
12
+ function requireCardScope(id, workspaceId) {
13
+ if (!getSqliteDatabase().prepare(`SELECT 1 FROM proactive_inbox_items i JOIN proactive_insights x USING(insight_id)
14
+ JOIN proactive_scenario_subscriptions s USING(subscription_id) WHERE i.inbox_item_id = ? AND s.workspace_id = ?`).get(id, workspaceId)) throw new Error("Card not found");
15
+ }
16
+ function getCard(id, workspaceId) {
17
+ requireCardScope(id, workspaceId);
18
+ const item = getInboxItem(id);
19
+ if (!item.withdrawnAt && item.insight.actionStatus !== "completed" && (!item.expiresAt || Date.parse(item.expiresAt) > Date.now()) && insightSourcesChanged(item.insightId)) {
20
+ item.expiresAt = (/* @__PURE__ */ new Date()).toISOString();
21
+ getSqliteDatabase().prepare("UPDATE proactive_inbox_items SET expires_at = ? WHERE inbox_item_id = ?").run(item.expiresAt, id);
22
+ item.revision = getInboxItem(id).revision;
23
+ }
24
+ if (!insightSourcesAuthorized(item.insightId)) {
25
+ withdrawCard(id);
26
+ item.withdrawnAt = (/* @__PURE__ */ new Date()).toISOString();
27
+ }
28
+ if (item.withdrawnAt) return {
29
+ schemaVersion: 1,
30
+ id,
31
+ revision: getInboxItem(id).revision,
32
+ notificationRevision: item.notificationRevision,
33
+ subscriptionId: item.subscriptionId,
34
+ scenarioKey: item.insight.scenarioKey,
35
+ kind: "briefing",
36
+ status: "withdrawn",
37
+ title: "Source unavailable / 来源已撤回",
38
+ summary: "",
39
+ whyNow: "",
40
+ recommendation: "",
41
+ workDone: "",
42
+ evidence: [],
43
+ createdAt: item.createdAt,
44
+ updatedAt: item.withdrawnAt,
45
+ fallbackText: "Source unavailable / 来源已撤回"
46
+ };
47
+ const insight = item.insight;
48
+ const expired = item.expiresAt && Date.parse(item.expiresAt) <= Date.now();
49
+ return {
50
+ schemaVersion: 1,
51
+ id,
52
+ revision: item.revision,
53
+ notificationRevision: item.notificationRevision,
54
+ subscriptionId: item.subscriptionId,
55
+ ...insight.artifact ? { artifact: insight.artifact } : {},
56
+ ...insight.proposedAction && insight.actionStatus === "approval_required" ? { taskDraft: insight.proposedAction.input } : {},
57
+ ...typeof insight.actionResult?.taskId === "string" ? { followUp: getSqliteDatabase().prepare("SELECT task_id AS taskId, title, phase, resolution FROM tasks WHERE task_id = ?").get(insight.actionResult.taskId) } : {},
58
+ ...insight.scenarioKey === "communication_follow_up" ? { communication: getSqliteDatabase().prepare(`SELECT f.id, f.session_key AS sessionKey
59
+ FROM proactive_runs r JOIN proactive_context_snapshots c ON c.snapshot_id = r.context_snapshot_id
60
+ JOIN proactive_follow_ups f ON f.id = json_extract(c.content_json, '$.follow_up.followUpId') WHERE r.run_id = (SELECT run_id FROM proactive_insights WHERE insight_id = ?)`).get(item.insightId) } : {},
61
+ relatedCardIds: item.correlationKey ? getSqliteDatabase().prepare("SELECT inbox_item_id AS id FROM proactive_inbox_items WHERE correlation_key = ? AND inbox_item_id <> ? AND withdrawn_at IS NULL").all(item.correlationKey, id).map((row) => row.id) : [],
62
+ scenarioKey: insight.scenarioKey,
63
+ kind: insight.attentionKind === "receipt" ? "receipt" : insight.decision ? "decision" : insight.scenarioKey === "meeting_preparation" ? "briefing" : insight.scenarioKey.includes("risk") || insight.scenarioKey.includes("failure") ? "risk" : insight.scenarioKey === "blocked_work" ? "reminder" : "recommendation",
64
+ status: expired ? "expired" : item.status,
65
+ title: insight.title,
66
+ summary: insight.summary,
67
+ whyNow: insight.whyNow,
68
+ recommendation: insight.recommendation,
69
+ workDone: insight.workDone,
70
+ evidence: insight.evidenceIds.map((evidenceId) => cardEvidence(evidenceId, workspaceId)),
71
+ ...insight.decision && !["completed", "rejected"].includes(insight.actionStatus ?? "") ? { decision: insight.decision } : {},
72
+ actionStatus: insight.actionStatus,
73
+ actionResult: insight.actionResult,
74
+ actionError: insight.actionError,
75
+ createdAt: item.createdAt,
76
+ updatedAt: item.updatedAt,
77
+ expiresAt: item.expiresAt,
78
+ fallbackText: `${insight.title}\n${insight.summary}\n${insight.whyNow}`
79
+ };
80
+ }
81
+ function listCards(workspaceId, input = {}) {
82
+ const limit = Math.min(100, Math.max(1, input.limit ?? 30));
83
+ const rows = getSqliteDatabase().prepare(`SELECT i.inbox_item_id, i.created_at FROM proactive_inbox_items i
84
+ JOIN proactive_insights x USING(insight_id) JOIN proactive_scenario_subscriptions s USING(subscription_id)
85
+ WHERE i.withdrawn_at IS NULL AND s.workspace_id = ? AND (? IS NULL OR i.status = ?) AND (? IS NULL OR i.created_at || ':' || i.inbox_item_id < ?)
86
+ ORDER BY i.created_at DESC, i.inbox_item_id DESC LIMIT ?`).all(workspaceId, input.status || null, input.status || null, input.before || null, input.before || null, limit + 1);
87
+ const page = rows.slice(0, limit);
88
+ const last = page.at(-1);
89
+ return {
90
+ cards: page.flatMap((row) => {
91
+ try {
92
+ const card = getCard(row.inbox_item_id, workspaceId);
93
+ return card.status === "withdrawn" ? [] : [card];
94
+ } catch {
95
+ return [];
96
+ }
97
+ }),
98
+ nextCursor: rows.length > limit && last ? `${last.created_at}:${last.inbox_item_id}` : null
99
+ };
100
+ }
101
+ function cardChanges(workspaceId, cursor = 0) {
102
+ const rows = getSqliteDatabase().prepare("SELECT sequence, inbox_item_id, deleted FROM proactive_card_changes WHERE sequence > ? AND workspace_id = ? ORDER BY sequence LIMIT 200").all(cursor, workspaceId);
103
+ const items = [];
104
+ for (const row of rows) {
105
+ if (row.deleted) {
106
+ items.push({
107
+ id: row.inbox_item_id,
108
+ deleted: true
109
+ });
110
+ continue;
111
+ }
112
+ try {
113
+ const card = getCard(row.inbox_item_id, workspaceId);
114
+ items.push(card.status === "withdrawn" ? {
115
+ id: row.inbox_item_id,
116
+ deleted: true
117
+ } : {
118
+ id: row.inbox_item_id,
119
+ deleted: false,
120
+ card
121
+ });
122
+ } catch {
123
+ items.push({
124
+ id: row.inbox_item_id,
125
+ deleted: true
126
+ });
127
+ }
128
+ }
129
+ return {
130
+ items,
131
+ nextCursor: rows.at(-1)?.sequence ?? cursor,
132
+ hasMore: rows.length === 200
133
+ };
134
+ }
135
+ function performCardAction(id, workspaceId, value) {
136
+ const input = ProactiveCardActionSchema.parse(value);
137
+ return runSqliteWriteTransaction((db) => {
138
+ requireCardScope(id, workspaceId);
139
+ const request = JSON.stringify({
140
+ id,
141
+ ...input
142
+ });
143
+ const prior = db.prepare("SELECT request_json, response_json FROM proactive_card_actions WHERE idempotency_key = ?").get(input.idempotencyKey);
144
+ if (prior) {
145
+ if (prior.request_json !== request) throw new ProactiveConflict("Idempotency key was already used for a different action");
146
+ return getCard(id, workspaceId);
147
+ }
148
+ const card = getCard(id, workspaceId);
149
+ if (card.revision !== input.expectedRevision) throw new ProactiveConflict("Card changed; refresh before acting");
150
+ if (card.status === "withdrawn" || card.status === "expired" || card.status === "resolved" && ![
151
+ "useful",
152
+ "not_useful",
153
+ "retry"
154
+ ].includes(input.actionId)) throw new ProactiveConflict("Card is no longer actionable");
155
+ applyCardAction(card, workspaceId, input);
156
+ const result = getCard(id, workspaceId);
157
+ db.prepare("INSERT INTO proactive_card_actions(idempotency_key, inbox_item_id, request_json, response_json) VALUES (?, ?, ?, ?)").run(input.idempotencyKey, id, request, JSON.stringify(result));
158
+ return result;
159
+ });
160
+ }
161
+ function applyCardAction(card, workspaceId, input) {
162
+ const service = new ProactiveInboxService();
163
+ if (input.taskDraft && (input.actionId !== "decide" || input.choice !== "approve" || !card.taskDraft)) throw new Error("Task draft requires a pending approval");
164
+ if (input.artifact && input.actionId !== "edit_artifact") throw new Error("Artifact requires an edit action");
165
+ switch (input.actionId) {
166
+ case "edit_artifact":
167
+ if (!input.artifact || !card.artifact || input.artifact.kind !== card.artifact.kind) throw new Error("An existing artifact is required");
168
+ getSqliteDatabase().prepare(`UPDATE proactive_insights SET artifact_json = ?, artifact_edited_at = ?
169
+ WHERE insight_id = (SELECT insight_id FROM proactive_inbox_items WHERE inbox_item_id = ?)`).run(JSON.stringify(input.artifact), (/* @__PURE__ */ new Date()).toISOString(), card.id);
170
+ break;
171
+ case "retry":
172
+ if (!effectiveProactivePolicy(card.subscriptionId).enabled) throw new Error("Subscription paused");
173
+ if (!getSqliteDatabase().prepare("UPDATE proactive_insights SET action_status = 'pending', action_error = NULL, action_updated_at = ? WHERE insight_id = (SELECT insight_id FROM proactive_inbox_items WHERE inbox_item_id = ?) AND action_status = 'failed'").run((/* @__PURE__ */ new Date()).toISOString(), card.id).changes) throw new Error("Only failed actions can be retried");
174
+ executePendingProactiveActions();
175
+ break;
176
+ case "useful":
177
+ case "not_useful":
178
+ service.feedback(card.id, input.actionId);
179
+ break;
180
+ case "read":
181
+ service.transition(card.id, { status: "read" });
182
+ break;
183
+ case "resolve":
184
+ service.transition(card.id, {
185
+ status: "resolved",
186
+ resolution: "dismissed"
187
+ });
188
+ break;
189
+ case "handled":
190
+ service.transition(card.id, {
191
+ status: "resolved",
192
+ resolution: "user_reported_done"
193
+ });
194
+ break;
195
+ case "snooze":
196
+ service.transition(card.id, {
197
+ status: "snoozed",
198
+ snoozedUntil: input.snoozedUntil ?? new Date(Date.now() + 36e5).toISOString()
199
+ });
200
+ break;
201
+ case "decide":
202
+ if (!input.choice) throw new Error("Decision choice required");
203
+ if (!effectiveProactivePolicy(card.subscriptionId).enabled) throw new Error("Subscription paused");
204
+ if (input.taskDraft) {
205
+ const item = getInboxItem(card.id);
206
+ getSqliteDatabase().prepare("UPDATE proactive_insights SET proposed_action_json = ? WHERE insight_id = ?").run(JSON.stringify({
207
+ ...item.insight.proposedAction,
208
+ input: input.taskDraft
209
+ }), item.insightId);
210
+ }
211
+ service.decide(card.id, input.choice);
212
+ break;
213
+ case "pause": {
214
+ const settings = subscriptionSettings(card.subscriptionId);
215
+ updateControlledSubscription(workspaceId, card.subscriptionId, {
216
+ expectedRevision: settings.revision,
217
+ enabled: false
218
+ });
219
+ service.transition(card.id, {
220
+ status: "resolved",
221
+ resolution: input.actionId
222
+ });
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ function cardEvidence(id, workspaceId) {
228
+ const db = getSqliteDatabase();
229
+ const split = id.indexOf(":");
230
+ const kind = id.slice(0, split);
231
+ const objectId = id.slice(split + 1);
232
+ if (kind === "task") {
233
+ const task = db.prepare("SELECT title FROM tasks WHERE task_id = ?").get(objectId);
234
+ if (task) return {
235
+ id,
236
+ label: task.title,
237
+ route: `/tasks/${encodeURIComponent(objectId)}`
238
+ };
239
+ }
240
+ if (kind === "project") {
241
+ const project = db.prepare("SELECT name FROM projects WHERE project_id = ?").get(objectId);
242
+ if (project) return {
243
+ id,
244
+ label: project.name,
245
+ route: `/projects/${encodeURIComponent(objectId)}`
246
+ };
247
+ }
248
+ if (kind === "source-item") {
249
+ const source = getKnowledgeSourceItem(objectId);
250
+ if (source && !source.deletedAt && source.metadata.workspaceId === workspaceId) {
251
+ const excerpt = source.normalizedText?.slice(0, 600) ?? "";
252
+ return {
253
+ id,
254
+ label: typeof source.metadata.title === "string" ? source.metadata.title.slice(0, 120) : excerpt.split("\n")[0]?.slice(0, 120) || source.itemType,
255
+ excerpt
256
+ };
257
+ }
258
+ }
259
+ const event = db.prepare("SELECT type, occurred_at FROM proactive_events WHERE event_id = ? AND workspace_id = ?").get(id, workspaceId);
260
+ return {
261
+ id,
262
+ label: event ? `${event.type} · ${event.occurred_at}` : kind || "Evidence"
263
+ };
264
+ }
265
+ //#endregion
266
+ export { cardChanges, getCard, listCards, performCardAction, requireCardScope };
@@ -0,0 +1,6 @@
1
+ import type { NotificationService } from '../../notifications/service.js';
2
+ import type { InboxItem } from './types.js';
3
+ /** The outbox is acknowledged only after a notification or suppression is persisted. */
4
+ export declare function deliverProactiveCard(item: InboxItem, notifications: NotificationService, publish: (type: string, payload: unknown) => void): void | {
5
+ retryAt: string;
6
+ };
@@ -0,0 +1,54 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
2
+ import { insightSourcesAuthorized, insightSourcesChanged } from "../execution/authorization.js";
3
+ import { effectiveProactivePolicy, localProactiveDay, nextDigestTime, quietHoursEnd, reserveProactiveNotification } from "../policy/service.js";
4
+ import { insightCorrelation } from "./lifecycle.js";
5
+ import { getInboxItem } from "./repository.js";
6
+ import { viewingProactiveCards } from "../policy/presence.js";
7
+ import { queueDigest } from "./digest.js";
8
+ import { notificationPlanFromGatewayEvent } from "../../notifications/planner.js";
9
+ import { createHash } from "node:crypto";
10
+ //#region src/proactive/inbox/delivery.ts
11
+ /** The outbox is acknowledged only after a notification or suppression is persisted. */
12
+ function deliverProactiveCard(item, notifications, publish) {
13
+ const result = runSqliteWriteTransaction(() => {
14
+ const current = getInboxItem(item.id);
15
+ if (!current || current.withdrawnAt || !insightSourcesAuthorized(current.insightId) || current.status === "resolved" || current.expiresAt && Date.parse(current.expiresAt) <= Date.now()) return null;
16
+ if (current.insight.actionStatus !== "completed" && insightSourcesChanged(current.insightId)) return null;
17
+ if (current.status === "snoozed") return { retryAt: current.snoozedUntil };
18
+ const policy = effectiveProactivePolicy(current.subscriptionId);
19
+ if (!policy.enabled || policy.settings.delivery === "inbox") return null;
20
+ if ((policy.preferences.digestEnabled || policy.settings.delivery === "digest") && current.insight.attentionKind !== "receipt") {
21
+ queueDigest(current, "daily", nextDigestTime(policy.preferences, /* @__PURE__ */ new Date()));
22
+ return null;
23
+ }
24
+ if (!notificationPlanFromGatewayEvent("proactive.inbox.created", current)) return null;
25
+ if (policy.level === "quiet") return null;
26
+ if (policy.level !== "active" && ["low", "medium"].includes(current.insight.urgency) && !current.insight.decision) return null;
27
+ const quietUntil = quietHoursEnd(policy.preferences, /* @__PURE__ */ new Date());
28
+ if (quietUntil) {
29
+ queueDigest(current, "quiet", quietUntil);
30
+ return null;
31
+ }
32
+ const db = getSqliteDatabase();
33
+ const key = `proactive:${insightCorrelation(current.insightId)}:${createHash("sha256").update(JSON.stringify([current.insight.urgency, current.insight.decision ?? null])).digest("hex")}:${localProactiveDay(/* @__PURE__ */ new Date(), policy.preferences.timezone)}`;
34
+ if (db.prepare("SELECT 1 FROM proactive_delivery_decisions WHERE notification_key = ?").get(key)) return null;
35
+ if (policy.preferences.suppressWhileViewing && viewingProactiveCards(policy.workspaceId)) {
36
+ db.prepare("INSERT INTO proactive_delivery_decisions VALUES (?, ?, ?, ?)").run(key, policy.workspaceId, "visible_in_app", (/* @__PURE__ */ new Date()).toISOString());
37
+ return null;
38
+ }
39
+ const decision = reserveProactiveNotification(current.subscriptionId, key);
40
+ if (decision instanceof Date) return { retryAt: decision.toISOString() };
41
+ if (decision === "suppressed") return null;
42
+ const result = notifications.persistGatewayEvent("proactive.inbox.created", current);
43
+ db.prepare("INSERT INTO proactive_delivery_decisions VALUES (?, ?, ?, ?)").run(key, policy.workspaceId, "immediate", (/* @__PURE__ */ new Date()).toISOString());
44
+ return result;
45
+ });
46
+ publish("proactive.card.changed", {
47
+ cardId: item.id,
48
+ revision: item.revision
49
+ });
50
+ if (result && "retryAt" in result) return result;
51
+ if (result) publish("notification.created", result);
52
+ }
53
+ //#endregion
54
+ export { deliverProactiveCard };
@@ -0,0 +1,8 @@
1
+ import type { ProductNotification } from '@xopcai/gateway-contract';
2
+ import type { NotificationPlan } from '../../notifications/planner.js';
3
+ import type { InboxItem } from './types.js';
4
+ export { nextDigestTime } from '../policy/service.js';
5
+ export declare function queueDigest(item: InboxItem, mode: 'daily' | 'quiet', dueAt: Date): void;
6
+ export declare function digestCards(id: string, workspaceId: string): import("@xopcai/gateway-contract").ProactiveCard[];
7
+ export declare function reserveWorkspaceAttention(workspaceId: string, key: string, now?: Date): boolean;
8
+ export declare function flushDueDigests(persist: (plan: NotificationPlan) => ProductNotification | null, now?: Date): ProductNotification[];