@xopcai/xopc 0.0.309 → 0.0.311

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 (489) 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-XKNYlFUI.js → activity-store-DVUTs1fk.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-1S2TFECE.js → agent-avatar-dicebear-zKINl7UH.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-D2ePZKdY.js +1 -0
  6. package/dist/gateway/static/root/assets/{agents-CPqUMtCL.js → agents-BiD9STyv.js} +1 -1
  7. package/dist/gateway/static/root/assets/{api-BEUEE4f2.js → api-_aHQA8bd.js} +1 -1
  8. package/dist/gateway/static/root/assets/{app-link-BiBXVEvp.js → app-link-wamYbNUl.js} +1 -1
  9. package/dist/gateway/static/root/assets/{app-management-settings-panel-CgVBSqzu.js → app-management-settings-panel-C94LAxyt.js} +1 -1
  10. package/dist/gateway/static/root/assets/{appearance-settings-CoWdCokb.js → appearance-settings-CP01zHvC.js} +1 -1
  11. package/dist/gateway/static/root/assets/apps-page-B7DmSJQj.js +1 -0
  12. package/dist/gateway/static/root/assets/{archive-plugin-C3ZA6LRS.js → archive-plugin-SwWFt5MT.js} +1 -1
  13. package/dist/gateway/static/root/assets/{attachment-load-CdgS6_zy.js → attachment-load-DdTFml9l.js} +1 -1
  14. package/dist/gateway/static/root/assets/{automations-page-BtHL2hpg.js → automations-page-Kmr5WVFp.js} +1 -1
  15. package/dist/gateway/static/root/assets/automations-workspace-bkijiu-Y.js +7 -0
  16. package/dist/gateway/static/root/assets/{binary-plugins-CiDhtq_H.js → binary-plugins-47UTtDNq.js} +1 -1
  17. package/dist/gateway/static/root/assets/{block-editor-Br00T83w.js → block-editor-DvzZkMG8.js} +3 -3
  18. package/dist/gateway/static/root/assets/{browser-automation-inputs-D2tILykA.js → browser-automation-inputs-C611KRBx.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-automations-page-BVuEmryB.js → browser-automations-page-DrUKNL9X.js} +2 -2
  20. package/dist/gateway/static/root/assets/browser-settings-page-Dhf_MWCf.js +3 -0
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel-BCubrDf8.js +8 -0
  22. package/dist/gateway/static/root/assets/channels-settings-e4T1DEfN.js +1 -0
  23. package/dist/gateway/static/root/assets/{chat-terminal-dock-JsMtzDao.js → chat-terminal-dock-Bg_m7HUE.js} +2 -2
  24. package/dist/gateway/static/root/assets/{chunk-KS7C4IRE-Cq1tE_3b.js → chunk-KS7C4IRE-BMYIfy9x.js} +2 -2
  25. package/dist/gateway/static/root/assets/computer-settings-page-CosCmVsT.js +1 -0
  26. package/dist/gateway/static/root/assets/{connector-service-page-DZFNUTiN.js → connector-service-page-BL-hSR7_.js} +1 -1
  27. package/dist/gateway/static/root/assets/connectors-page-C0WWxnxJ.js +2 -0
  28. package/dist/gateway/static/root/assets/date-picker-DqYFVasF.js +1 -0
  29. package/dist/gateway/static/root/assets/{dependency-picker-B-BcuG1y.js → dependency-picker-DCyyFLKp.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-CdBBhhdY.js → desktop-pet-CFnnpRA_.js} +1 -1
  31. package/dist/gateway/static/root/assets/desktop-pet-settings-U5wWRH4c.js +1 -0
  32. package/dist/gateway/static/root/assets/device-pairing-wizard-BhHxvFO1.js +1 -0
  33. package/dist/gateway/static/root/assets/{extension-debug-page-BFp5eKtY.js → extension-debug-page-DA8WJ3-E.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-DL3vyyH6.js → extension-page-C2BIB88m.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-BXtvdAsY.js → extension-settings-page-kpYo979N.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-swr-xAOGtenO.js → gateway-config-swr-DUDX42qY.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-realtime-BuaTsMSi.js → gateway-realtime-CGOW0-b4.js} +1 -1
  38. package/dist/gateway/static/root/assets/gateway-realtime-store-D4um_sV7.js +1 -0
  39. package/dist/gateway/static/root/assets/gateway-settings-CE9t1yTa.js +1 -0
  40. package/dist/gateway/static/root/assets/{gateway-startup-retry-BwDxKHd4.js → gateway-startup-retry-Cf8OxlvY.js} +1 -1
  41. package/dist/gateway/static/root/assets/home-page-C7Ezx8Du.js +1 -0
  42. package/dist/gateway/static/root/assets/{image-generation-api-wGMeI_yW.js → image-generation-api-D2YorS--.js} +1 -1
  43. package/dist/gateway/static/root/assets/{imports-page-zexFT5Rq.js → imports-page-NPk698vI.js} +1 -1
  44. package/dist/gateway/static/root/assets/index-1wIKA-kM.css +1 -0
  45. package/dist/gateway/static/root/assets/index-DxYitLp8.js +79 -0
  46. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CutG9zLh.js → keyboard-shortcuts-settings-BhreF0_O.js} +1 -1
  47. package/dist/gateway/static/root/assets/local-app-workbench-page-CPmynb3x.js +2 -0
  48. package/dist/gateway/static/root/assets/{local-apps-page-CJy6I7Dt.js → local-apps-page-DOZZBdqj.js} +1 -1
  49. package/dist/gateway/static/root/assets/{locale-store-DPZ79W1-.js → locale-store-DA0Z5jM_.js} +1 -1
  50. package/dist/gateway/static/root/assets/{logs-page-egGcPHNi.js → logs-page-M8kz26yo.js} +2 -2
  51. package/dist/gateway/static/root/assets/{management-settings-BQhIOELc.js → management-settings-JLqHYQvY.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-p2P5hP1U.js → markdown-view-BDpcjR_Z.js} +1 -1
  53. package/dist/gateway/static/root/assets/markdown-view-DSjfHfaW.css +1 -0
  54. package/dist/gateway/static/root/assets/{media-plugins-CsBylqlN.js → media-plugins-B75SWUJd.js} +1 -1
  55. package/dist/gateway/static/root/assets/messages-4BtQKYlM.js +3 -0
  56. package/dist/gateway/static/root/assets/note-detail-panel-B_Ft0M32.js +5 -0
  57. package/dist/gateway/static/root/assets/notes-page-C32mxSD-.js +8 -0
  58. package/dist/gateway/static/root/assets/{page-tabs-ChQ4jJFs.js → page-tabs-CMlQUzT6.js} +1 -1
  59. package/dist/gateway/static/root/assets/{popover-select-9i-ZtHXL.js → popover-select-B-GDnZO9.js} +1 -1
  60. package/dist/gateway/static/root/assets/preview-open-alternatives-cZ2hszQT.js +1 -0
  61. package/dist/gateway/static/root/assets/{product-open-page-BADxWEdP.js → product-open-page-CPoSbglF.js} +1 -1
  62. package/dist/gateway/static/root/assets/project-detail-page-DYttWua0.js +1 -0
  63. package/dist/gateway/static/root/assets/projects-page-D46drWbl.js +1 -0
  64. package/dist/gateway/static/root/assets/{registry-api-B_e_nZSN.js → registry-api-C0DCx3wv.js} +1 -1
  65. package/dist/gateway/static/root/assets/remote-access-hub-C9n9Nuhi.js +1 -0
  66. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-D-pwiIyp.js → runtime-tools-settings-panel-bMLWRApp.js} +2 -2
  67. package/dist/gateway/static/root/assets/scenes-page-Dw3hmHAr.js +1 -0
  68. package/dist/gateway/static/root/assets/{schema-form-IWmb05DX.js → schema-form-UVJV_7AS.js} +1 -1
  69. package/dist/gateway/static/root/assets/sessions-page-C8P6ff91.js +1 -0
  70. package/dist/gateway/static/root/assets/settings-advanced-gate-whpUsAy3.js +1 -0
  71. package/dist/gateway/static/root/assets/{settings-form-section-B0veRBZs.js → settings-form-section-BqzOqonM.js} +1 -1
  72. package/dist/gateway/static/root/assets/settings-page-DQ9RJ8-b.js +1 -0
  73. package/dist/gateway/static/root/assets/settings-page-layout-D3Spald3.js +1 -0
  74. package/dist/gateway/static/root/assets/{settings-shell-layer-context-DCp6ykjQ.js → settings-shell-layer-context-BTQUDoJ2.js} +1 -1
  75. package/dist/gateway/static/root/assets/setup-status-panel-C8u601m8.js +1 -0
  76. package/dist/gateway/static/root/assets/{share-preview-page-CirpgkfD.js → share-preview-page-BcgYzA7y.js} +1 -1
  77. package/dist/gateway/static/root/assets/shares-settings-Bv-2rZ8e.js +1 -0
  78. package/dist/gateway/static/root/assets/skill-reload-api-LM-cgWUS.js +1 -0
  79. package/dist/gateway/static/root/assets/skills-page-ggmb7rCW.js +2 -0
  80. package/dist/gateway/static/root/assets/{skills-page.utils-B42mpWKV.js → skills-page.utils-ADABzrtk.js} +1 -1
  81. package/dist/gateway/static/root/assets/system-settings-panel-DfkcW_Za.js +1 -0
  82. package/dist/gateway/static/root/assets/task-detail-page-DC4yqDFd.js +1 -0
  83. package/dist/gateway/static/root/assets/{theme-store-BFbj_2Tu.js → theme-store-CegIlB8d.js} +1 -1
  84. package/dist/gateway/static/root/assets/tunnel-api-CTuwTdMm.js +1 -0
  85. package/dist/gateway/static/root/assets/use-autosave-5ZDd9XKh.js +1 -0
  86. package/dist/gateway/static/root/assets/user-model-page-BFKFhdZ0.js +2 -0
  87. package/dist/gateway/static/root/assets/{voice-api-key-field-BZbKQ8HT.js → voice-api-key-field-D3urfd-n.js} +1 -1
  88. package/dist/gateway/static/root/assets/{voice-config-api-SKCeQLrH.js → voice-config-api-DTyMdGOR.js} +1 -1
  89. package/dist/gateway/static/root/assets/{work-discovery-overlay-BRVTTXNG.js → work-discovery-overlay-JzM3Ylbs.js} +1 -1
  90. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-D2kuwv5e.js → workflow-run-setup-panel-Bn2CtRdB.js} +2 -2
  91. package/dist/gateway/static/root/assets/workflows-page-BiCmt98A.js +6 -0
  92. package/dist/gateway/static/root/connector-icons/SOURCES.md +0 -2
  93. package/dist/gateway/static/root/index.html +17 -20
  94. package/dist/gateway/static/root/notification-sw.js +1 -1
  95. package/dist/package.js +1 -1
  96. package/dist/packages/gateway-contract/src/notifications.js +12 -11
  97. package/dist/packages/gateway-contract/src/proactive.js +6 -6
  98. package/dist/packages/gateway-contract/src/project-monitoring.js +4 -4
  99. package/dist/packages/gateway-contract/src/project-operating.js +1 -4
  100. package/dist/src/agent/agent-manager.d.ts +2 -6
  101. package/dist/src/agent/agent-manager.js +2 -1
  102. package/dist/src/agent/background-review/run-background-review.js +1 -1
  103. package/dist/src/agent/commands/command-registry.js +1 -1
  104. package/dist/src/agent/context/execution-scope.js +1 -1
  105. package/dist/src/agent/external-tools/cliProvider.d.ts +47 -0
  106. package/dist/src/agent/external-tools/cliProvider.js +317 -0
  107. package/dist/src/agent/external-tools/composio-provider.js +3 -3
  108. package/dist/src/agent/external-tools/gateway-tools.js +8 -2
  109. package/dist/src/agent/external-tools/index.js +6 -0
  110. package/dist/src/agent/external-tools/service.js +1 -1
  111. package/dist/src/agent/external-tools/types.d.ts +1 -1
  112. package/dist/src/agent/external-tools/types.js +1 -0
  113. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  114. package/dist/src/agent/mcp/oauth/mcp-oauth-manager.js +1 -1
  115. package/dist/src/agent/memory/turn-provenance.js +2 -1
  116. package/dist/src/agent/service/parse-outbound-session-key.js +1 -1
  117. package/dist/src/agent/service.js +1 -0
  118. package/dist/src/agent/service.types.d.ts +2 -0
  119. package/dist/src/agent/tools/factory.d.ts +2 -0
  120. package/dist/src/agent/tools/factory.js +3 -2
  121. package/dist/src/agent/tools/search/providers/xopc-cloud.d.ts +20 -0
  122. package/dist/src/agent/tools/search/providers/xopc-cloud.js +56 -0
  123. package/dist/src/agent/tools/search/registry.d.ts +4 -2
  124. package/dist/src/agent/tools/search/registry.js +5 -3
  125. package/dist/src/agent/tools/xopc-use-tool.d.ts +4 -2
  126. package/dist/src/agent/tools/xopc-use-tool.js +46 -43
  127. package/dist/src/automations/service/automation-service.d.ts +0 -4
  128. package/dist/src/automations/service/automation-service.js +0 -31
  129. package/dist/src/capabilities/store-connector.js +46 -0
  130. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  131. package/dist/src/cli/commands/project.js +1 -1
  132. package/dist/src/config/schema.d.ts +4 -4
  133. package/dist/src/config/schema.js +2 -12
  134. package/dist/src/config/web-patch.js +0 -5
  135. package/dist/src/connectors/account-selection.d.ts +14 -0
  136. package/dist/src/connectors/account-selection.js +26 -0
  137. package/dist/src/connectors/china-catalog.js +93 -226
  138. package/dist/src/connectors/cli/adapterRegistry.d.ts +4 -0
  139. package/dist/src/connectors/cli/adapterRegistry.js +26 -0
  140. package/dist/src/connectors/cli/adapters/lark.d.ts +2 -0
  141. package/dist/src/connectors/cli/adapters/lark.js +143 -0
  142. package/dist/src/connectors/cli/adapters/larkShortcuts.d.ts +3 -0
  143. package/dist/src/connectors/cli/adapters/larkShortcuts.js +64 -0
  144. package/dist/src/connectors/cli/adapters/wecom.d.ts +2 -0
  145. package/dist/src/connectors/cli/adapters/wecom.js +97 -0
  146. package/dist/src/connectors/cli/adapters/wps365/bundle.js +575 -0
  147. package/dist/src/connectors/cli/adapters/wps365.d.ts +2 -0
  148. package/dist/src/connectors/cli/adapters/wps365.js +115 -0
  149. package/dist/src/connectors/cli/assets.d.ts +3 -0
  150. package/dist/src/connectors/cli/assets.js +44 -0
  151. package/dist/src/connectors/cli/authorization.d.ts +16 -0
  152. package/dist/src/connectors/cli/authorization.js +184 -0
  153. package/dist/src/connectors/cli/distributions.d.ts +4 -0
  154. package/dist/src/connectors/cli/distributions.js +69 -0
  155. package/dist/src/connectors/cli/installer.d.ts +5 -0
  156. package/dist/src/connectors/cli/installer.js +102 -0
  157. package/dist/src/connectors/cli/lifecycle.d.ts +1 -0
  158. package/dist/src/connectors/cli/lifecycle.js +19 -0
  159. package/dist/src/connectors/cli/process.d.ts +18 -0
  160. package/dist/src/connectors/cli/process.js +100 -0
  161. package/dist/src/connectors/cli/protocol.d.ts +10 -0
  162. package/dist/src/connectors/cli/protocol.js +63 -0
  163. package/dist/src/connectors/cli/runtime.d.ts +10 -0
  164. package/dist/src/connectors/cli/runtime.js +93 -0
  165. package/dist/src/connectors/cli/store.d.ts +35 -0
  166. package/dist/src/connectors/cli/store.js +121 -0
  167. package/dist/src/connectors/cli/types.d.ts +73 -0
  168. package/dist/src/connectors/composio-backends.js +1 -1
  169. package/dist/src/connectors/composio-sessions.d.ts +1 -0
  170. package/dist/src/connectors/composio-sessions.js +8 -3
  171. package/dist/src/connectors/composio.js +1 -1
  172. package/dist/src/connectors/connection-candidates.js +22 -3
  173. package/dist/src/connectors/connection-recovery-service.d.ts +1 -0
  174. package/dist/src/connectors/connection-recovery-service.js +53 -8
  175. package/dist/src/connectors/health.js +52 -0
  176. package/dist/src/connectors/instances.js +5 -1
  177. package/dist/src/connectors/lifecycle.js +2 -0
  178. package/dist/src/connectors/principal.js +2 -1
  179. package/dist/src/connectors/runtime-adapter-registry.js +31 -0
  180. package/dist/src/connectors/types.d.ts +13 -1
  181. package/dist/src/connectors/usage.js +1 -1
  182. package/dist/src/connectors/workflow-preflight.js +8 -4
  183. package/dist/src/extensions/install.js +1 -1
  184. package/dist/src/gateway/hono/app.d.ts +2 -0
  185. package/dist/src/gateway/hono/app.js +4 -2
  186. package/dist/src/gateway/hono/lib/config-payload.d.ts +0 -15
  187. package/dist/src/gateway/hono/lib/config-payload.js +0 -11
  188. package/dist/src/gateway/hono/routes/cli-connectors.d.ts +3 -0
  189. package/dist/src/gateway/hono/routes/cli-connectors.js +219 -0
  190. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +2 -2
  191. package/dist/src/gateway/hono/routes/config-patch/gateway.js +0 -59
  192. package/dist/src/gateway/hono/routes/config.js +0 -16
  193. package/dist/src/gateway/hono/routes/connectors.js +21 -22
  194. package/dist/src/gateway/hono/routes/deps.d.ts +2 -0
  195. package/dist/src/gateway/hono/routes/lazy-bundles.js +4 -17
  196. package/dist/src/gateway/hono/routes/projects.js +1 -1
  197. package/dist/src/gateway/hono/routes/public-gateway.js +0 -1
  198. package/dist/src/gateway/hono/routes/scenes.d.ts +3 -0
  199. package/dist/src/gateway/hono/routes/scenes.js +142 -0
  200. package/dist/src/gateway/hono/routes/tasks.js +0 -46
  201. package/dist/src/gateway/hono/routes/user-model.js +54 -1
  202. package/dist/src/gateway/scenes/browserNotifications.d.ts +4 -0
  203. package/dist/src/gateway/scenes/browserNotifications.js +54 -0
  204. package/dist/src/gateway/scenes/host.d.ts +34 -0
  205. package/dist/src/gateway/scenes/host.js +129 -0
  206. package/dist/src/gateway/scenes/mailContext.d.ts +32 -0
  207. package/dist/src/gateway/scenes/mailContext.js +235 -0
  208. package/dist/src/gateway/security/gateway-scopes.js +0 -4
  209. package/dist/src/gateway/security/index.js +1 -1
  210. package/dist/src/gateway/service/config-coordinator.d.ts +1 -10
  211. package/dist/src/gateway/service/config-coordinator.js +0 -16
  212. package/dist/src/gateway/service/run-gateway-agent.js +1 -1
  213. package/dist/src/gateway/service.d.ts +4 -25
  214. package/dist/src/gateway/service.js +44 -99
  215. package/dist/src/gateway/session-context-summary.js +2 -1
  216. package/dist/src/heartbeat/index.js +1 -1
  217. package/dist/src/infra/unhandled-rejections.js +1 -1
  218. package/dist/src/migrations/runner.js +1 -4
  219. package/dist/src/notifications/browser-endpoint.d.ts +2 -0
  220. package/dist/src/notifications/browser-endpoint.js +13 -0
  221. package/dist/src/notifications/browserSubscriptions.d.ts +20 -0
  222. package/dist/src/notifications/browserSubscriptions.js +59 -0
  223. package/dist/src/notifications/browserTransport.d.ts +9 -0
  224. package/dist/src/notifications/browserTransport.js +64 -0
  225. package/dist/src/notifications/dispatch.d.ts +46 -0
  226. package/dist/src/notifications/dispatch.js +109 -0
  227. package/dist/src/notifications/domain-delivery.d.ts +21 -0
  228. package/dist/src/notifications/planner.js +0 -36
  229. package/dist/src/notifications/quietHours.d.ts +6 -0
  230. package/dist/src/notifications/quietHours.js +23 -0
  231. package/dist/src/notifications/resultOutbox.d.ts +23 -0
  232. package/dist/src/notifications/resultOutbox.js +49 -0
  233. package/dist/src/notifications/service.d.ts +18 -4
  234. package/dist/src/notifications/service.js +42 -61
  235. package/dist/src/notifications/store.d.ts +2 -1
  236. package/dist/src/notifications/store.js +7 -8
  237. package/dist/src/projects/project-service.d.ts +1 -4
  238. package/dist/src/projects/project-service.js +2 -30
  239. package/dist/src/projects/workspace.js +1 -1
  240. package/dist/src/providers/model-catalog-persistence.js +19 -1
  241. package/dist/src/providers/model-catalog-store.js +1 -0
  242. package/dist/src/providers/model-catalog-types.d.ts +17 -0
  243. package/dist/src/providers/xopc-cloud-model-source.js +27 -0
  244. package/dist/src/scenes/agentExecutor.d.ts +8 -0
  245. package/dist/src/scenes/agentExecutor.js +63 -0
  246. package/dist/src/scenes/contracts.d.ts +118 -0
  247. package/dist/src/scenes/contracts.js +145 -0
  248. package/dist/src/scenes/execution.d.ts +56 -0
  249. package/dist/src/scenes/execution.js +168 -0
  250. package/dist/src/scenes/httpServices.d.ts +34 -0
  251. package/dist/src/scenes/inbox.d.ts +16 -0
  252. package/dist/src/scenes/inbox.js +68 -0
  253. package/dist/src/scenes/mailContext.d.ts +20 -0
  254. package/dist/src/scenes/mailContext.js +106 -0
  255. package/dist/src/scenes/mailObservations.d.ts +20 -0
  256. package/dist/src/scenes/mailObservations.js +107 -0
  257. package/dist/src/scenes/maintenance.d.ts +3 -0
  258. package/dist/src/scenes/maintenance.js +24 -0
  259. package/dist/src/scenes/metrics.d.ts +35 -0
  260. package/dist/src/scenes/metrics.js +94 -0
  261. package/dist/src/scenes/notificationEligibility.d.ts +17 -0
  262. package/dist/src/scenes/notificationEligibility.js +72 -0
  263. package/dist/src/scenes/notificationPublication.d.ts +7 -0
  264. package/dist/src/scenes/notificationPublication.js +43 -0
  265. package/dist/src/scenes/preferences.d.ts +40 -0
  266. package/dist/src/scenes/preferences.js +108 -0
  267. package/dist/src/scenes/readiness.d.ts +7 -0
  268. package/dist/src/scenes/readiness.js +20 -0
  269. package/dist/src/scenes/repository.d.ts +174 -0
  270. package/dist/src/scenes/repository.js +740 -0
  271. package/dist/src/scenes/resultNotifications.d.ts +17 -0
  272. package/dist/src/scenes/resultNotifications.js +204 -0
  273. package/dist/src/scenes/runtime.d.ts +25 -0
  274. package/dist/src/scenes/runtime.js +101 -0
  275. package/dist/src/scenes/schedule.d.ts +9 -0
  276. package/dist/src/scenes/schedule.js +45 -0
  277. package/dist/src/scenes/service.d.ts +29 -0
  278. package/dist/src/scenes/service.js +116 -0
  279. package/dist/src/scenes/templates.d.ts +64 -0
  280. package/dist/src/scenes/templates.js +76 -0
  281. package/dist/src/scenes/userNotes.d.ts +10 -0
  282. package/dist/src/scenes/userNotes.js +34 -0
  283. package/dist/src/session/resolve-session.js +2 -1
  284. package/dist/src/session/store.js +2 -1
  285. package/dist/src/share/share-config.js +0 -5
  286. package/dist/src/share/site-share-config.js +0 -5
  287. package/dist/src/storage/sqlite/connection-wait-repository.js +20 -7
  288. package/dist/src/storage/sqlite/connection.js +1 -1
  289. package/dist/src/storage/sqlite/connector-account-repository.js +1 -0
  290. package/dist/src/storage/sqlite/connector-sync-policy-repository.d.ts +0 -4
  291. package/dist/src/storage/sqlite/connector-sync-policy-repository.js +3 -17
  292. package/dist/src/storage/sqlite/conversation-repository.js +2 -1
  293. package/dist/src/storage/sqlite/index.js +2 -1
  294. package/dist/src/storage/sqlite/migrations/187_cli_connectors.sql +34 -0
  295. package/dist/src/storage/sqlite/migrations/188_scene_storage.sql +2 -0
  296. package/dist/src/storage/sqlite/migrations/189_scene_source_health.sql +8 -0
  297. package/dist/src/storage/sqlite/migrations/conversation-backup.d.ts +2 -0
  298. package/dist/src/storage/sqlite/migrations/conversation-backup.js +7 -3
  299. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  300. package/dist/src/storage/sqlite/migrations/runner.js +20 -44
  301. package/dist/src/storage/sqlite/migrations/scene-reset.d.ts +5 -0
  302. package/dist/src/storage/sqlite/migrations/scene-reset.js +40 -0
  303. package/dist/src/storage/sqlite/scenes-schema.d.ts +8 -0
  304. package/dist/src/storage/sqlite/scenes-schema.js +61 -0
  305. package/dist/src/storage/sqlite/schema.js +4 -10
  306. package/dist/src/storage/sqlite/schemas/notifications.sql +65 -0
  307. package/dist/src/storage/sqlite/schemas/scenes.sql +212 -0
  308. package/dist/src/storage/sqlite/session-read-repository.d.ts +8 -0
  309. package/dist/src/storage/sqlite/session-read-repository.js +35 -0
  310. package/dist/src/storage/sqlite/session-repository.d.ts +1 -1
  311. package/dist/src/storage/sqlite/session-repository.js +2 -25
  312. package/dist/src/storage/sqlite/sql-assets.d.ts +2 -0
  313. package/dist/src/storage/sqlite/sql-assets.js +18 -0
  314. package/dist/src/storage/sqlite/transaction.d.ts +2 -0
  315. package/dist/src/storage/sqlite/transaction.js +20 -1
  316. package/dist/src/tasks/home-query-service.d.ts +0 -2
  317. package/dist/src/tasks/home-query-service.js +13 -43
  318. package/dist/src/tasks/index.d.ts +0 -1
  319. package/dist/src/tasks/index.js +4 -5
  320. package/dist/src/tasks/project-operating-view-service.js +1 -4
  321. package/dist/src/tui/backends/embedded-backend.js +1 -1
  322. package/dist/src/tui/tui-commands.js +1 -1
  323. package/dist/src/user-model/goals.d.ts +10 -0
  324. package/dist/src/user-model/goals.js +29 -1
  325. package/dist/src/user-model/index.js +2 -2
  326. package/dist/src/voice/realtime/memory-context.js +2 -1
  327. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  328. package/package.json +4 -2
  329. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-f0-YFMUz.js +0 -1
  330. package/dist/gateway/static/root/assets/api-CN4id43t.js +0 -1
  331. package/dist/gateway/static/root/assets/apps-page-CexHOcWg.js +0 -1
  332. package/dist/gateway/static/root/assets/automations-workspace-onJPfmO6.js +0 -6
  333. package/dist/gateway/static/root/assets/browser-settings-page-BgVTyPFY.js +0 -3
  334. package/dist/gateway/static/root/assets/capabilities-settings-panel-Dra8wj1S.js +0 -8
  335. package/dist/gateway/static/root/assets/channels-settings-CkxiMDJ4.js +0 -1
  336. package/dist/gateway/static/root/assets/channels-status-swr-Dtder4G_.js +0 -1
  337. package/dist/gateway/static/root/assets/computer-settings-page-DHerePwo.js +0 -1
  338. package/dist/gateway/static/root/assets/connectors-page-cl9ZrV4D.js +0 -2
  339. package/dist/gateway/static/root/assets/cron-expression-DAhdv_Xv.js +0 -2
  340. package/dist/gateway/static/root/assets/date-picker-PacO1m3a.js +0 -1
  341. package/dist/gateway/static/root/assets/desktop-pet-settings-Gy03NgMI.js +0 -1
  342. package/dist/gateway/static/root/assets/device-pairing-wizard-aV25nKag.js +0 -1
  343. package/dist/gateway/static/root/assets/external-link-DjqjBmc3.js +0 -1
  344. package/dist/gateway/static/root/assets/gateway-realtime-store-BG95uGVW.js +0 -1
  345. package/dist/gateway/static/root/assets/gateway-settings-Dcr2Ikeh.js +0 -1
  346. package/dist/gateway/static/root/assets/globe-DSSGFWFn.js +0 -1
  347. package/dist/gateway/static/root/assets/heartbeat-settings-DOy3alvT.js +0 -1
  348. package/dist/gateway/static/root/assets/home-page-CDm3dGVy.js +0 -1
  349. package/dist/gateway/static/root/assets/index-D-vczw_0.css +0 -1
  350. package/dist/gateway/static/root/assets/index-DJo4DimT.js +0 -79
  351. package/dist/gateway/static/root/assets/loader-circle-CifKLtgG.js +0 -1
  352. package/dist/gateway/static/root/assets/local-app-workbench-page-CwLvTwjn.js +0 -2
  353. package/dist/gateway/static/root/assets/markdown-view-Y68dCxuc.css +0 -1
  354. package/dist/gateway/static/root/assets/messages-BiYTyLJr.js +0 -3
  355. package/dist/gateway/static/root/assets/note-detail-panel-c2k5Uz6X.js +0 -5
  356. package/dist/gateway/static/root/assets/notes-page-BLQWJMhf.js +0 -8
  357. package/dist/gateway/static/root/assets/preview-open-alternatives-9B5W7nVE.js +0 -1
  358. package/dist/gateway/static/root/assets/proactive-page-iOjI0d_e.js +0 -1
  359. package/dist/gateway/static/root/assets/project-detail-page-BcxbQn2M.js +0 -1
  360. package/dist/gateway/static/root/assets/projects-page-BuSvld0O.js +0 -1
  361. package/dist/gateway/static/root/assets/remote-access-hub-CFS8LQKW.js +0 -1
  362. package/dist/gateway/static/root/assets/sessions-page-DZjc__ok.js +0 -1
  363. package/dist/gateway/static/root/assets/settings-advanced-gate-C1SM5B93.js +0 -1
  364. package/dist/gateway/static/root/assets/settings-page-CxRWyWYe.js +0 -1
  365. package/dist/gateway/static/root/assets/settings-page-layout-X1x-2Cp9.js +0 -1
  366. package/dist/gateway/static/root/assets/setup-status-panel-D9Rl7Tec.js +0 -1
  367. package/dist/gateway/static/root/assets/shares-settings-CwCThBs0.js +0 -1
  368. package/dist/gateway/static/root/assets/skill-reload-api-DG6VGbOX.js +0 -1
  369. package/dist/gateway/static/root/assets/skills-page-BdRJ2vnL.js +0 -2
  370. package/dist/gateway/static/root/assets/system-settings-panel-DXIZpAvU.js +0 -1
  371. package/dist/gateway/static/root/assets/task-detail-page-QXo9LJly.js +0 -1
  372. package/dist/gateway/static/root/assets/tunnel-api-Db2guRdW.js +0 -1
  373. package/dist/gateway/static/root/assets/use-autosave-DDY9qoXZ.js +0 -1
  374. package/dist/gateway/static/root/assets/user-model-page-CzPmMkaO.js +0 -2
  375. package/dist/gateway/static/root/assets/workflows-page-CQXtiYtq.js +0 -6
  376. package/dist/gateway/static/root/connector-icons/wps-calendar.svg +0 -14
  377. package/dist/gateway/static/root/connector-icons/wps-mail.svg +0 -7
  378. package/dist/src/connectors/source-change-publisher.d.ts +0 -17
  379. package/dist/src/connectors/source-change-publisher.js +0 -151
  380. package/dist/src/gateway/heartbeat/check-store.d.ts +0 -18
  381. package/dist/src/gateway/heartbeat/check-store.js +0 -74
  382. package/dist/src/gateway/heartbeat/index.d.ts +0 -1
  383. package/dist/src/gateway/heartbeat/index.js +0 -2
  384. package/dist/src/gateway/heartbeat/service.d.ts +0 -55
  385. package/dist/src/gateway/heartbeat/service.js +0 -228
  386. package/dist/src/gateway/hono/routes/proactive-control.d.ts +0 -3
  387. package/dist/src/gateway/hono/routes/proactive-control.js +0 -135
  388. package/dist/src/gateway/hono/routes/proactive.d.ts +0 -4
  389. package/dist/src/gateway/hono/routes/proactive.js +0 -164
  390. package/dist/src/gateway/hono/routes/workspace.d.ts +0 -3
  391. package/dist/src/gateway/hono/routes/workspace.js +0 -62
  392. package/dist/src/gateway/workspace-heartbeat-path.d.ts +0 -3
  393. package/dist/src/gateway/workspace-heartbeat-path.js +0 -14
  394. package/dist/src/migrations/conversation-routing-config.d.ts +0 -2
  395. package/dist/src/migrations/conversation-routing-config.js +0 -42
  396. package/dist/src/notifications/proactive-channel.d.ts +0 -8
  397. package/dist/src/notifications/proactive-channel.js +0 -60
  398. package/dist/src/notifications/proactive-policy.d.ts +0 -3
  399. package/dist/src/notifications/proactive-policy.js +0 -35
  400. package/dist/src/notifications/web-push.d.ts +0 -19
  401. package/dist/src/notifications/web-push.js +0 -185
  402. package/dist/src/proactive/actions/service.d.ts +0 -1
  403. package/dist/src/proactive/actions/service.js +0 -82
  404. package/dist/src/proactive/events/envelope.d.ts +0 -2
  405. package/dist/src/proactive/events/envelope.js +0 -54
  406. package/dist/src/proactive/events/product-event-bridge.d.ts +0 -7
  407. package/dist/src/proactive/events/product-event-bridge.js +0 -87
  408. package/dist/src/proactive/events/publisher.d.ts +0 -4
  409. package/dist/src/proactive/events/repository.d.ts +0 -9
  410. package/dist/src/proactive/events/repository.js +0 -63
  411. package/dist/src/proactive/events/types.d.ts +0 -44
  412. package/dist/src/proactive/events/types.js +0 -15
  413. package/dist/src/proactive/execution/agent-executor.d.ts +0 -21
  414. package/dist/src/proactive/execution/agent-executor.js +0 -125
  415. package/dist/src/proactive/execution/authorization.d.ts +0 -7
  416. package/dist/src/proactive/execution/authorization.js +0 -71
  417. package/dist/src/proactive/execution/context.d.ts +0 -49
  418. package/dist/src/proactive/execution/context.js +0 -408
  419. package/dist/src/proactive/execution/insight.d.ts +0 -14
  420. package/dist/src/proactive/execution/insight.js +0 -132
  421. package/dist/src/proactive/execution/repository.d.ts +0 -28
  422. package/dist/src/proactive/execution/repository.js +0 -211
  423. package/dist/src/proactive/execution/types.d.ts +0 -80
  424. package/dist/src/proactive/execution/worker.d.ts +0 -15
  425. package/dist/src/proactive/execution/worker.js +0 -146
  426. package/dist/src/proactive/experience.d.ts +0 -89
  427. package/dist/src/proactive/experience.js +0 -195
  428. package/dist/src/proactive/follow-ups.d.ts +0 -128
  429. package/dist/src/proactive/follow-ups.js +0 -182
  430. package/dist/src/proactive/inbox/cards.d.ts +0 -21
  431. package/dist/src/proactive/inbox/cards.js +0 -276
  432. package/dist/src/proactive/inbox/delivery.d.ts +0 -6
  433. package/dist/src/proactive/inbox/delivery.js +0 -54
  434. package/dist/src/proactive/inbox/digest.d.ts +0 -8
  435. package/dist/src/proactive/inbox/digest.js +0 -123
  436. package/dist/src/proactive/inbox/lifecycle.d.ts +0 -4
  437. package/dist/src/proactive/inbox/lifecycle.js +0 -62
  438. package/dist/src/proactive/inbox/notification-policy.d.ts +0 -1
  439. package/dist/src/proactive/inbox/notification-policy.js +0 -25
  440. package/dist/src/proactive/inbox/repository.d.ts +0 -25
  441. package/dist/src/proactive/inbox/repository.js +0 -244
  442. package/dist/src/proactive/inbox/service.d.ts +0 -23
  443. package/dist/src/proactive/inbox/service.js +0 -46
  444. package/dist/src/proactive/inbox/types.d.ts +0 -64
  445. package/dist/src/proactive/inbox/worker.d.ts +0 -13
  446. package/dist/src/proactive/inbox/worker.js +0 -71
  447. package/dist/src/proactive/index.d.ts +0 -20
  448. package/dist/src/proactive/index.js +0 -14
  449. package/dist/src/proactive/mail-thread.d.ts +0 -13
  450. package/dist/src/proactive/mail-thread.js +0 -55
  451. package/dist/src/proactive/maintenance.d.ts +0 -4
  452. package/dist/src/proactive/maintenance.js +0 -28
  453. package/dist/src/proactive/metrics.d.ts +0 -10
  454. package/dist/src/proactive/metrics.js +0 -28
  455. package/dist/src/proactive/policy/presence.d.ts +0 -2
  456. package/dist/src/proactive/policy/presence.js +0 -30
  457. package/dist/src/proactive/policy/service.d.ts +0 -54
  458. package/dist/src/proactive/policy/service.js +0 -123
  459. package/dist/src/proactive/routing/batch-repository.d.ts +0 -27
  460. package/dist/src/proactive/routing/batch-repository.js +0 -57
  461. package/dist/src/proactive/routing/matcher.d.ts +0 -5
  462. package/dist/src/proactive/routing/matcher.js +0 -57
  463. package/dist/src/proactive/routing/types.d.ts +0 -44
  464. package/dist/src/proactive/routing/types.js +0 -1
  465. package/dist/src/proactive/scenarios/control.d.ts +0 -52
  466. package/dist/src/proactive/scenarios/control.js +0 -75
  467. package/dist/src/proactive/scenarios/prompt-composer.d.ts +0 -14
  468. package/dist/src/proactive/scenarios/prompt-composer.js +0 -26
  469. package/dist/src/proactive/scenarios/repository.d.ts +0 -23
  470. package/dist/src/proactive/scenarios/repository.js +0 -174
  471. package/dist/src/proactive/scenarios/service.d.ts +0 -15
  472. package/dist/src/proactive/scenarios/service.js +0 -26
  473. package/dist/src/proactive/scenarios/types.d.ts +0 -43
  474. package/dist/src/proactive/scenarios/types.js +0 -1
  475. package/dist/src/proactive/service.d.ts +0 -25
  476. package/dist/src/proactive/service.js +0 -78
  477. package/dist/src/proactive/source-freshness.d.ts +0 -8
  478. package/dist/src/proactive/source-freshness.js +0 -28
  479. package/dist/src/proactive/status.d.ts +0 -2
  480. package/dist/src/proactive/status.js +0 -22
  481. package/dist/src/proactive/temporal/schedule.d.ts +0 -3
  482. package/dist/src/proactive/temporal/schedule.js +0 -68
  483. package/dist/src/proactive/temporal/worker.d.ts +0 -17
  484. package/dist/src/proactive/temporal/worker.js +0 -129
  485. package/dist/src/tasks/project-monitoring-service.d.ts +0 -32
  486. package/dist/src/tasks/project-monitoring-service.js +0 -118
  487. /package/dist/src/{proactive/execution → connectors/cli}/types.js +0 -0
  488. /package/dist/src/{proactive/events/publisher.js → notifications/domain-delivery.js} +0 -0
  489. /package/dist/src/{proactive/inbox/types.js → scenes/httpServices.js} +0 -0
