@xopcai/xopc 0.0.227 → 0.0.228

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 (200) 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-BevwE16n.js → activity-store-M0nAY5XB.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BggVQrEZ.js → agent-avatar-dicebear-CjGZBs4C.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-B0NI4_NI.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-Bx_BpIt8.js → api-BywYxpaS.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-BtKP9n2r.js → app-management-settings-panel--SjycrXk.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-BoH2NAUX.js → appearance-settings-8G-TVUtg.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-2NiqD-YP.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-DnceUyxm.js → archive-plugin-7_DXaMTP.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-Bh8_9gkr.js → attachment-load-Bx44zSpz.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-page-ZKeAAVxD.js → automations-page-CrKAHqc0.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-workspace-Q3g9cf0h.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-Cy9er6w-.js → binary-plugins-DjDwnHAQ.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-Cc0wWvNX.js → block-editor-7aMOKoZM.js} +3 -3
  16. package/dist/gateway/static/root/assets/browser-settings-page-DTwPkmAC.js +8 -0
  17. package/dist/gateway/static/root/assets/{browser-workflow-inputs-BS7rpLLl.js → browser-workflow-inputs-8NC2qDRF.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-workflows-page-BdBVUZXm.js → browser-workflows-page-DVLrWKz_.js} +2 -2
  19. package/dist/gateway/static/root/assets/capabilities-settings-panel-YcP4FbN7.js +7 -0
  20. package/dist/gateway/static/root/assets/{capability-presets-api-D1Lqz0Tt.js → capability-presets-api-o0y9WgAI.js} +1 -1
  21. package/dist/gateway/static/root/assets/capability-presets-settings-panel-AcPjHHia.js +3 -0
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-CGuEl-l2.js → channel-recipient-api-C5ABr73b.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-settings-Ca94Q1D-.js +1 -0
  24. package/dist/gateway/static/root/assets/channels-status-swr-38FsOnR0.js +1 -0
  25. package/dist/gateway/static/root/assets/{chat-terminal-dock-CdX_7aQh.js → chat-terminal-dock-BR-_7KC0.js} +2 -2
  26. package/dist/gateway/static/root/assets/connectors-page-SGxfLTd6.js +2 -0
  27. package/dist/gateway/static/root/assets/{cron-expression-BnSQc4Ek.js → cron-expression-D8-c-Tkw.js} +1 -1
  28. package/dist/gateway/static/root/assets/{date-picker-BbuG7zQ_.js → date-picker-kV-Jyl9J.js} +1 -1
  29. package/dist/gateway/static/root/assets/{dependency-picker-snZeqOmG.js → dependency-picker-CwKcrfGj.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-DJQ8JoR1.js → desktop-pet-D9rnchu3.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-settings-B9txsRtv.js → desktop-pet-settings-CTs5WGzk.js} +1 -1
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-D7WKWkNB.js → directory-picker-path-field-CCl0aaDm.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-D_7cOCj2.js → extension-debug-page-Cau40SvC.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-Bwu47iUb.js → extension-page-BsJXdvIL.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-Dwg4dSZM.js → extension-settings-page-qWqJyF7-.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-api-jURUSte_.js → gateway-config-api-BI0SELXm.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-swr-6SQXF1yR.js → gateway-config-swr-BWGjnmDl.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-settings-UklJOMLX.js → gateway-settings-6ZdOIC7F.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-DqWaxyvZ.js → gateway-startup-retry-B_zrtccP.js} +1 -1
  40. package/dist/gateway/static/root/assets/{heartbeat-settings-Djs_nduD.js → heartbeat-settings-KBwhXeFD.js} +1 -1
  41. package/dist/gateway/static/root/assets/home-page-BA2M4CLi.js +1 -0
  42. package/dist/gateway/static/root/assets/{image-generation-api-Bxmt6LEr.js → image-generation-api-YKP2me5Z.js} +1 -1
  43. package/dist/gateway/static/root/assets/index-BX0dz1JK.js +70 -0
  44. package/dist/gateway/static/root/assets/index-D_hrEKXM.css +2 -0
  45. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-ByVsCH3d.js → keyboard-shortcuts-settings-kHtpnyEm.js} +1 -1
  46. package/dist/gateway/static/root/assets/local-app-workbench-page-D2nkn09R.js +2 -0
  47. package/dist/gateway/static/root/assets/{local-apps-page-D19R-xsz.js → local-apps-page-BCXa0oUa.js} +1 -1
  48. package/dist/gateway/static/root/assets/{locale-store-Ci1Qi0Q-.js → locale-store-CXhjfe4q.js} +1 -1
  49. package/dist/gateway/static/root/assets/logs-page-D3sNst0K.js +2 -0
  50. package/dist/gateway/static/root/assets/{management-settings-G0y7zmfY.js → management-settings-tHnlfOAQ.js} +1 -1
  51. package/dist/gateway/static/root/assets/{markdown-split-B69d2HWt.js → markdown-split-BMLqHHr5.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-DD_PgVue.css → markdown-view-CLzTdVLW.css} +1 -1
  53. package/dist/gateway/static/root/assets/markdown-view-DvM5PWEH.js +201 -0
  54. package/dist/gateway/static/root/assets/{media-plugins-6TWEQgN_.js → media-plugins-DybwqTgv.js} +1 -1
  55. package/dist/gateway/static/root/assets/messages-BMdD0LWk.js +3 -0
  56. package/dist/gateway/static/root/assets/{notes-api-DLsnnZCS.js → notes-api-BFsOGYkd.js} +1 -1
  57. package/dist/gateway/static/root/assets/{notes-page-CkRJJKDm.js → notes-page-C24SfF6r.js} +1 -1
  58. package/dist/gateway/static/root/assets/notes-workbench-CTdZc3ov.js +4 -0
  59. package/dist/gateway/static/root/assets/{product-open-page-HxArIvoN.js → product-open-page-DBfCVlJq.js} +1 -1
  60. package/dist/gateway/static/root/assets/project-detail-page-COHaEpbA.js +1 -0
  61. package/dist/gateway/static/root/assets/projects-page-lMnST7D_.js +1 -0
  62. package/dist/gateway/static/root/assets/{registry-api-lEar5FOV.js → registry-api-BFZrVYfy.js} +1 -1
  63. package/dist/gateway/static/root/assets/remote-access-hub-BIRKBvO_.js +1 -0
  64. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Bf03UNGD.js → runtime-tools-settings-panel-BPEGTip5.js} +1 -1
  65. package/dist/gateway/static/root/assets/{schema-form-CIXMBgx5.js → schema-form-Dxlu60Td.js} +1 -1
  66. package/dist/gateway/static/root/assets/sessions-page-CATO9G3y.js +1 -0
  67. package/dist/gateway/static/root/assets/settings-advanced-gate-Cco_W41x.js +1 -0
  68. package/dist/gateway/static/root/assets/{settings-form-section-DgdB26mE.js → settings-form-section-ChXh3Bwk.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-loading-skeleton-efk5W6lG.js → settings-loading-skeleton-BCAUqIe2.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-BHVf4gEP.js → settings-page-D25W9Okr.js} +1 -1
  71. package/dist/gateway/static/root/assets/{setup-status-panel-WK0dGNMv.js → setup-status-panel-DsN7Z4Fl.js} +1 -1
  72. package/dist/gateway/static/root/assets/{share-preview-page-Cod-w2qm.js → share-preview-page-CRFnUaNb.js} +1 -1
  73. package/dist/gateway/static/root/assets/{shares-settings-CUqQ31gt.js → shares-settings-CAX0eNaP.js} +1 -1
  74. package/dist/gateway/static/root/assets/skill-reload-api-Doy1gghe.js +1 -0
  75. package/dist/gateway/static/root/assets/{skills-page-DwCcipqK.js → skills-page-Cl7jjTK7.js} +1 -1
  76. package/dist/gateway/static/root/assets/{skills-page.utils-3WSbtl3S.js → skills-page.utils-BiCl4nb5.js} +1 -1
  77. package/dist/gateway/static/root/assets/{system-settings-panel-C5VxqC_s.js → system-settings-panel-BuESR7ZT.js} +1 -1
  78. package/dist/gateway/static/root/assets/task-detail-page-BRiy8NL3.js +1 -0
  79. package/dist/gateway/static/root/assets/{theme-store-Dd89g_ki.js → theme-store-DOdCjyuw.js} +1 -1
  80. package/dist/gateway/static/root/assets/{use-autosave-CeV8VJh2.js → use-autosave-Dg1mgJ_l.js} +1 -1
  81. package/dist/gateway/static/root/assets/user-context-page-CqoADNKu.js +1 -0
  82. package/dist/gateway/static/root/assets/{voice-api-key-field-EmJ-8aZu.js → voice-api-key-field-BDNexlVR.js} +1 -1
  83. package/dist/gateway/static/root/assets/{work-discovery-overlay-zsk6E3Mi.js → work-discovery-overlay-BHTfeaWL.js} +1 -1
  84. package/dist/gateway/static/root/assets/workflow-run-setup-panel-Cf_w-NZy.js +3 -0
  85. package/dist/gateway/static/root/assets/workflows-page-j0P-uECz.js +5 -0
  86. package/dist/gateway/static/root/index.html +12 -12
  87. package/dist/gateway/static/root/notification-sw.js +1 -1
  88. package/dist/package.js +1 -1
  89. package/dist/packages/gateway-contract/src/notifications.js +56 -0
  90. package/dist/packages/gateway-contract/src/turn-outcome.js +114 -0
  91. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +1 -0
  92. package/dist/src/agent/lifecycle/timeout-wrapper.js +7 -6
  93. package/dist/src/agent/mcp/mcp-transport-config.d.ts +1 -1
  94. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  95. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +2 -2
  96. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +4 -4
  97. package/dist/src/agent/tools/create-share-tool.js +14 -1
  98. package/dist/src/agent/tools/exec-command.js +3 -3
  99. package/dist/src/agent/tools/execute-code-tool.js +1 -1
  100. package/dist/src/agent/tools/executor.js +2 -2
  101. package/dist/src/agent/tools/image-generate-tool.js +12 -0
  102. package/dist/src/agent/tools/send-media.js +11 -0
  103. package/dist/src/agent/tools/xopc-use-tool.js +1 -1
  104. package/dist/src/cli/command-loaders.js +1 -0
  105. package/dist/src/cli/command-manifest.js +4 -0
  106. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  107. package/dist/src/cli/commands/history.d.ts +1 -0
  108. package/dist/src/cli/commands/history.js +50 -0
  109. package/dist/src/config/agent-profile.js +1 -1
  110. package/dist/src/gateway/agents-admin.js +1 -1
  111. package/dist/src/gateway/chat-stream/protocol.d.ts +3 -2
  112. package/dist/src/gateway/chat-stream/turn-outcome-collector.d.ts +16 -0
  113. package/dist/src/gateway/chat-stream/turn-outcome-collector.js +111 -0
  114. package/dist/src/gateway/hono/lib/static-ui.js +7 -5
  115. package/dist/src/gateway/hono/routes/index.js +2 -0
  116. package/dist/src/gateway/hono/routes/mobile.js +18 -40
  117. package/dist/src/gateway/hono/routes/notifications.d.ts +3 -0
  118. package/dist/src/gateway/hono/routes/notifications.js +45 -0
  119. package/dist/src/gateway/hono/routes/sessions.js +31 -0
  120. package/dist/src/gateway/hono/routes/tasks.js +1 -1
  121. package/dist/src/gateway/service/agent-runner.js +6 -2
  122. package/dist/src/gateway/service/run-gateway-agent.js +53 -1
  123. package/dist/src/gateway/service/sessions-api.d.ts +6 -0
  124. package/dist/src/gateway/service/sessions-api.js +60 -3
  125. package/dist/src/gateway/service.d.ts +2 -2
  126. package/dist/src/gateway/service.js +8 -7
  127. package/dist/src/history/ctx/exporter.d.ts +14 -0
  128. package/dist/src/history/ctx/exporter.js +99 -0
  129. package/dist/src/history/ctx/format.d.ts +25 -0
  130. package/dist/src/history/ctx/format.js +282 -0
  131. package/dist/src/media/session-references.d.ts +6 -0
  132. package/dist/src/media/session-references.js +24 -3
  133. package/dist/src/notifications/device-store.d.ts +16 -0
  134. package/dist/src/notifications/device-store.js +96 -0
  135. package/dist/src/notifications/planner.d.ts +6 -0
  136. package/dist/src/notifications/planner.js +156 -0
  137. package/dist/src/notifications/service.d.ts +18 -0
  138. package/dist/src/notifications/service.js +184 -0
  139. package/dist/src/notifications/store.d.ts +53 -0
  140. package/dist/src/notifications/store.js +184 -0
  141. package/dist/src/notifications/types.d.ts +29 -0
  142. package/dist/src/notifications/types.js +14 -0
  143. package/dist/src/session/client-history.d.ts +3 -1
  144. package/dist/src/session/client-history.js +13 -0
  145. package/dist/src/session/index.d.ts +1 -1
  146. package/dist/src/session/manager.d.ts +1 -0
  147. package/dist/src/session/manager.js +6 -0
  148. package/dist/src/session/store.d.ts +15 -0
  149. package/dist/src/session/store.js +58 -0
  150. package/dist/src/storage/sqlite/index.d.ts +1 -1
  151. package/dist/src/storage/sqlite/index.js +2 -2
  152. package/dist/src/storage/sqlite/migrations/136_product_notifications.sql +79 -0
  153. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  154. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  155. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  156. package/dist/src/storage/sqlite/schema.js +1 -1
  157. package/dist/src/storage/sqlite/session-input-repository.d.ts +5 -0
  158. package/dist/src/storage/sqlite/session-input-repository.js +9 -1
  159. package/dist/src/tasks/index.js +2 -2
  160. package/dist/src/tasks/task-application-service.js +42 -6
  161. package/dist/src/tasks/task-change-events.d.ts +9 -0
  162. package/dist/src/tasks/task-change-events.js +17 -1
  163. package/dist/src/tasks/task-outbox-dispatcher.js +5 -1
  164. package/dist/src/tasks/task-run-dispatcher.js +4 -2
  165. package/dist/src/tasks/task-run-repository.js +13 -0
  166. package/package.json +2 -2
  167. package/dist/gateway/static/root/assets/agents-TJpjRHnr.js +0 -68
  168. package/dist/gateway/static/root/assets/apps-page-CZ4Da51l.js +0 -1
  169. package/dist/gateway/static/root/assets/automations-workspace-2Y00PvJU.js +0 -6
  170. package/dist/gateway/static/root/assets/browser-settings-page-CbJD27_j.js +0 -8
  171. package/dist/gateway/static/root/assets/capabilities-settings-panel-Dv8y6OuP.js +0 -7
  172. package/dist/gateway/static/root/assets/capability-presets-settings-panel-TZmjzWzt.js +0 -3
  173. package/dist/gateway/static/root/assets/channels-settings-g-68dutA.js +0 -1
  174. package/dist/gateway/static/root/assets/channels-status-swr-D7KsAeWh.js +0 -1
  175. package/dist/gateway/static/root/assets/connectors-page-DXA0rAvQ.js +0 -2
  176. package/dist/gateway/static/root/assets/home-page-i6bXCHdo.js +0 -1
  177. package/dist/gateway/static/root/assets/index-BBBE89vm.css +0 -2
  178. package/dist/gateway/static/root/assets/index-BsD5wygl.js +0 -74
  179. package/dist/gateway/static/root/assets/local-app-workbench-page-BCKEDMlP.js +0 -2
  180. package/dist/gateway/static/root/assets/logs-page-Cy9YQK7j.js +0 -2
  181. package/dist/gateway/static/root/assets/markdown-view-CBM-v2ph.js +0 -69
  182. package/dist/gateway/static/root/assets/mermaid-render-BbqDc_LQ.js +0 -134
  183. package/dist/gateway/static/root/assets/messages-CtOJwGW3.js +0 -3
  184. package/dist/gateway/static/root/assets/notes-workbench-B10T3hGX.js +0 -4
  185. package/dist/gateway/static/root/assets/project-detail-page-DsqRlgsy.js +0 -1
  186. package/dist/gateway/static/root/assets/projects-page-Bu2PN8OK.js +0 -1
  187. package/dist/gateway/static/root/assets/remote-access-hub-CXACdJ2f.js +0 -1
  188. package/dist/gateway/static/root/assets/sessions-page-CS2knWDT.js +0 -1
  189. package/dist/gateway/static/root/assets/settings-advanced-gate-ByFGk0q_.js +0 -1
  190. package/dist/gateway/static/root/assets/skill-reload-api-DweAPjI8.js +0 -1
  191. package/dist/gateway/static/root/assets/task-detail-page-DEj-BFMP.js +0 -1
  192. package/dist/gateway/static/root/assets/user-context-page-f7yh7aD5.js +0 -1
  193. package/dist/gateway/static/root/assets/workflow-run-setup-panel-CBAYvm3G.js +0 -3
  194. package/dist/gateway/static/root/assets/workflows-page-BN0JmHrG.js +0 -5
  195. package/dist/src/mobile/notification-service.d.ts +0 -9
  196. package/dist/src/mobile/notification-service.js +0 -156
  197. package/dist/src/mobile/notification-store.d.ts +0 -28
  198. package/dist/src/mobile/notification-store.js +0 -132
  199. package/dist/src/mobile/notification-types.d.ts +0 -37
  200. package/dist/src/mobile/notification-types.js +0 -9