@@ -0,0 +1,740 @@
1
+ import { activationInputSchema, canTransitionActivation, sceneContentHash, sceneNotesSchema, sceneTemplateSchema } from "./contracts.js";
2
+ import { nextSceneScheduleAt, sceneScheduleSchema } from "./schedule.js";
3
+ import { randomUUID } from "node:crypto";
4
+ //#region src/scenes/repository.ts
5
+ const checksAllowed = `NOT EXISTS (SELECT 1 FROM scene_preferences policy
6
+ WHERE policy.owner_id = a.owner_id AND policy.workspace_id = a.workspace_id
7
+ AND (json_extract(policy.preferences_json, '$.checksPaused') = 1
8
+ OR julianday(json_extract(policy.preferences_json, '$.checksPausedUntil')) > julianday(?, 'unixepoch')))`;
9
+ var SceneConflictError = class extends Error {};
10
+ var SceneNotFoundError = class extends Error {};
11
+ var SceneInputError = class extends Error {};
12
+ /** The caller supplies the database; storage is initialized by the normal database upgrade. */
13
+ var SceneRepository = class {
14
+ constructor(db) {
15
+ this.db = db;
16
+ }
17
+ transaction(operation) {
18
+ const savepoint = `scene_${randomUUID().replaceAll("-", "")}`;
19
+ this.db.exec(`SAVEPOINT ${savepoint}`);
20
+ try {
21
+ const result = operation();
22
+ this.db.exec(`RELEASE ${savepoint}`);
23
+ return result;
24
+ } catch (error) {
25
+ this.db.exec(`ROLLBACK TO ${savepoint}`);
26
+ this.db.exec(`RELEASE ${savepoint}`);
27
+ throw error;
28
+ }
29
+ }
30
+ installTemplate(value) {
31
+ const template = sceneTemplateSchema.parse(value);
32
+ const hash = sceneContentHash(template);
33
+ return this.transaction(() => {
34
+ const previous = this.db.prepare("SELECT content_hash FROM scene_template_versions WHERE template_key = ? AND version = ?").get(template.key, template.version);
35
+ if (previous && previous.content_hash !== hash) throw new SceneConflictError("Template versions are immutable");
36
+ this.db.prepare("INSERT OR IGNORE INTO scene_template_versions VALUES (?, ?, ?, ?)").run(template.key, template.version, hash, JSON.stringify(template));
37
+ return template;
38
+ });
39
+ }
40
+ getTemplate(key, version) {
41
+ const row = this.db.prepare("SELECT manifest_json FROM scene_template_versions WHERE template_key = ? AND version = ?").get(key, version);
42
+ if (!row) throw new SceneNotFoundError("Scene template not found");
43
+ return sceneTemplateSchema.parse(JSON.parse(String(row.manifest_json)));
44
+ }
45
+ listTemplates() {
46
+ return this.db.prepare("SELECT manifest_json FROM scene_template_versions ORDER BY template_key, version").all().map((row) => sceneTemplateSchema.parse(JSON.parse(String(row.manifest_json))));
47
+ }
48
+ listRuns(principal, activationId, limit = 50, afterId = "") {
49
+ this.getActivation(principal, activationId);
50
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new Error("Invalid scene list limit");
51
+ const cursor = afterId ? this.db.prepare("SELECT created_at FROM scene_runs WHERE id = ? AND activation_id = ?").get(afterId, activationId) : void 0;
52
+ if (afterId && !cursor) throw new SceneNotFoundError("Scene run cursor not found");
53
+ return this.db.prepare(`SELECT id, activation_id AS activationId, status, attempt, reason, created_at AS createdAt, retry_at AS retryAt
54
+ FROM scene_runs WHERE activation_id = ? AND (? IS NULL OR created_at < ? OR (created_at = ? AND id < ?))
55
+ ORDER BY created_at DESC, id DESC LIMIT ?`).all(activationId, cursor?.created_at ?? null, cursor?.created_at ?? null, cursor?.created_at ?? null, afterId, limit);
56
+ }
57
+ createActivation(principal, value, requestId) {
58
+ this.assertPrincipal(principal);
59
+ const input = activationInputSchema.parse(value);
60
+ if (requestId !== void 0 && (!requestId.trim() || requestId.length > 200)) throw new Error("Invalid scene request identity");
61
+ return this.transaction(() => {
62
+ const hash = sceneContentHash(input);
63
+ if (requestId !== void 0) {
64
+ const previous = this.db.prepare("SELECT * FROM scene_activation_requests WHERE owner_id = ? AND workspace_id = ? AND request_id = ?").get(principal.ownerId, principal.workspaceId, requestId);
65
+ if (previous) {
66
+ if (previous.content_hash !== hash) throw new SceneConflictError("Scene request identity was reused");
67
+ return this.getActivation(principal, String(previous.activation_id));
68
+ }
69
+ }
70
+ this.getTemplate(input.templateKey, input.templateVersion);
71
+ const activation = {
72
+ ...input,
73
+ ...principal,
74
+ id: randomUUID(),
75
+ status: "needs_setup",
76
+ revision: 1
77
+ };
78
+ this.db.prepare(`INSERT INTO scene_activations VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(activation.id, principal.ownerId, principal.workspaceId, input.templateKey, input.templateVersion, input.goal, JSON.stringify(input.scope), JSON.stringify(input.permissions), activation.status, activation.revision);
79
+ if (requestId !== void 0) this.db.prepare("INSERT INTO scene_activation_requests VALUES (?, ?, ?, ?, ?)").run(principal.ownerId, principal.workspaceId, requestId, hash, activation.id);
80
+ return activation;
81
+ });
82
+ }
83
+ listActivations(principal, limit = 50, afterId = "") {
84
+ this.assertPrincipal(principal);
85
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new Error("Invalid scene list limit");
86
+ return this.db.prepare("SELECT id FROM scene_activations WHERE owner_id = ? AND workspace_id = ? AND id > ? ORDER BY id LIMIT ?").all(principal.ownerId, principal.workspaceId, afterId, limit).map((row) => this.getActivation(principal, String(row.id)));
87
+ }
88
+ getActivation(principal, id) {
89
+ this.assertPrincipal(principal);
90
+ const row = this.db.prepare("SELECT * FROM scene_activations WHERE id = ? AND owner_id = ? AND workspace_id = ?").get(id, principal.ownerId, principal.workspaceId);
91
+ if (!row) throw new SceneNotFoundError("Scene activation not found");
92
+ const providers = this.getTemplate(String(row.template_key), String(row.template_version)).contextProviders;
93
+ const setupMissing = [];
94
+ if (providers.includes("user_notes")) {
95
+ const notes = this.db.prepare("SELECT content, valid_until FROM scene_notes WHERE activation_id = ?").get(id);
96
+ if (!notes || !String(notes.content).trim() || notes.valid_until !== null && Number(notes.valid_until) <= Date.now()) setupMissing.push("notes");
97
+ if (!this.db.prepare("SELECT 1 FROM scene_schedule_cursors WHERE activation_id = ?").get(id)) setupMissing.push("schedule");
98
+ }
99
+ if (providers.includes("mail") && !this.db.prepare("SELECT 1 FROM scene_work_items WHERE activation_id = ? AND status = 'watching'").get(id)) setupMissing.push("deadline");
100
+ return {
101
+ setupMissing,
102
+ id: String(row.id),
103
+ ...principal,
104
+ templateKey: String(row.template_key),
105
+ templateVersion: String(row.template_version),
106
+ goal: String(row.goal),
107
+ scope: JSON.parse(String(row.scope_json)),
108
+ permissions: JSON.parse(String(row.permissions_json)),
109
+ status: row.status,
110
+ revision: Number(row.revision)
111
+ };
112
+ }
113
+ recordSourceHealth(activation, reason, now, retryAt = null) {
114
+ if (!this.db.prepare("SELECT 1 FROM scene_activations WHERE id = ? AND revision = ? AND status = 'active'").get(activation.id, activation.revision)) return;
115
+ this.db.prepare(`INSERT INTO scene_source_health VALUES (?, ?, ?, ?, ?, ?)
116
+ ON CONFLICT(activation_id) DO UPDATE SET last_attempt_at = excluded.last_attempt_at,
117
+ last_success_at = COALESCE(excluded.last_success_at, scene_source_health.last_success_at),
118
+ reason = excluded.reason, consecutive_failures = CASE WHEN excluded.reason IS NULL THEN 0 ELSE scene_source_health.consecutive_failures + 1 END,
119
+ retry_at = excluded.retry_at`).run(activation.id, now, reason === null ? now : null, reason, reason === null ? 0 : 1, retryAt);
120
+ }
121
+ getSourceHealth(activationId) {
122
+ return this.db.prepare(`SELECT last_attempt_at AS lastAttemptAt, last_success_at AS lastSuccessAt,
123
+ reason, consecutive_failures AS consecutiveFailures, retry_at AS retryAt FROM scene_source_health WHERE activation_id = ?`).get(activationId) ?? null;
124
+ }
125
+ reuseUnchangedResult(claim, hash, now, inspectOnly = false) {
126
+ return this.transaction(() => {
127
+ if (!this.isCurrentClaim(claim, now)) return false;
128
+ if (!this.db.prepare(`SELECT p.id FROM scene_presentations p
129
+ JOIN scene_outcomes o ON o.id = p.outcome_id JOIN scene_runs r ON r.id = o.run_id
130
+ JOIN scene_context_snapshots s ON s.run_id = r.id AND s.lease_epoch = r.lease_epoch
131
+ JOIN scene_trigger_intents i ON i.id = r.intent_id JOIN scene_events e ON e.id = i.event_id
132
+ JOIN scene_trigger_intents ci ON ci.id = ? JOIN scene_events ce ON ce.id = ci.event_id
133
+ WHERE r.activation_id = ? AND r.activation_revision = ? AND s.content_hash = ?
134
+ AND ce.event_type IN ('manual.check', 'scene.manual.check') AND e.subject_id = ce.subject_id AND e.account_id IS ce.account_id
135
+ AND p.status <> 'withdrawn' AND p.withdrawn_at IS NULL AND (p.expires_at IS NULL OR p.expires_at > ?)
136
+ LIMIT 1`).get(claim.intentId, claim.activationId, claim.activationRevision, hash, now)) return false;
137
+ if (inspectOnly) return true;
138
+ this.db.prepare("UPDATE scene_runs SET status = 'skipped', reason = 'unchanged_result', lease_owner = NULL, lease_until = NULL WHERE id = ?").run(claim.id);
139
+ this.db.prepare("UPDATE scene_trigger_intents SET status = 'resolved' WHERE id = ?").run(claim.intentId);
140
+ return true;
141
+ });
142
+ }
143
+ /** Configuration edits require a fresh preflight before any execution resumes. */
144
+ configureActivation(principal, id, revision, value) {
145
+ return this.transaction(() => {
146
+ const activation = this.getActivation(principal, id);
147
+ if (activation.revision !== revision || ![
148
+ "active",
149
+ "paused",
150
+ "needs_setup"
151
+ ].includes(activation.status)) throw new SceneConflictError("Scene activation changed or cannot be configured");
152
+ const input = activationInputSchema.pick({
153
+ goal: true,
154
+ scope: true,
155
+ permissions: true
156
+ }).parse(value);
157
+ this.db.prepare(`UPDATE scene_activations SET goal = ?, scope_json = ?, permissions_json = ?,
158
+ status = 'needs_setup', revision = revision + 1 WHERE id = ?`).run(input.goal, JSON.stringify(input.scope), JSON.stringify(input.permissions), id);
159
+ this.db.prepare("UPDATE scene_trigger_intents SET status = 'cancelled' WHERE activation_id = ? AND status IN ('pending', 'claimed')").run(id);
160
+ this.db.prepare("UPDATE scene_runs SET status = 'cancelled', reason = 'activation_changed', lease_owner = NULL, lease_until = NULL WHERE activation_id = ? AND status IN ('running', 'retry_wait')").run(id);
161
+ this.db.prepare(`UPDATE scene_work_items SET status = 'paused', revision = revision + 1, last_check_reason = 'activation_changed'
162
+ WHERE activation_id = ? AND status <> 'completed'`).run(id);
163
+ this.db.prepare(`UPDATE scene_presentations SET status = 'withdrawn' WHERE outcome_id IN
164
+ (SELECT o.id FROM scene_outcomes o JOIN scene_runs r ON r.id = o.run_id WHERE r.activation_id = ?)`).run(id);
165
+ return this.getActivation(principal, id);
166
+ });
167
+ }
168
+ writeNotes(principal, id, value, now) {
169
+ const input = sceneNotesSchema.parse(value);
170
+ if (!Number.isSafeInteger(now) || now < 0 || input.validUntil !== null && input.validUntil <= now) throw new Error("Invalid notes validity time");
171
+ return this.transaction(() => {
172
+ const activation = this.getActivation(principal, id);
173
+ if (activation.scope.kind !== "personal" || ![
174
+ "active",
175
+ "paused",
176
+ "needs_setup"
177
+ ].includes(activation.status) || !this.getTemplate(activation.templateKey, activation.templateVersion).contextProviders.includes("user_notes")) throw new SceneConflictError("Scene does not accept personal notes");
178
+ const row = this.db.prepare("SELECT revision FROM scene_notes WHERE activation_id = ?").get(id);
179
+ if (Number(row?.revision ?? 0) !== input.expectedRevision) throw new SceneConflictError("Scene notes changed");
180
+ const revision = input.expectedRevision + 1;
181
+ this.db.prepare(`INSERT INTO scene_notes VALUES (?, ?, ?, ?, ?)
182
+ ON CONFLICT(activation_id) DO UPDATE SET content = excluded.content, revision = excluded.revision,
183
+ valid_until = excluded.valid_until, updated_at = excluded.updated_at`).run(id, input.content, revision, input.validUntil, now);
184
+ this.db.prepare(`UPDATE scene_trigger_intents SET status = 'cancelled' WHERE id IN
185
+ (SELECT intent_id FROM scene_runs WHERE activation_id = ? AND status IN ('running', 'retry_wait'))`).run(id);
186
+ this.db.prepare(`UPDATE scene_runs SET status = 'cancelled', reason = 'source_changed', lease_owner = NULL, lease_until = NULL
187
+ WHERE activation_id = ? AND status IN ('running', 'retry_wait')`).run(id);
188
+ this.db.prepare(`UPDATE scene_presentations SET status = 'withdrawn' WHERE outcome_id IN
189
+ (SELECT o.id FROM scene_outcomes o JOIN scene_runs r ON r.id = o.run_id WHERE r.activation_id = ?)`).run(id);
190
+ return revision;
191
+ });
192
+ }
193
+ readNotes(principal, id) {
194
+ this.getActivation(principal, id);
195
+ return this.db.prepare("SELECT content, revision, valid_until AS validUntil FROM scene_notes WHERE activation_id = ?").get(id) ?? {
196
+ content: "",
197
+ revision: 0,
198
+ validUntil: null
199
+ };
200
+ }
201
+ listWorkItems(principal, activationId, limit = 50, afterId = "") {
202
+ this.getActivation(principal, activationId);
203
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new Error("Invalid scene list limit");
204
+ return this.db.prepare(`SELECT id, subject_id AS subjectId, account_id AS accountId, due_at AS dueAt, status, revision,
205
+ last_check_reason AS lastCheckReason FROM scene_work_items WHERE activation_id = ? AND id > ? ORDER BY id LIMIT ?`).all(activationId, afterId, limit);
206
+ }
207
+ listSchedules(principal, activationId) {
208
+ this.getActivation(principal, activationId);
209
+ return this.db.prepare("SELECT * FROM scene_schedule_cursors WHERE activation_id = ? ORDER BY trigger_key").all(activationId).map((row) => ({
210
+ triggerKey: String(row.trigger_key),
211
+ schedule: sceneScheduleSchema.parse(JSON.parse(String(row.schedule_json))),
212
+ revision: Number(row.revision),
213
+ nextDueAt: Number(row.next_due_at)
214
+ }));
215
+ }
216
+ createWorkItem(principal, activationId, input, now) {
217
+ return this.transaction(() => {
218
+ const activation = this.getActivation(principal, activationId);
219
+ if (activation.status !== "active") throw new SceneConflictError("Scene activation is not active");
220
+ if (!input.subjectId.trim() || !activation.permissions.accountIds.includes(input.accountId)) throw new SceneInputError("Work item account is not authorized");
221
+ if (activation.scope.kind !== "objects" || !activation.scope.ids.includes(input.subjectId)) throw new SceneInputError("Mail follow-up requires an explicit object scope");
222
+ if (!this.getTemplate(activation.templateKey, activation.templateVersion).triggers.some((item) => item.type === "schedule")) throw new SceneInputError("Work item template needs a schedule trigger");
223
+ if (!Number.isSafeInteger(input.dueAt) || input.dueAt <= now) throw new SceneInputError("Work item deadline must be in the future");
224
+ if (this.db.prepare("SELECT 1 FROM scene_work_items WHERE activation_id = ? AND account_id = ? AND subject_id = ?").get(activationId, input.accountId, input.subjectId)) throw new SceneConflictError("Scene work item already exists");
225
+ const id = randomUUID();
226
+ this.db.prepare(`INSERT INTO scene_work_items (id, activation_id, subject_id, account_id, due_at, revision, status)
227
+ VALUES (?, ?, ?, ?, ?, 1, 'watching')`).run(id, activationId, input.subjectId, input.accountId, input.dueAt);
228
+ return {
229
+ id,
230
+ activationId,
231
+ ...input,
232
+ revision: 1,
233
+ status: "watching"
234
+ };
235
+ });
236
+ }
237
+ /** Internal worker scan; identity comes from persisted ownership, never an event payload. */
238
+ recordModelUsage(claim, usage, now) {
239
+ if (!this.isCurrentClaim(claim, now)) return;
240
+ this.db.prepare(`INSERT OR IGNORE INTO scene_model_usage VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(claim.id, claim.leaseEpoch, usage.provider, usage.model, usage.inputTokens, usage.outputTokens, usage.totalTokens, usage.estimatedCost, now);
241
+ }
242
+ listMailObservationItems(afterId = "", now = Date.now()) {
243
+ return this.db.prepare(`SELECT w.*, a.owner_id, a.workspace_id FROM scene_work_items w
244
+ JOIN scene_activations a ON a.id = w.activation_id
245
+ WHERE a.status = 'active' AND w.status = 'watching' AND w.id > ? AND ${checksAllowed}
246
+ ORDER BY w.id LIMIT 100`).all(afterId, now / 1e3).map((row) => ({
247
+ principal: {
248
+ ownerId: String(row.owner_id),
249
+ workspaceId: String(row.workspace_id)
250
+ },
251
+ item: {
252
+ id: String(row.id),
253
+ activationId: String(row.activation_id),
254
+ subjectId: String(row.subject_id),
255
+ accountId: String(row.account_id),
256
+ dueAt: Number(row.due_at),
257
+ revision: Number(row.revision),
258
+ status: "watching"
259
+ }
260
+ }));
261
+ }
262
+ isMailObservationCurrent(activation, item, now = Date.now()) {
263
+ return Boolean(this.db.prepare(`SELECT 1 FROM scene_work_items w JOIN scene_activations a ON a.id = w.activation_id
264
+ WHERE w.id = ? AND w.activation_id = ? AND w.revision = ? AND w.status = 'watching'
265
+ AND a.owner_id = ? AND a.workspace_id = ? AND a.revision = ? AND a.status = 'active' AND ${checksAllowed}`).get(item.id, activation.id, item.revision, activation.ownerId, activation.workspaceId, activation.revision, now / 1e3));
266
+ }
267
+ observeMailWorkItem(principal, item, fingerprint, now) {
268
+ if (!/^[a-f0-9]{64}$/.test(fingerprint)) throw new Error("Invalid scene observation fingerprint");
269
+ return this.transaction(() => {
270
+ const activation = this.getActivation(principal, item.activationId);
271
+ if (!this.isMailObservationCurrent(activation, item, now)) return false;
272
+ const row = this.db.prepare("SELECT * FROM scene_work_items WHERE id = ? AND activation_id = ?").get(item.id, item.activationId);
273
+ if (!row || row.revision !== item.revision || row.status !== "watching" || activation.status !== "active") return false;
274
+ if (row.subject_id !== item.subjectId || row.account_id !== item.accountId) throw new SceneConflictError("Work item identity changed");
275
+ if (row.observed_fingerprint === fingerprint) return false;
276
+ const trigger = this.getTemplate(activation.templateKey, activation.templateVersion).triggers.find((entry) => entry.type === "event" && entry.eventType === "mail.thread.changed");
277
+ if (!trigger) return false;
278
+ const sequence = Number(row.observation_sequence) + 1;
279
+ this.db.prepare("UPDATE scene_work_items SET observed_fingerprint = ?, observation_sequence = ? WHERE id = ?").run(fingerprint, sequence, item.id);
280
+ if (row.observed_fingerprint === null) return false;
281
+ const occurrenceKey = `mail:${item.id}:${item.revision}:${sequence}`;
282
+ this.acceptTrigger(principal, item.activationId, {
283
+ triggerKey: trigger.id,
284
+ occurrenceKey,
285
+ dueAt: now,
286
+ event: {
287
+ source: `mail:${item.accountId}`,
288
+ sourceEventId: occurrenceKey,
289
+ subjectId: item.subjectId,
290
+ accountId: item.accountId,
291
+ eventType: "mail.thread.changed",
292
+ occurredAt: now
293
+ }
294
+ }, now);
295
+ this.db.prepare(`UPDATE scene_presentations SET status = 'withdrawn' WHERE outcome_id IN (
296
+ SELECT o.id FROM scene_outcomes o JOIN scene_runs r ON r.id = o.run_id
297
+ JOIN scene_trigger_intents i ON i.id = r.intent_id JOIN scene_events e ON e.id = i.event_id
298
+ WHERE i.activation_id = ? AND e.subject_id = ? AND (e.account_id = ? OR e.account_id IS NULL))`).run(item.activationId, item.subjectId, item.accountId);
299
+ return true;
300
+ });
301
+ }
302
+ /** Revision changes invalidate only this item's pending work and visible results. */
303
+ updateWorkItem(principal, id, input, now) {
304
+ return this.transaction(() => {
305
+ this.assertPrincipal(principal);
306
+ const row = this.db.prepare(`SELECT w.* FROM scene_work_items w JOIN scene_activations a ON a.id = w.activation_id
307
+ WHERE w.id = ? AND a.owner_id = ? AND a.workspace_id = ?`).get(id, principal.ownerId, principal.workspaceId);
308
+ if (!row) throw new SceneNotFoundError("Scene work item not found");
309
+ if (row.revision !== input.expectedRevision || row.status === "completed") throw new SceneConflictError("Work item changed or completed");
310
+ const status = input.status ?? row.status;
311
+ if (![
312
+ "watching",
313
+ "paused",
314
+ "completed"
315
+ ].includes(String(status))) throw new Error("Invalid work item status");
316
+ if (status === "watching") {
317
+ const activation = this.getActivation(principal, String(row.activation_id));
318
+ if (activation.status !== "active" || activation.scope.kind !== "objects" || !activation.scope.ids.includes(String(row.subject_id)) || !activation.permissions.accountIds.includes(String(row.account_id))) throw new SceneConflictError("Work item is outside the active scene scope");
319
+ }
320
+ if (input.dueAt !== void 0 && (!Number.isSafeInteger(input.dueAt) || input.dueAt <= now)) throw new SceneInputError("Work item deadline must be in the future");
321
+ if (input.status === void 0 && input.dueAt === void 0) throw new SceneInputError("Work item update is empty");
322
+ this.db.prepare("UPDATE scene_work_items SET due_at = ?, status = ?, revision = revision + 1, last_check_reason = NULL WHERE id = ?").run(input.dueAt ?? row.due_at, status, id);
323
+ const matchingIntents = `SELECT i.id FROM scene_trigger_intents i JOIN scene_events e ON e.id = i.event_id
324
+ WHERE i.activation_id = ? AND e.subject_id = ? AND (e.account_id = ? OR e.account_id IS NULL)`;
325
+ this.db.prepare(`UPDATE scene_runs SET status = 'cancelled', reason = 'work_item_changed', lease_owner = NULL, lease_until = NULL
326
+ WHERE intent_id IN (${matchingIntents}) AND status IN ('running', 'retry_wait')`).run(row.activation_id, row.subject_id, row.account_id);
327
+ this.db.prepare(`UPDATE scene_trigger_intents SET status = 'cancelled' WHERE id IN (${matchingIntents}) AND status IN ('pending', 'claimed')`).run(row.activation_id, row.subject_id, row.account_id);
328
+ this.db.prepare(`UPDATE scene_presentations SET status = 'withdrawn' WHERE outcome_id IN (
329
+ SELECT o.id FROM scene_outcomes o JOIN scene_runs r ON r.id = o.run_id WHERE r.intent_id IN (${matchingIntents}))`).run(row.activation_id, row.subject_id, row.account_id);
330
+ return {
331
+ id,
332
+ activationId: String(row.activation_id),
333
+ accountId: String(row.account_id),
334
+ subjectId: String(row.subject_id),
335
+ dueAt: input.dueAt ?? Number(row.due_at),
336
+ revision: Number(row.revision) + 1,
337
+ status
338
+ };
339
+ });
340
+ }
341
+ /** Due dates are durable; restarting cannot generate a second occurrence. */
342
+ enqueueDueWorkItems(now) {
343
+ return this.transaction(() => {
344
+ const rows = this.db.prepare(`SELECT w.*, a.owner_id, a.workspace_id FROM scene_work_items w
345
+ JOIN scene_activations a ON a.id = w.activation_id WHERE w.status = 'watching' AND a.status = 'active'
346
+ AND w.due_at <= ? AND (w.last_triggered_revision IS NULL OR w.last_triggered_revision <> w.revision) AND ${checksAllowed}
347
+ ORDER BY w.due_at, w.id LIMIT 100`).all(now, now / 1e3);
348
+ let accepted = 0;
349
+ for (const row of rows) {
350
+ const activationId = String(row.activation_id);
351
+ const principal = {
352
+ ownerId: String(row.owner_id),
353
+ workspaceId: String(row.workspace_id)
354
+ };
355
+ const activation = this.getActivation(principal, activationId);
356
+ if (now - Number(row.due_at) > 864e5) {
357
+ this.db.prepare("UPDATE scene_work_items SET status = 'paused', last_check_reason = 'deadline_expired' WHERE id = ?").run(row.id);
358
+ continue;
359
+ }
360
+ const trigger = this.getTemplate(activation.templateKey, activation.templateVersion).triggers.find((item) => item.type === "schedule");
361
+ if (!trigger) throw new Error("Work item template needs a schedule trigger");
362
+ const occurrenceKey = `work-item:${row.id}:${row.revision}`;
363
+ this.acceptTrigger(principal, activationId, {
364
+ triggerKey: trigger.id,
365
+ occurrenceKey,
366
+ dueAt: Number(row.due_at),
367
+ event: {
368
+ source: "schedule",
369
+ sourceEventId: occurrenceKey,
370
+ eventType: "scene.work_item.due",
371
+ subjectId: String(row.subject_id),
372
+ accountId: String(row.account_id),
373
+ occurredAt: Number(row.due_at)
374
+ }
375
+ }, now);
376
+ this.db.prepare("UPDATE scene_work_items SET last_triggered_revision = revision, last_check_reason = 'due' WHERE id = ?").run(row.id);
377
+ accepted += 1;
378
+ }
379
+ return accepted;
380
+ });
381
+ }
382
+ transitionActivation(principal, id, revision, status, now = Date.now()) {
383
+ return this.transaction(() => {
384
+ const activation = this.getActivation(principal, id);
385
+ if (activation.revision !== revision || !canTransitionActivation(activation.status, status)) throw new SceneConflictError("Scene activation changed or transition is invalid");
386
+ this.db.prepare("UPDATE scene_activations SET status = ?, revision = revision + 1 WHERE id = ?").run(status, id);
387
+ this.db.prepare("UPDATE scene_trigger_intents SET status = 'cancelled' WHERE activation_id = ? AND status IN ('pending', 'claimed')").run(id);
388
+ this.db.prepare("UPDATE scene_runs SET status = 'cancelled', reason = 'activation_changed', lease_owner = NULL, lease_until = NULL WHERE activation_id = ? AND status IN ('running', 'retry_wait')").run(id);
389
+ if (status === "active") {
390
+ this.db.prepare("UPDATE scene_work_items SET revision = revision + 1, last_check_reason = NULL WHERE activation_id = ? AND status = 'watching'").run(id);
391
+ const schedules = this.db.prepare("SELECT trigger_key, schedule_json FROM scene_schedule_cursors WHERE activation_id = ?").all(id);
392
+ for (const schedule of schedules) this.db.prepare("UPDATE scene_schedule_cursors SET next_due_at = ?, revision = revision + 1 WHERE activation_id = ? AND trigger_key = ?").run(nextSceneScheduleAt(JSON.parse(String(schedule.schedule_json)), now), id, schedule.trigger_key);
393
+ }
394
+ if (status === "needs_setup" || status === "archived") this.db.prepare(`UPDATE scene_presentations SET status = 'withdrawn' WHERE outcome_id IN
395
+ (SELECT o.id FROM scene_outcomes o JOIN scene_runs r ON r.id = o.run_id WHERE r.activation_id = ?)`).run(id);
396
+ return this.getActivation(principal, id);
397
+ });
398
+ }
399
+ setSchedule(principal, activationId, triggerKey, expectedRevision, value, now) {
400
+ const schedule = sceneScheduleSchema.parse(value);
401
+ const nextDueAt = nextSceneScheduleAt(schedule, now);
402
+ if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0) throw new Error("Invalid schedule revision");
403
+ return this.transaction(() => {
404
+ const activation = this.getActivation(principal, activationId);
405
+ if (activation.status !== "active") throw new SceneConflictError("Scene activation is not active");
406
+ if (!this.getTemplate(activation.templateKey, activation.templateVersion).triggers.some((trigger) => trigger.id === triggerKey && trigger.type === "schedule")) throw new SceneInputError("Unknown schedule trigger");
407
+ const previous = this.db.prepare("SELECT revision FROM scene_schedule_cursors WHERE activation_id = ? AND trigger_key = ?").get(activationId, triggerKey);
408
+ if (Number(previous?.revision ?? 0) !== expectedRevision) throw new SceneConflictError("Scene schedule changed");
409
+ const revision = expectedRevision + 1;
410
+ this.db.prepare(`INSERT INTO scene_schedule_cursors VALUES (?, ?, ?, ?, ?)
411
+ ON CONFLICT(activation_id, trigger_key) DO UPDATE SET schedule_json = excluded.schedule_json,
412
+ revision = excluded.revision, next_due_at = excluded.next_due_at`).run(activationId, triggerKey, JSON.stringify(schedule), revision, nextDueAt);
413
+ this.db.prepare(`UPDATE scene_runs SET status = 'cancelled', reason = 'schedule_changed', lease_owner = NULL, lease_until = NULL
414
+ WHERE status IN ('running', 'retry_wait') AND intent_id IN
415
+ (SELECT id FROM scene_trigger_intents WHERE activation_id = ? AND trigger_key = ?)`).run(activationId, triggerKey);
416
+ this.db.prepare("UPDATE scene_trigger_intents SET status = 'cancelled' WHERE activation_id = ? AND trigger_key = ? AND status IN ('pending', 'claimed')").run(activationId, triggerKey);
417
+ return revision;
418
+ });
419
+ }
420
+ /** Coalesces missed occurrences into at most one bounded catch-up per schedule. */
421
+ enqueueDueSchedules(now) {
422
+ if (!Number.isSafeInteger(now) || now < 0) throw new Error("Invalid scene schedule time");
423
+ return this.transaction(() => {
424
+ const rows = this.db.prepare(`SELECT s.*, a.owner_id, a.workspace_id FROM scene_schedule_cursors s
425
+ JOIN scene_activations a ON a.id = s.activation_id WHERE a.status = 'active' AND s.next_due_at <= ? AND ${checksAllowed}
426
+ ORDER BY s.next_due_at, s.activation_id, s.trigger_key LIMIT 100`).all(now, now / 1e3);
427
+ let published = 0;
428
+ for (const row of rows) {
429
+ const principal = {
430
+ ownerId: String(row.owner_id),
431
+ workspaceId: String(row.workspace_id)
432
+ };
433
+ const activation = this.getActivation(principal, String(row.activation_id));
434
+ if (now - Number(row.next_due_at) <= 864e5) {
435
+ const occurrenceKey = sceneContentHash({
436
+ activationId: activation.id,
437
+ activationRevision: activation.revision,
438
+ triggerKey: row.trigger_key,
439
+ scheduleRevision: row.revision,
440
+ dueAt: row.next_due_at
441
+ });
442
+ this.acceptTrigger(principal, activation.id, {
443
+ triggerKey: String(row.trigger_key),
444
+ occurrenceKey,
445
+ dueAt: now,
446
+ event: {
447
+ source: "scene_schedule",
448
+ sourceEventId: occurrenceKey,
449
+ eventType: "scene.schedule.due",
450
+ subjectId: activation.scope.kind === "objects" ? activation.scope.ids[0] : activation.id,
451
+ occurredAt: Number(row.next_due_at),
452
+ ...activation.permissions.accountIds.length === 1 ? { accountId: activation.permissions.accountIds[0] } : {}
453
+ }
454
+ }, now);
455
+ published += 1;
456
+ }
457
+ this.db.prepare("UPDATE scene_schedule_cursors SET next_due_at = ? WHERE activation_id = ? AND trigger_key = ?").run(nextSceneScheduleAt(JSON.parse(String(row.schedule_json)), now), activation.id, row.trigger_key);
458
+ }
459
+ return published;
460
+ });
461
+ }
462
+ /** Called by an authorized producer inside its domain transaction when applicable. */
463
+ acceptTrigger(principal, activationId, input, now) {
464
+ return this.transaction(() => {
465
+ const activation = this.getActivation(principal, activationId);
466
+ if (activation.status !== "active") throw new SceneConflictError("Scene activation is not active");
467
+ const trigger = this.getTemplate(activation.templateKey, activation.templateVersion).triggers.find((item) => item.id === input.triggerKey);
468
+ if (!trigger || trigger.type === "event" && trigger.eventType !== input.event.eventType) throw new SceneConflictError("Scene trigger does not match");
469
+ if (!input.occurrenceKey || !input.event.source || !input.event.sourceEventId || !input.event.subjectId || !input.event.eventType) throw new Error("Missing scene event identity");
470
+ if (input.event.accountId !== void 0 && !activation.permissions.accountIds.includes(input.event.accountId)) throw new Error("Scene event account is not authorized");
471
+ for (const time of [
472
+ now,
473
+ input.dueAt,
474
+ input.event.occurredAt
475
+ ]) if (!Number.isSafeInteger(time) || time < 0) throw new Error("Invalid scene event time");
476
+ this.db.prepare(`INSERT OR IGNORE INTO scene_events VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), principal.ownerId, principal.workspaceId, input.event.source, input.event.sourceEventId, input.event.eventType, input.event.subjectId, input.event.occurredAt, now, input.event.accountId ?? null);
477
+ const event = this.db.prepare("SELECT * FROM scene_events WHERE owner_id = ? AND workspace_id = ? AND source = ? AND source_event_id = ?").get(principal.ownerId, principal.workspaceId, input.event.source, input.event.sourceEventId);
478
+ if (event.event_type !== input.event.eventType || event.subject_id !== input.event.subjectId || event.account_id !== (input.event.accountId ?? null) || Number(event.occurred_at) !== input.event.occurredAt) throw new SceneConflictError("Event identity was reused with different content");
479
+ const previous = this.db.prepare("SELECT * FROM scene_trigger_intents WHERE activation_id = ? AND trigger_key = ? AND occurrence_key = ?").get(activationId, input.triggerKey, input.occurrenceKey);
480
+ if (previous) {
481
+ if (previous.event_id !== event.id) throw new SceneConflictError("Occurrence identity was reused");
482
+ return String(previous.id);
483
+ }
484
+ const id = randomUUID();
485
+ this.db.prepare(`INSERT INTO scene_trigger_intents VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')`).run(id, activationId, activation.revision, input.triggerKey, input.occurrenceKey, event.id, input.dueAt);
486
+ this.db.prepare("INSERT INTO scene_intent_events VALUES (?, ?, ?)").run(id, event.id, now);
487
+ return id;
488
+ });
489
+ }
490
+ acceptManualCheck(principal, activationId, triggerKey, requestId, now) {
491
+ return this.transaction(() => {
492
+ const activation = this.getActivation(principal, activationId);
493
+ if (activation.status !== "active") throw new SceneConflictError("Scene activation is not active");
494
+ const occurrenceKey = sceneContentHash({
495
+ activationId,
496
+ revision: activation.revision,
497
+ requestId
498
+ });
499
+ const previous = this.db.prepare("SELECT id FROM scene_trigger_intents WHERE activation_id = ? AND trigger_key = ? AND occurrence_key = ?").get(activationId, triggerKey, occurrenceKey);
500
+ if (previous) return String(previous.id);
501
+ return this.acceptTrigger(principal, activationId, {
502
+ triggerKey,
503
+ occurrenceKey,
504
+ dueAt: now,
505
+ event: {
506
+ source: "manual",
507
+ sourceEventId: occurrenceKey,
508
+ eventType: "scene.manual.check",
509
+ subjectId: activation.scope.kind === "objects" ? activation.scope.ids[0] : activation.id,
510
+ occurredAt: now,
511
+ ...activation.permissions.accountIds.length === 1 ? { accountId: activation.permissions.accountIds[0] } : {}
512
+ }
513
+ }, now);
514
+ });
515
+ }
516
+ claimNext(worker, now, leaseMs = 12e4) {
517
+ if (!worker.trim() || !Number.isSafeInteger(now) || now < 0 || !Number.isSafeInteger(leaseMs) || leaseMs < 1) throw new Error("Invalid scene lease");
518
+ return this.transaction(() => {
519
+ this.db.prepare(`UPDATE scene_runs SET status = 'failed', reason = 'attempt_limit', lease_owner = NULL, lease_until = NULL
520
+ WHERE status = 'running' AND lease_until <= ? AND attempt >= 3`).run(now);
521
+ this.db.prepare(`UPDATE scene_trigger_intents SET status = 'resolved' WHERE status = 'claimed'
522
+ AND id IN (SELECT intent_id FROM scene_runs WHERE status = 'failed')`).run();
523
+ const retry = this.db.prepare(`SELECT r.* FROM scene_runs r JOIN scene_activations a ON a.id = r.activation_id
524
+ WHERE a.status = 'active' AND a.revision = r.activation_revision AND ${checksAllowed}
525
+ AND (r.attempt < 3 OR (r.status = 'retry_wait' AND r.reason IN ('source_not_ready', 'daily_budget')))
526
+ AND ((r.status = 'running' AND r.lease_until <= ?) OR (r.status = 'retry_wait' AND r.retry_at <= ?))
527
+ ORDER BY r.created_at, r.id LIMIT 1`).get(now / 1e3, now, now);
528
+ if (retry) {
529
+ this.db.prepare(`UPDATE scene_runs SET status = 'running', attempt = attempt + CASE WHEN status = 'retry_wait' AND reason IN ('source_not_ready', 'daily_budget') THEN 0 ELSE 1 END, lease_epoch = lease_epoch + 1,
530
+ lease_owner = ?, lease_until = ?, retry_at = NULL WHERE id = ?`).run(worker, now + leaseMs, retry.id);
531
+ return this.readClaim(String(retry.id));
532
+ }
533
+ const intent = this.db.prepare(`SELECT i.* FROM scene_trigger_intents i JOIN scene_activations a ON a.id = i.activation_id
534
+ WHERE i.status = 'pending' AND i.due_at <= ? AND a.status = 'active' AND a.revision = i.activation_revision AND ${checksAllowed}
535
+ AND NOT EXISTS (SELECT 1 FROM scene_runs r WHERE r.activation_id = a.id AND r.status IN ('running', 'retry_wait'))
536
+ ORDER BY i.due_at, i.id LIMIT 1`).get(now, now / 1e3);
537
+ if (!intent) return null;
538
+ const id = randomUUID();
539
+ this.db.prepare(`INSERT INTO scene_runs (id, intent_id, activation_id, activation_revision, status, attempt, lease_epoch, lease_owner, lease_until, created_at)
540
+ VALUES (?, ?, ?, ?, 'running', 1, 1, ?, ?, ?)`).run(id, intent.id, intent.activation_id, intent.activation_revision, worker, now + leaseMs, now);
541
+ this.db.prepare("UPDATE scene_trigger_intents SET status = 'claimed' WHERE id = ?").run(intent.id);
542
+ return this.readClaim(id);
543
+ });
544
+ }
545
+ renewLease(claim, now, leaseMs) {
546
+ if (!Number.isSafeInteger(leaseMs) || leaseMs < 1) throw new Error("Invalid scene lease");
547
+ return this.db.prepare(`UPDATE scene_runs SET lease_until = ? WHERE id = ? AND status = 'running'
548
+ AND lease_owner = ? AND lease_epoch = ? AND lease_until > ?`).run(now + leaseMs, claim.id, claim.leaseOwner, claim.leaseEpoch, now).changes === 1;
549
+ }
550
+ reserveModelCall(claim, now, limits = {
551
+ activation: 30,
552
+ owner: 100
553
+ }) {
554
+ if (![limits.activation, limits.owner].every((limit) => Number.isInteger(limit) && limit > 0)) throw new Error("Invalid model budget");
555
+ return this.transaction(() => {
556
+ if (!this.isCurrentClaim(claim, now)) return false;
557
+ if (this.db.prepare("SELECT 1 FROM scene_model_reservations WHERE run_id = ? AND lease_epoch = ?").get(claim.id, claim.leaseEpoch)) return false;
558
+ const row = this.db.prepare("SELECT owner_id FROM scene_activations WHERE id = ?").get(claim.activationId);
559
+ const start = Math.floor(now / 864e5) * 864e5;
560
+ const count = this.db.prepare(`SELECT count(*) AS owner_count, COALESCE(sum(activation_id = ?), 0) AS activation_count
561
+ FROM scene_model_reservations WHERE owner_id = ? AND reserved_at >= ? AND reserved_at < ?`).get(claim.activationId, row.owner_id, start, start + 864e5);
562
+ if (Number(count.owner_count) >= limits.owner || Number(count.activation_count) >= limits.activation) return false;
563
+ this.db.prepare("INSERT INTO scene_model_reservations VALUES (?, ?, ?, ?, ?)").run(claim.id, claim.leaseEpoch, row.owner_id, claim.activationId, now);
564
+ return true;
565
+ });
566
+ }
567
+ deferRun(claim, now, reason, retryAt) {
568
+ if (!Number.isSafeInteger(retryAt) || retryAt <= now) throw new Error("Invalid scene retry time");
569
+ return this.transaction(() => {
570
+ if (!this.isCurrentClaim(claim, now)) return "discarded";
571
+ const row = this.db.prepare("SELECT created_at FROM scene_runs WHERE id = ?").get(claim.id);
572
+ if (reason === "source_not_ready" && now - Number(row.created_at) >= 864e5) return this.failRun(claim, now, "source_wait_expired") ? "failed" : "discarded";
573
+ this.db.prepare("UPDATE scene_runs SET status = 'retry_wait', reason = ?, retry_at = ?, lease_owner = NULL, lease_until = NULL WHERE id = ?").run(reason, retryAt, claim.id);
574
+ return "deferred";
575
+ });
576
+ }
577
+ getRunInput(claim, now) {
578
+ if (!this.isCurrentClaim(claim, now)) return null;
579
+ const row = this.db.prepare(`SELECT a.owner_id, a.workspace_id, e.subject_id, e.account_id, e.event_type, e.occurred_at FROM scene_activations a
580
+ JOIN scene_trigger_intents i ON i.activation_id = a.id JOIN scene_events e ON e.id = i.event_id
581
+ WHERE i.id = ? AND a.id = ?`).get(claim.intentId, claim.activationId);
582
+ return {
583
+ activation: this.getActivation({
584
+ ownerId: String(row.owner_id),
585
+ workspaceId: String(row.workspace_id)
586
+ }, claim.activationId),
587
+ subjectId: String(row.subject_id),
588
+ ...row.account_id !== null ? { accountId: String(row.account_id) } : {},
589
+ ...["scene.work_item.due", "scene.schedule.due"].includes(String(row.event_type)) ? { notBefore: Number(row.occurred_at) } : {}
590
+ };
591
+ }
592
+ saveSnapshot(claim, hash, evidenceIds, now) {
593
+ return this.transaction(() => {
594
+ if (!this.isCurrentClaim(claim, now)) return false;
595
+ this.db.prepare("INSERT INTO scene_context_snapshots VALUES (?, ?, ?, ?, ?)").run(claim.id, claim.leaseEpoch, hash, JSON.stringify(evidenceIds), now);
596
+ return true;
597
+ });
598
+ }
599
+ finishReadOnlyRun(claim, result, now) {
600
+ return this.transaction(() => {
601
+ if (!this.isCurrentClaim(claim, now)) return false;
602
+ const row = this.db.prepare(`SELECT t.manifest_json, a.owner_id, a.workspace_id FROM scene_activations a JOIN scene_template_versions t
603
+ ON a.template_key = t.template_key AND a.template_version = t.version WHERE a.id = ?`).get(claim.activationId);
604
+ const template = sceneTemplateSchema.parse(JSON.parse(String(row.manifest_json)));
605
+ if (![
606
+ "no_change",
607
+ "observation",
608
+ "artifact",
609
+ "decision"
610
+ ].includes(result.kind) || !template.allowedOutcomeKinds.includes(result.kind)) throw new Error("Scene result kind is not allowed");
611
+ if (result.kind !== "no_change" && (!result.summary.trim() || result.evidenceIds.length === 0)) throw new Error("Scene result needs summary and evidence");
612
+ const snapshot = this.db.prepare("SELECT content_hash, evidence_ids_json FROM scene_context_snapshots WHERE run_id = ? AND lease_epoch = ?").get(claim.id, claim.leaseEpoch);
613
+ if (snapshot) this.db.prepare(`UPDATE scene_work_items SET observed_fingerprint = ? WHERE activation_id = ? AND status = 'watching'
614
+ AND EXISTS (SELECT 1 FROM scene_trigger_intents i JOIN scene_events e ON e.id = i.event_id
615
+ WHERE i.id = ? AND e.subject_id = scene_work_items.subject_id AND e.account_id = scene_work_items.account_id)`).run(snapshot.content_hash, claim.activationId, claim.intentId);
616
+ const outcomeId = randomUUID();
617
+ this.db.prepare(`UPDATE scene_presentations SET status = 'withdrawn' WHERE outcome_id IN (
618
+ SELECT o.id FROM scene_outcomes o JOIN scene_runs r ON r.id = o.run_id
619
+ JOIN scene_trigger_intents i ON i.id = r.intent_id JOIN scene_events e ON e.id = i.event_id
620
+ JOIN scene_trigger_intents current_intent ON current_intent.id = ?
621
+ JOIN scene_events current_event ON current_event.id = current_intent.event_id
622
+ WHERE r.activation_id = ? AND e.subject_id = current_event.subject_id AND e.account_id IS current_event.account_id
623
+ )`).run(claim.intentId, claim.activationId);
624
+ this.db.prepare("INSERT INTO scene_outcomes VALUES (?, ?, ?, ?, ?)").run(outcomeId, claim.id, result.kind, JSON.stringify(result), now);
625
+ if (result.kind !== "no_change") {
626
+ const presentationId = randomUUID();
627
+ this.db.prepare("INSERT INTO scene_presentations(id, outcome_id, destination, status, created_at) VALUES (?, ?, 'inbox', 'unread', ?)").run(presentationId, outcomeId, now);
628
+ this.db.prepare(`INSERT INTO notification_result_outbox
629
+ (id, owner_id, workspace_id, subject_id, status, attempt, next_attempt_at, created_at, updated_at)
630
+ VALUES (?, ?, ?, ?, 'pending', 0, ?, ?, ?)`).run(presentationId, row.owner_id, row.workspace_id, presentationId, now, now, now);
631
+ }
632
+ const reason = result.kind === "no_change" ? snapshot && JSON.parse(String(snapshot.evidence_ids_json)).length === 0 ? "empty_input" : "no_relevant_change" : null;
633
+ this.db.prepare("UPDATE scene_runs SET status = ?, reason = ?, lease_owner = NULL, lease_until = NULL WHERE id = ?").run(result.kind === "no_change" ? "skipped" : "succeeded", reason, claim.id);
634
+ this.db.prepare("UPDATE scene_trigger_intents SET status = 'resolved' WHERE id = ?").run(claim.intentId);
635
+ return true;
636
+ });
637
+ }
638
+ listInbox(principal, limit = 50, afterId = "", activationId = null) {
639
+ this.assertPrincipal(principal);
640
+ const now = Date.now();
641
+ if (activationId !== null) this.getActivation(principal, activationId);
642
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new Error("Invalid scene inbox limit");
643
+ const cursor = afterId ? this.db.prepare(`SELECT p.created_at FROM scene_presentations p
644
+ JOIN scene_outcomes o ON o.id = p.outcome_id JOIN scene_runs r ON r.id = o.run_id
645
+ JOIN scene_activations a ON a.id = r.activation_id WHERE p.id = ? AND a.owner_id = ? AND a.workspace_id = ?
646
+ AND (? IS NULL OR a.id = ?)`).get(afterId, principal.ownerId, principal.workspaceId, activationId, activationId) : void 0;
647
+ if (afterId && !cursor) throw new SceneNotFoundError("Scene inbox cursor not found");
648
+ return this.db.prepare(`SELECT p.id, p.status, o.id AS outcome_id, o.content_json, a.id AS activation_id
649
+ FROM scene_presentations p JOIN scene_outcomes o ON o.id = p.outcome_id JOIN scene_runs r ON r.id = o.run_id
650
+ JOIN scene_activations a ON a.id = r.activation_id WHERE a.owner_id = ? AND a.workspace_id = ?
651
+ AND (? IS NULL OR a.id = ?)
652
+ AND (p.status IN ('unread', 'read') OR (p.status = 'snoozed' AND p.snoozed_until <= ?))
653
+ AND (p.expires_at IS NULL OR p.expires_at > ?) AND p.withdrawn_at IS NULL
654
+ AND (? IS NULL OR p.created_at < ? OR (p.created_at = ? AND p.id < ?))
655
+ ORDER BY p.created_at DESC, p.id DESC LIMIT ?`).all(principal.ownerId, principal.workspaceId, activationId, activationId, now, now, cursor?.created_at ?? null, cursor?.created_at ?? null, cursor?.created_at ?? null, afterId, limit).map((row) => this.getPresentation(principal, String(row.id)));
656
+ }
657
+ getPresentation(principal, id) {
658
+ this.assertPrincipal(principal);
659
+ const row = this.db.prepare(`SELECT p.*, o.id AS outcome_id, o.content_json, r.activation_id, r.intent_id
660
+ FROM scene_presentations p JOIN scene_outcomes o ON o.id = p.outcome_id
661
+ JOIN scene_runs r ON r.id = o.run_id JOIN scene_activations a ON a.id = r.activation_id
662
+ WHERE p.id = ? AND a.owner_id = ? AND a.workspace_id = ? AND p.withdrawn_at IS NULL AND p.status <> 'withdrawn'`).get(id, principal.ownerId, principal.workspaceId);
663
+ if (!row) throw new SceneNotFoundError("Scene presentation not found");
664
+ const now = Date.now();
665
+ const actionable = (row.status === "unread" || row.status === "read" || row.status === "snoozed" && row.snoozed_until !== null && Number(row.snoozed_until) <= now) && (row.expires_at === null || Number(row.expires_at) > now);
666
+ const source = this.db.prepare(`SELECT m.subject, m.sender, m.thread_id FROM scene_trigger_intents i
667
+ JOIN scene_events e ON e.id = i.event_id JOIN scene_mail_sources m ON m.id = e.subject_id
668
+ AND m.owner_id = e.owner_id AND m.workspace_id = e.workspace_id AND m.account_id = e.account_id WHERE i.id = ?`).get(row.intent_id);
669
+ const providers = this.getActivation(principal, String(row.activation_id)).permissions.contextProviders;
670
+ const sources = source ? [{
671
+ kind: "mail",
672
+ title: String(source.subject),
673
+ sender: String(source.sender),
674
+ href: `https://mail.google.com/mail/#all/${encodeURIComponent(String(source.thread_id))}`
675
+ }] : providers.includes("user_notes") ? [{
676
+ kind: "notes",
677
+ title: "",
678
+ sender: "",
679
+ href: `#/scenes/${row.activation_id}`
680
+ }] : [];
681
+ return {
682
+ createdAt: Number(row.created_at),
683
+ sources,
684
+ sourceHealth: this.getSourceHealth(String(row.activation_id)),
685
+ id: String(row.id),
686
+ activationId: String(row.activation_id),
687
+ outcomeId: String(row.outcome_id),
688
+ content: JSON.parse(String(row.content_json)),
689
+ status: String(row.status),
690
+ readOnly: !actionable
691
+ };
692
+ }
693
+ listDigestResults(principal, digestId, limit = 20, afterId = "") {
694
+ this.assertPrincipal(principal);
695
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new SceneInputError("Invalid digest page");
696
+ if (!this.db.prepare("SELECT 1 FROM notification_digests WHERE id = ? AND owner_id = ? AND workspace_id = ?").get(digestId, principal.ownerId, principal.workspaceId)) throw new SceneNotFoundError("Scene digest not found");
697
+ const rows = this.db.prepare(`SELECT p.id FROM notification_digest_members m
698
+ JOIN scene_presentations p ON p.id = m.subject_id JOIN scene_outcomes o ON o.id = p.outcome_id
699
+ JOIN scene_runs r ON r.id = o.run_id JOIN scene_activations a ON a.id = r.activation_id
700
+ WHERE m.digest_id = ? AND a.owner_id = ? AND a.workspace_id = ?
701
+ AND p.withdrawn_at IS NULL AND p.status <> 'withdrawn' AND p.id > ? ORDER BY p.id LIMIT ?`).all(digestId, principal.ownerId, principal.workspaceId, afterId, limit + 1);
702
+ return {
703
+ outcomes: rows.slice(0, limit).map((row) => this.getPresentation(principal, String(row.id))),
704
+ nextCursor: rows.length > limit ? String(rows[limit - 1].id) : null
705
+ };
706
+ }
707
+ failRun(claim, now, reason, retryAt) {
708
+ return this.transaction(() => {
709
+ if (!this.isCurrentClaim(claim, now)) return false;
710
+ const retry = retryAt !== void 0 && claim.attempt < 3;
711
+ if (retry && (!Number.isSafeInteger(retryAt) || retryAt <= now)) throw new Error("Invalid scene retry time");
712
+ this.db.prepare("UPDATE scene_runs SET status = ?, reason = ?, retry_at = ?, lease_owner = NULL, lease_until = NULL WHERE id = ?").run(retry ? "retry_wait" : "failed", reason, retry ? retryAt : null, claim.id);
713
+ if (!retry) this.db.prepare("UPDATE scene_trigger_intents SET status = 'resolved' WHERE id = ?").run(claim.intentId);
714
+ return true;
715
+ });
716
+ }
717
+ isCurrentClaim(claim, now) {
718
+ return !!this.db.prepare(`SELECT 1 FROM scene_runs r JOIN scene_activations a ON a.id = r.activation_id
719
+ WHERE r.id = ? AND r.intent_id = ? AND r.activation_id = ? AND r.activation_revision = ?
720
+ AND r.status = 'running' AND r.lease_owner = ? AND r.lease_epoch = ? AND r.lease_until > ?
721
+ AND a.status = 'active' AND a.revision = r.activation_revision AND ${checksAllowed}`).get(claim.id, claim.intentId, claim.activationId, claim.activationRevision, claim.leaseOwner, claim.leaseEpoch, now, now / 1e3);
722
+ }
723
+ readClaim(id) {
724
+ const row = this.db.prepare("SELECT * FROM scene_runs WHERE id = ?").get(id);
725
+ return {
726
+ id,
727
+ intentId: String(row.intent_id),
728
+ activationId: String(row.activation_id),
729
+ activationRevision: Number(row.activation_revision),
730
+ leaseOwner: String(row.lease_owner),
731
+ leaseEpoch: Number(row.lease_epoch),
732
+ attempt: Number(row.attempt)
733
+ };
734
+ }
735
+ assertPrincipal(principal) {
736
+ if (!principal.ownerId?.trim() || !principal.workspaceId?.trim()) throw new Error("Scene principal is required");
737
+ }
738
+ };
739
+ //#endregion
740
+ export { SceneConflictError, SceneInputError, SceneNotFoundError, SceneRepository };