@@ -0,0 +1,99 @@
1
+ import { init_paths_state, resolveStateDir } from "../../config/paths-state.js";
2
+ import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
3
+ import { buildCtxHistoryJsonl, buildCtxPluginManifest } from "./format.js";
4
+ import { join, resolve } from "node:path";
5
+ import { mkdir, readFile } from "node:fs/promises";
6
+ //#region src/history/ctx/exporter.ts
7
+ init_paths_state();
8
+ init_write_file_atomic();
9
+ function readSnapshot(db) {
10
+ db.exec("BEGIN");
11
+ try {
12
+ const transcripts = db.prepare(`SELECT
13
+ t.session_id,
14
+ t.status,
15
+ t.created_at,
16
+ t.archived_at,
17
+ t.cwd,
18
+ s.agent_id,
19
+ s.session_type
20
+ FROM transcripts t
21
+ JOIN sessions s ON s.session_key = t.session_key
22
+ WHERE t.status = 'active' OR t.archive_reason IN ('reset', 'stale')
23
+ ORDER BY t.created_at ASC, t.session_id ASC`).all();
24
+ const entries = db.prepare(`SELECT e.entry_id, e.session_id, e.seq, e.payload_json, e.created_at
25
+ FROM transcript_entries e
26
+ JOIN transcripts t ON t.session_id = e.session_id
27
+ JOIN sessions s ON s.session_key = t.session_key
28
+ WHERE t.status = 'active' OR t.archive_reason IN ('reset', 'stale')
29
+ ORDER BY t.created_at ASC, t.session_id ASC, e.seq ASC, e.entry_id ASC`).all();
30
+ db.exec("COMMIT");
31
+ const entriesBySession = /* @__PURE__ */ new Map();
32
+ for (const entry of entries) {
33
+ const bucket = entriesBySession.get(entry.session_id) ?? [];
34
+ bucket.push({
35
+ entryId: entry.entry_id,
36
+ seq: entry.seq,
37
+ createdAt: entry.created_at,
38
+ payloadJson: entry.payload_json
39
+ });
40
+ entriesBySession.set(entry.session_id, bucket);
41
+ }
42
+ return transcripts.map((transcript) => ({
43
+ sessionId: transcript.session_id,
44
+ status: transcript.status,
45
+ createdAt: transcript.created_at,
46
+ archivedAt: transcript.archived_at,
47
+ cwd: transcript.cwd,
48
+ agentId: transcript.agent_id,
49
+ sessionType: transcript.session_type,
50
+ entries: entriesBySession.get(transcript.session_id) ?? []
51
+ }));
52
+ } catch (error) {
53
+ try {
54
+ db.exec("ROLLBACK");
55
+ } catch {}
56
+ throw error;
57
+ }
58
+ }
59
+ async function writeIfChanged(path, contents) {
60
+ let existing = null;
61
+ try {
62
+ existing = await readFile(path, "utf8");
63
+ } catch (error) {
64
+ if (error.code !== "ENOENT") throw error;
65
+ }
66
+ if (existing === contents) return false;
67
+ await writeTextAtomic(path, contents, {
68
+ mode: 384,
69
+ ensureDirMode: 448
70
+ });
71
+ return true;
72
+ }
73
+ function resolveDefaultCtxHistoryExportDir(env = process.env) {
74
+ return join(resolveStateDir(env), "exports", "ctx");
75
+ }
76
+ async function exportCtxHistory(db, options = {}) {
77
+ const outputDir = resolve(options.outputDir ?? resolveDefaultCtxHistoryExportDir(options.env));
78
+ const historyPath = join(outputDir, "history.jsonl");
79
+ const manifestPath = join(outputDir, "ctx-history-plugin.json");
80
+ const sessions = readSnapshot(db);
81
+ const history = buildCtxHistoryJsonl(sessions);
82
+ const manifest = buildCtxPluginManifest();
83
+ await mkdir(outputDir, {
84
+ recursive: true,
85
+ mode: 448
86
+ });
87
+ const historyChanged = await writeIfChanged(historyPath, history.contents);
88
+ const manifestChanged = await writeIfChanged(manifestPath, manifest);
89
+ return {
90
+ outputDir,
91
+ historyPath,
92
+ manifestPath,
93
+ sessionCount: sessions.length,
94
+ eventCount: history.eventCount,
95
+ changed: historyChanged || manifestChanged
96
+ };
97
+ }
98
+ //#endregion
99
+ export { exportCtxHistory, resolveDefaultCtxHistoryExportDir };
@@ -0,0 +1,25 @@
1
+ export declare const CTX_HISTORY_SCHEMA_VERSION = "ctx-history-jsonl-v2";
2
+ export declare const CTX_PROVIDER_KEY = "xopc";
3
+ export declare const CTX_SOURCE_ID = "default";
4
+ export declare const CTX_SOURCE_FORMAT = "xopc-session-history-v1";
5
+ export interface XopcHistorySession {
6
+ sessionId: string;
7
+ status: string;
8
+ createdAt: number;
9
+ archivedAt: number | null;
10
+ cwd: string;
11
+ agentId: string;
12
+ sessionType: string | null;
13
+ entries: XopcHistoryEntry[];
14
+ }
15
+ export interface XopcHistoryEntry {
16
+ entryId: string;
17
+ seq: number;
18
+ createdAt: number;
19
+ payloadJson: string;
20
+ }
21
+ export declare function buildCtxHistoryJsonl(sessions: XopcHistorySession[]): {
22
+ contents: string;
23
+ eventCount: number;
24
+ };
25
+ export declare function buildCtxPluginManifest(): string;
@@ -0,0 +1,282 @@
1
+ import { createHash } from "node:crypto";
2
+ //#region src/history/ctx/format.ts
3
+ const CTX_HISTORY_SCHEMA_VERSION = "ctx-history-jsonl-v2";
4
+ const CTX_PROVIDER_KEY = "xopc";
5
+ const CTX_SOURCE_ID = "default";
6
+ const CTX_SOURCE_FORMAT = "xopc-session-history-v1";
7
+ const EVENT_INDEX_STRIDE = 65536;
8
+ const MAX_TEXT_CHUNK_BYTES = 1024 * 1024;
9
+ const MAX_JSONL_LINE_BYTES = 16 * 1024 * 1024;
10
+ function asRecord(value) {
11
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
12
+ }
13
+ function stringValue(value) {
14
+ return typeof value === "string" ? value : void 0;
15
+ }
16
+ function timestamp(value, label) {
17
+ const date = new Date(value);
18
+ if (!Number.isFinite(value) || Number.isNaN(date.getTime())) throw new Error(`Invalid ${label}: ${String(value)}`);
19
+ return date.toISOString();
20
+ }
21
+ function stringifyValue(value) {
22
+ if (typeof value === "string") return value;
23
+ if (value === void 0) return "";
24
+ return JSON.stringify(value) ?? "";
25
+ }
26
+ function textParts(content) {
27
+ if (typeof content === "string") return content.length > 0 ? [content] : [];
28
+ if (!Array.isArray(content)) return [];
29
+ return content.flatMap((block) => {
30
+ const record = asRecord(block);
31
+ return record?.type === "text" && typeof record.text === "string" && record.text.length > 0 ? [record.text] : [];
32
+ });
33
+ }
34
+ function outputText(output) {
35
+ if (typeof output === "string") return output;
36
+ return textParts(output).join("");
37
+ }
38
+ function toolCallDetails(block) {
39
+ const toolName = stringValue(block.name);
40
+ if (!toolName) return null;
41
+ const toolCallId = stringValue(block.id);
42
+ const argumentsText = stringifyValue(block.arguments);
43
+ return {
44
+ eventType: "tool_call",
45
+ role: "assistant",
46
+ text: argumentsText ? `${toolName} ${argumentsText}` : toolName,
47
+ details: {
48
+ tool_name: toolName,
49
+ ...toolCallId ? { tool_call_id: toolCallId } : {}
50
+ }
51
+ };
52
+ }
53
+ function normalizeAssistant(row) {
54
+ if (typeof row.content === "string") return row.content.length > 0 ? [{
55
+ eventType: "message",
56
+ role: "assistant",
57
+ text: row.content
58
+ }] : [];
59
+ if (!Array.isArray(row.content)) return [];
60
+ const events = [];
61
+ for (const block of row.content) {
62
+ const record = asRecord(block);
63
+ if (!record) continue;
64
+ if (record.type === "text" && typeof record.text === "string" && record.text.length > 0) {
65
+ events.push({
66
+ eventType: "message",
67
+ role: "assistant",
68
+ text: record.text
69
+ });
70
+ continue;
71
+ }
72
+ if (record.type === "toolCall") {
73
+ const event = toolCallDetails(record);
74
+ if (event) events.push(event);
75
+ }
76
+ }
77
+ return events;
78
+ }
79
+ function normalizeToolResult(row) {
80
+ const text = textParts(row.content).join("");
81
+ if (!text) return [];
82
+ const toolName = stringValue(row.toolName);
83
+ const toolCallId = stringValue(row.toolCallId);
84
+ return [{
85
+ eventType: "tool_output",
86
+ role: "tool",
87
+ text,
88
+ details: {
89
+ ...toolName ? { tool_name: toolName } : {},
90
+ ...toolCallId ? { tool_call_id: toolCallId } : {},
91
+ ...typeof row.isError === "boolean" ? { is_error: row.isError } : {}
92
+ }
93
+ }];
94
+ }
95
+ function normalizeBashExecution(row) {
96
+ const command = stringValue(row.command);
97
+ if (!command) return [];
98
+ const events = [{
99
+ eventType: "command_started",
100
+ role: "user",
101
+ text: command
102
+ }];
103
+ const output = outputText(row.output);
104
+ if (output) events.push({
105
+ eventType: "command_output",
106
+ role: "tool",
107
+ text: output
108
+ });
109
+ events.push({
110
+ eventType: "command_finished",
111
+ role: "tool",
112
+ text: row.exitCode == null ? "Command finished" : `Command exited with code ${String(row.exitCode)}`,
113
+ details: {
114
+ ...typeof row.exitCode === "number" ? { exit_code: row.exitCode } : {},
115
+ ...typeof row.signal === "string" ? { signal: row.signal } : {},
116
+ ...typeof row.truncated === "boolean" ? { truncated: row.truncated } : {}
117
+ }
118
+ });
119
+ return events;
120
+ }
121
+ function normalizeStoredRow(row) {
122
+ if (row.kind === "context" || row.role === "system") return [];
123
+ if (row.type === "compaction" && typeof row.summary === "string" && row.summary.length > 0) return [{
124
+ eventType: "summary",
125
+ role: "assistant",
126
+ text: row.summary
127
+ }];
128
+ if ((row.role === "branchSummary" || row.role === "compactionSummary") && typeof row.summary === "string" && row.summary.length > 0) return [{
129
+ eventType: "summary",
130
+ role: "assistant",
131
+ text: row.summary
132
+ }];
133
+ if (row.role === "bashExecution") return normalizeBashExecution(row);
134
+ if (row.role === "custom" || row.type === "custom_message") {
135
+ if (row.display === false) return [];
136
+ return textParts(row.content).map((text) => ({
137
+ eventType: "message",
138
+ role: "user",
139
+ text
140
+ }));
141
+ }
142
+ if (row.role === "user") return textParts(row.content).map((text) => ({
143
+ eventType: "message",
144
+ role: "user",
145
+ text
146
+ }));
147
+ if (row.role === "assistant") return normalizeAssistant(row);
148
+ if (row.role === "toolResult" || row.role === "tool") return normalizeToolResult(row);
149
+ return [];
150
+ }
151
+ function splitTextByUtf8Bytes(text) {
152
+ if (Buffer.byteLength(text, "utf8") <= MAX_TEXT_CHUNK_BYTES) return [text];
153
+ const chunks = [];
154
+ let start = 0;
155
+ while (start < text.length) {
156
+ let low = start + 1;
157
+ let high = text.length;
158
+ let end = low;
159
+ while (low <= high) {
160
+ const middle = Math.floor((low + high) / 2);
161
+ if (Buffer.byteLength(text.slice(start, middle), "utf8") <= MAX_TEXT_CHUNK_BYTES) {
162
+ end = middle;
163
+ low = middle + 1;
164
+ } else high = middle - 1;
165
+ }
166
+ const previous = text.charCodeAt(end - 1);
167
+ const next = text.charCodeAt(end);
168
+ if (previous >= 55296 && previous <= 56319 && next >= 56320 && next <= 57343) end -= 1;
169
+ chunks.push(text.slice(start, end));
170
+ start = end;
171
+ }
172
+ return chunks;
173
+ }
174
+ function stableEventId(sessionId, entryId, eventOffset) {
175
+ return `xopc:${createHash("sha256").update(sessionId).update("\0").update(entryId).update("\0").update(String(eventOffset)).digest("hex")}`;
176
+ }
177
+ function serializeRecord(record) {
178
+ const line = JSON.stringify(record);
179
+ const size = Buffer.byteLength(line, "utf8");
180
+ if (size > MAX_JSONL_LINE_BYTES) throw new Error(`ctx history record exceeds 16 MiB (${size} bytes)`);
181
+ return line;
182
+ }
183
+ function sessionRecord(session) {
184
+ const active = session.status === "active";
185
+ return {
186
+ record_type: "session",
187
+ source_id: CTX_SOURCE_ID,
188
+ provider_session_id: session.sessionId,
189
+ started_at: timestamp(session.createdAt, `session timestamp for ${session.sessionId}`),
190
+ external_agent_id: session.agentId,
191
+ cwd: session.cwd,
192
+ ...session.archivedAt === null ? {} : { ended_at: timestamp(session.archivedAt, `session end timestamp for ${session.sessionId}`) },
193
+ status: active ? "active" : "completed",
194
+ agent_scope: session.sessionType === "workflow-subagent" ? "subagent" : "primary"
195
+ };
196
+ }
197
+ function entryRecords(session, entry) {
198
+ let parsed;
199
+ try {
200
+ parsed = JSON.parse(entry.payloadJson);
201
+ } catch (error) {
202
+ const message = error instanceof Error ? error.message : String(error);
203
+ throw new Error(`Invalid transcript payload for entry ${entry.entryId}: ${message}`);
204
+ }
205
+ const row = asRecord(parsed);
206
+ if (!row) throw new Error(`Transcript payload for entry ${entry.entryId} is not an object`);
207
+ const occurredAt = timestamp(entry.createdAt, `entry timestamp for ${entry.entryId}`);
208
+ const records = [];
209
+ let offset = 0;
210
+ for (const event of normalizeStoredRow(row)) {
211
+ const chunks = splitTextByUtf8Bytes(event.text);
212
+ for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex += 1) {
213
+ if (offset >= EVENT_INDEX_STRIDE) throw new Error(`Transcript entry ${entry.entryId} expands to too many ctx events`);
214
+ const eventIndex = entry.seq * EVENT_INDEX_STRIDE + offset;
215
+ if (!Number.isSafeInteger(eventIndex) || eventIndex < 0) throw new Error(`Transcript sequence is outside the supported range: ${entry.seq}`);
216
+ records.push({
217
+ record_type: "event",
218
+ source_id: CTX_SOURCE_ID,
219
+ provider_session_id: session.sessionId,
220
+ event_index: eventIndex,
221
+ event_id: stableEventId(session.sessionId, entry.entryId, offset),
222
+ occurred_at: occurredAt,
223
+ event_type: event.eventType,
224
+ ...event.role ? { role: event.role } : {},
225
+ payload: {
226
+ text: chunks[chunkIndex],
227
+ ...event.details,
228
+ ...chunks.length > 1 ? {
229
+ chunk_index: chunkIndex,
230
+ chunk_count: chunks.length
231
+ } : {}
232
+ }
233
+ });
234
+ offset += 1;
235
+ }
236
+ }
237
+ return records;
238
+ }
239
+ function buildCtxHistoryJsonl(sessions) {
240
+ const lines = [serializeRecord({
241
+ record_type: "manifest",
242
+ schema_version: CTX_HISTORY_SCHEMA_VERSION,
243
+ producer: "xopc"
244
+ }), serializeRecord({
245
+ record_type: "source",
246
+ source_id: CTX_SOURCE_ID,
247
+ provider_key: CTX_PROVIDER_KEY,
248
+ source_format: CTX_SOURCE_FORMAT
249
+ })];
250
+ let eventCount = 0;
251
+ for (const session of sessions) {
252
+ lines.push(serializeRecord(sessionRecord(session)));
253
+ for (const entry of session.entries) {
254
+ const records = entryRecords(session, entry);
255
+ eventCount += records.length;
256
+ lines.push(...records.map(serializeRecord));
257
+ }
258
+ }
259
+ return {
260
+ contents: `${lines.join("\n")}\n`,
261
+ eventCount
262
+ };
263
+ }
264
+ function buildCtxPluginManifest() {
265
+ return `${JSON.stringify({
266
+ schema_version: 1,
267
+ name: "xopc",
268
+ display_name: "XOPC history",
269
+ version: "1.0.0",
270
+ history_sources: [{
271
+ id: CTX_SOURCE_ID,
272
+ provider_key: CTX_PROVIDER_KEY,
273
+ source_id: CTX_SOURCE_ID,
274
+ source_format: CTX_SOURCE_FORMAT,
275
+ path: "history.jsonl",
276
+ enabled: true,
277
+ refresh: "manual"
278
+ }]
279
+ }, null, 2)}\n`;
280
+ }
281
+ //#endregion
282
+ export { CTX_HISTORY_SCHEMA_VERSION, CTX_PROVIDER_KEY, CTX_SOURCE_FORMAT, CTX_SOURCE_ID, buildCtxHistoryJsonl, buildCtxPluginManifest };
@@ -1,6 +1,12 @@
1
1
  import type { AgentMessage } from '@earendil-works/pi-agent-core';
2
+ export declare function collectMediaUrisFromValues(values: readonly unknown[]): Set<string>;
2
3
  export declare function collectMediaUrisFromMessages(messages: readonly AgentMessage[]): Set<string>;
3
4
  export declare function messagesReferenceMediaUri(messages: readonly AgentMessage[], uri: string): boolean;
5
+ /**
6
+ * A media object remains live while any non-deleted session references it,
7
+ * including archived transcripts retained by a reset of that session key.
8
+ */
9
+ export declare function isMediaUriReferencedByLiveSession(uri: string): boolean;
4
10
  export declare function deleteMediaUris(uris: Iterable<string>): Promise<void>;
5
11
  export declare function deleteMediaUrisNoLongerReferenced(params: {
6
12
  removed: readonly AgentMessage[];
@@ -1,3 +1,5 @@
1
+ import { getSqliteDatabase } from "../storage/sqlite/transaction.js";
2
+ import "../storage/sqlite/index.js";
1
3
  import { tryParseMediaUri } from "./uri.js";
2
4
  import { deleteMediaBuffer } from "./store.js";
3
5
  //#region src/media/session-references.ts
@@ -19,19 +21,38 @@ function collectFromValue(value, out, seen) {
19
21
  const record = value;
20
22
  for (const nested of Object.values(record)) collectFromValue(nested, out, seen);
21
23
  }
22
- function collectMediaUrisFromMessages(messages) {
24
+ function collectMediaUrisFromValues(values) {
23
25
  const out = /* @__PURE__ */ new Set();
24
26
  const seen = /* @__PURE__ */ new WeakSet();
25
- for (const message of messages) collectFromValue(message, out, seen);
27
+ for (const value of values) collectFromValue(value, out, seen);
26
28
  return out;
27
29
  }
30
+ function collectMediaUrisFromMessages(messages) {
31
+ return collectMediaUrisFromValues(messages);
32
+ }
28
33
  function messagesReferenceMediaUri(messages, uri) {
29
34
  return collectMediaUrisFromMessages(messages).has(uri.trim());
30
35
  }
36
+ /**
37
+ * A media object remains live while any non-deleted session references it,
38
+ * including archived transcripts retained by a reset of that session key.
39
+ */
40
+ function isMediaUriReferencedByLiveSession(uri) {
41
+ const normalized = uri.trim();
42
+ if (!normalized) return false;
43
+ const escaped = JSON.stringify(normalized).replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
44
+ return getSqliteDatabase().prepare(`SELECT 1 AS referenced
45
+ FROM transcript_entries e
46
+ JOIN transcripts t ON t.session_id = e.session_id
47
+ JOIN sessions s ON s.session_key = t.session_key
48
+ WHERE e.payload_json LIKE ? ESCAPE '\\'
49
+ LIMIT 1`).get(`%${escaped}%`)?.referenced === 1;
50
+ }
31
51
  async function deleteMediaUris(uris) {
32
52
  for (const uri of new Set(uris)) {
33
53
  const parsed = tryParseMediaUri(uri);
34
54
  if (!parsed) continue;
55
+ if (isMediaUriReferencedByLiveSession(uri)) continue;
35
56
  await deleteMediaBuffer(parsed.id, parsed.bucket).catch(() => {});
36
57
  }
37
58
  }
@@ -43,4 +64,4 @@ async function deleteMediaUrisNoLongerReferenced(params) {
43
64
  await deleteMediaUris(removedUris);
44
65
  }
45
66
  //#endregion
46
- export { collectMediaUrisFromMessages, deleteMediaUris, deleteMediaUrisNoLongerReferenced, messagesReferenceMediaUri };
67
+ export { collectMediaUrisFromMessages, collectMediaUrisFromValues, deleteMediaUris, deleteMediaUrisNoLongerReferenced, isMediaUriReferencedByLiveSession, messagesReferenceMediaUri };
@@ -0,0 +1,16 @@
1
+ import { type NotificationDevice, type NotificationDevicePlatform, type NotificationLanguage, type NotificationPermission, type NotificationPreferences } from './types.js';
2
+ export type RegisterNotificationDeviceInput = {
3
+ id: string;
4
+ platform: NotificationDevicePlatform;
5
+ pushToken: string;
6
+ permissions: NotificationPermission;
7
+ preferences?: Partial<NotificationPreferences>;
8
+ locale: NotificationLanguage;
9
+ appVersion?: string;
10
+ };
11
+ export declare function registerNotificationDevice(input: RegisterNotificationDeviceInput): NotificationDevice;
12
+ export declare function getNotificationDevice(deviceId: string): NotificationDevice | null;
13
+ export declare function listDeliverableNotificationDevices(now?: number): NotificationDevice[];
14
+ export declare function updateNotificationDevicePreferences(deviceId: string, patch: Partial<NotificationPreferences>): NotificationDevice | null;
15
+ export declare function removeNotificationDevice(deviceId: string): boolean;
16
+ export declare function disableNotificationDeviceForPushToken(pushToken: string): void;
@@ -0,0 +1,96 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
+ import { DEFAULT_NOTIFICATION_PREFERENCES } from "./types.js";
3
+ //#region src/notifications/device-store.ts
4
+ const DEVICE_LEASE_MS = 10080 * 60 * 1e3;
5
+ function parsePreferences(raw) {
6
+ try {
7
+ const value = JSON.parse(raw);
8
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
9
+ } catch {
10
+ return {};
11
+ }
12
+ }
13
+ function normalizePreferences(input) {
14
+ return {
15
+ chatCompleted: input?.chatCompleted ?? DEFAULT_NOTIFICATION_PREFERENCES.chatCompleted,
16
+ chatFailed: input?.chatFailed ?? DEFAULT_NOTIFICATION_PREFERENCES.chatFailed,
17
+ taskNeedsInput: input?.taskNeedsInput ?? DEFAULT_NOTIFICATION_PREFERENCES.taskNeedsInput,
18
+ taskBlocked: input?.taskBlocked ?? DEFAULT_NOTIFICATION_PREFERENCES.taskBlocked,
19
+ taskFailed: input?.taskFailed ?? DEFAULT_NOTIFICATION_PREFERENCES.taskFailed,
20
+ taskCompleted: input?.taskCompleted ?? DEFAULT_NOTIFICATION_PREFERENCES.taskCompleted,
21
+ automationCompleted: input?.automationCompleted ?? DEFAULT_NOTIFICATION_PREFERENCES.automationCompleted,
22
+ automationFailed: input?.automationFailed ?? DEFAULT_NOTIFICATION_PREFERENCES.automationFailed,
23
+ proactiveInsight: input?.proactiveInsight ?? DEFAULT_NOTIFICATION_PREFERENCES.proactiveInsight
24
+ };
25
+ }
26
+ function deviceFromRow(row) {
27
+ return {
28
+ id: row.device_id,
29
+ platform: row.platform === "android" ? "android" : "ios",
30
+ pushToken: row.push_token,
31
+ enabled: row.enabled !== 0,
32
+ permissions: row.permissions === "granted" || row.permissions === "denied" ? row.permissions : "unknown",
33
+ preferences: normalizePreferences(parsePreferences(row.preferences_json)),
34
+ locale: row.locale === "zh" ? "zh" : "en",
35
+ appVersion: row.app_version ?? void 0,
36
+ leaseExpiresAt: row.lease_expires_at,
37
+ lastSeenAt: row.last_seen_at,
38
+ createdAt: row.created_at,
39
+ updatedAt: row.updated_at
40
+ };
41
+ }
42
+ function registerNotificationDevice(input) {
43
+ const now = Date.now();
44
+ const preferences = input.preferences ? normalizePreferences(input.preferences) : getNotificationDevice(input.id)?.preferences ?? normalizePreferences();
45
+ runSqliteWriteTransaction((db) => {
46
+ db.prepare(`DELETE FROM notification_devices
47
+ WHERE push_provider = 'expo' AND push_token = ? AND device_id <> ?`).run(input.pushToken, input.id);
48
+ db.prepare(`INSERT INTO notification_devices (
49
+ device_id, platform, push_token, enabled, permissions, preferences_json,
50
+ locale, app_version, lease_expires_at, last_seen_at, created_at, updated_at
51
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
52
+ ON CONFLICT(device_id) DO UPDATE SET
53
+ platform = excluded.platform,
54
+ push_token = excluded.push_token,
55
+ enabled = excluded.enabled,
56
+ permissions = excluded.permissions,
57
+ preferences_json = excluded.preferences_json,
58
+ locale = excluded.locale,
59
+ app_version = excluded.app_version,
60
+ lease_expires_at = excluded.lease_expires_at,
61
+ last_seen_at = excluded.last_seen_at,
62
+ updated_at = excluded.updated_at`).run(input.id, input.platform, input.pushToken, input.permissions === "granted" ? 1 : 0, input.permissions, JSON.stringify(preferences), input.locale, input.appVersion?.trim() || null, now + DEVICE_LEASE_MS, now, now, now);
63
+ });
64
+ return getNotificationDevice(input.id);
65
+ }
66
+ function getNotificationDevice(deviceId) {
67
+ const row = getSqliteDatabase().prepare("SELECT * FROM notification_devices WHERE device_id = ?").get(deviceId);
68
+ return row ? deviceFromRow(row) : null;
69
+ }
70
+ function listDeliverableNotificationDevices(now = Date.now()) {
71
+ return getSqliteDatabase().prepare(`SELECT * FROM notification_devices
72
+ WHERE enabled = 1 AND permissions = 'granted' AND lease_expires_at > ?
73
+ ORDER BY updated_at DESC`).all(now).map(deviceFromRow);
74
+ }
75
+ function updateNotificationDevicePreferences(deviceId, patch) {
76
+ const current = getNotificationDevice(deviceId);
77
+ if (!current) return null;
78
+ const preferences = normalizePreferences({
79
+ ...current.preferences,
80
+ ...patch
81
+ });
82
+ runSqliteWriteTransaction((db) => {
83
+ db.prepare("UPDATE notification_devices SET preferences_json = ?, updated_at = ? WHERE device_id = ?").run(JSON.stringify(preferences), Date.now(), deviceId);
84
+ });
85
+ return getNotificationDevice(deviceId);
86
+ }
87
+ function removeNotificationDevice(deviceId) {
88
+ return runSqliteWriteTransaction((db) => db.prepare("DELETE FROM notification_devices WHERE device_id = ?").run(deviceId).changes > 0);
89
+ }
90
+ function disableNotificationDeviceForPushToken(pushToken) {
91
+ runSqliteWriteTransaction((db) => {
92
+ db.prepare("UPDATE notification_devices SET enabled = 0, updated_at = ? WHERE push_token = ?").run(Date.now(), pushToken);
93
+ });
94
+ }
95
+ //#endregion
96
+ export { disableNotificationDeviceForPushToken, getNotificationDevice, listDeliverableNotificationDevices, registerNotificationDevice, removeNotificationDevice, updateNotificationDevicePreferences };
@@ -0,0 +1,6 @@
1
+ import { type ProductNotification } from '@xopcai/gateway-contract';
2
+ export type NotificationPlan = {
3
+ dedupeKey: string;
4
+ notification: Omit<ProductNotification, 'schemaVersion' | 'id' | 'createdAt'>;
5
+ };
6
+ export declare function notificationPlanFromGatewayEvent(type: string, payload: unknown): NotificationPlan | null;