@xopcai/xopc 0.0.268 → 0.0.269

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 (207) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-Bhyy6ENz.js → activity-store-DcwCSY6s.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-B9WObxjr.js → agent-avatar-dicebear-D0NBOWC4.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-J1YFNG26.js → agent-defaults-settings-panel-qKCezlBg.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-BMhnZKhP.js +1 -0
  7. package/dist/gateway/static/root/assets/{api-B2y7ZjZk.js → api-eaPO-mdr.js} +1 -1
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-Bbn9mDfL.js → app-management-settings-panel-E3xm2teY.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-Deo5EsIc.js → appearance-settings-CiZOjgls.js} +1 -1
  10. package/dist/gateway/static/root/assets/apps-page-DduJDWGY.js +1 -0
  11. package/dist/gateway/static/root/assets/{archive-plugin-CxJTU8MU.js → archive-plugin-D09yo_Hc.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-C71Cukgp.js → attachment-load-CNYb8Qru.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-page-_J7UZ55Q.js → automations-page-DsrfW5ii.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-workspace-X7hNHCjW.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-CX8fC8Zt.js → binary-plugins-OWlaGqlY.js} +1 -1
  16. package/dist/gateway/static/root/assets/{block-editor-Ci9fjoQL.js → block-editor-CVND3M6r.js} +3 -3
  17. package/dist/gateway/static/root/assets/{browser-automation-inputs-s5I78Iko.js → browser-automation-inputs-D7IzVK_J.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-automations-page-rDSx0ozx.js → browser-automations-page-CQ6cCpIv.js} +2 -2
  19. package/dist/gateway/static/root/assets/browser-settings-page-BZ7nK_pN.js +8 -0
  20. package/dist/gateway/static/root/assets/capabilities-settings-panel-BfKvJO1C.js +7 -0
  21. package/dist/gateway/static/root/assets/channels-settings-D1UDxJuA.js +1 -0
  22. package/dist/gateway/static/root/assets/{channels-status-swr-_OsWr_vS.js → channels-status-swr-D4JrgJKa.js} +1 -1
  23. package/dist/gateway/static/root/assets/{chat-terminal-dock-DMSl8KxS.js → chat-terminal-dock-BUjnhT7C.js} +2 -2
  24. package/dist/gateway/static/root/assets/connectors-page-C2nuqs_O.js +2 -0
  25. package/dist/gateway/static/root/assets/{cron-expression-BByq6Uh8.js → cron-expression-DlyRIyUn.js} +1 -1
  26. package/dist/gateway/static/root/assets/{date-picker-m6JZCUEW.js → date-picker-CU-ij_yE.js} +1 -1
  27. package/dist/gateway/static/root/assets/{dependency-picker-CwCAjbaJ.js → dependency-picker-Z6Nf0Wjq.js} +1 -1
  28. package/dist/gateway/static/root/assets/{desktop-pet-DSmeEREd.js → desktop-pet-C0F5Iqnc.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-settings-MfKceMoe.js → desktop-pet-settings-Bj_W6HL1.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-debug-page-CwvWy26h.js → extension-debug-page-Ba09EnAn.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-page-DLrbVBHM.js → extension-page-C88aGXJs.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-settings-page-BI6C4PM-.js → extension-settings-page-CMyt0A32.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-config-swr-OObCK8jM.js → gateway-config-swr-bjzk_4hn.js} +1 -1
  34. package/dist/gateway/static/root/assets/gateway-settings-ogv8ZbvG.js +1 -0
  35. package/dist/gateway/static/root/assets/{gateway-startup-retry-D_TxKN_A.js → gateway-startup-retry-DrbIzFyB.js} +1 -1
  36. package/dist/gateway/static/root/assets/{heartbeat-settings-CUl832Tb.js → heartbeat-settings-BnrMRKaZ.js} +1 -1
  37. package/dist/gateway/static/root/assets/home-page-CbcJCREd.js +1 -0
  38. package/dist/gateway/static/root/assets/{image-generation-api-fPU9yGsw.js → image-generation-api-CBWU9Ner.js} +1 -1
  39. package/dist/gateway/static/root/assets/index-7PZ6jmhy.css +2 -0
  40. package/dist/gateway/static/root/assets/{index-wKE8Q2FX.js → index-CfyyA9-g.js} +47 -47
  41. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-r4DCS19O.js → keyboard-shortcuts-settings-B8Z7kKQY.js} +1 -1
  42. package/dist/gateway/static/root/assets/local-app-workbench-page-D5XVZnK1.js +2 -0
  43. package/dist/gateway/static/root/assets/{local-apps-page-DkfCRLUh.js → local-apps-page-CLSfzkRp.js} +1 -1
  44. package/dist/gateway/static/root/assets/{locale-store-DFQX9kTl.js → locale-store-BQ2HKTWb.js} +1 -1
  45. package/dist/gateway/static/root/assets/logs-page-CWDU1AGN.js +2 -0
  46. package/dist/gateway/static/root/assets/{management-settings-Cfx4ATTa.js → management-settings-CuFNgQUy.js} +1 -1
  47. package/dist/gateway/static/root/assets/{markdown-view-D-5jHRWg.js → markdown-view-DIcfrLmG.js} +1 -1
  48. package/dist/gateway/static/root/assets/{media-plugins-DOLjWd3y.js → media-plugins-CSv6-xTA.js} +1 -1
  49. package/dist/gateway/static/root/assets/{messages-Dj8QuS6e.js → messages-BRjoarIr.js} +2 -2
  50. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-BGW9joAb.js → mobile-pairing-wizard-C7d_ztgp.js} +1 -1
  51. package/dist/gateway/static/root/assets/note-detail-panel-D5JuBLyt.js +5 -0
  52. package/dist/gateway/static/root/assets/notes-page-C5-ccCi4.js +8 -0
  53. package/dist/gateway/static/root/assets/{page-tabs-_SGXMT2O.js → page-tabs-BfV-PQa5.js} +1 -1
  54. package/dist/gateway/static/root/assets/{preview-open-alternatives-lVetfCWk.js → preview-open-alternatives-DQS7NFpg.js} +1 -1
  55. package/dist/gateway/static/root/assets/proactive-page-fHjPigmx.js +1 -0
  56. package/dist/gateway/static/root/assets/{product-open-page-CIGLPFm7.js → product-open-page-BIhjZLKg.js} +1 -1
  57. package/dist/gateway/static/root/assets/project-detail-page-6v_khvEt.js +1 -0
  58. package/dist/gateway/static/root/assets/projects-page-CCy1aK4V.js +1 -0
  59. package/dist/gateway/static/root/assets/{registry-api-CsZLatKo.js → registry-api-CwveUfe8.js} +1 -1
  60. package/dist/gateway/static/root/assets/remote-access-hub-pkqKRnch.js +1 -0
  61. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BDqVrUEj.js → runtime-tools-settings-panel-DMQ0wpNg.js} +2 -2
  62. package/dist/gateway/static/root/assets/{schema-form-8RQgFJJO.js → schema-form-C91g2PRU.js} +1 -1
  63. package/dist/gateway/static/root/assets/sessions-page-Cr4E4cap.js +1 -0
  64. package/dist/gateway/static/root/assets/settings-advanced-gate-D1JRDRyL.js +1 -0
  65. package/dist/gateway/static/root/assets/{settings-form-section-j4UjGM-f.js → settings-form-section-Cem6L49Y.js} +1 -1
  66. package/dist/gateway/static/root/assets/{settings-page-lMi50B_s.js → settings-page-afDUxqlV.js} +1 -1
  67. package/dist/gateway/static/root/assets/{setup-status-panel-Dx4Fy3rU.js → setup-status-panel-Cc_iP_vv.js} +1 -1
  68. package/dist/gateway/static/root/assets/{share-preview-page-aSyKRH5y.js → share-preview-page-CVEVHksg.js} +1 -1
  69. package/dist/gateway/static/root/assets/shares-settings-BrMECMAB.js +1 -0
  70. package/dist/gateway/static/root/assets/skill-reload-api-DuqIDYjc.js +1 -0
  71. package/dist/gateway/static/root/assets/{skills-page-C2_CAvxW.js → skills-page-CNrq1W6C.js} +1 -1
  72. package/dist/gateway/static/root/assets/{skills-page.utils-B00e4Q30.js → skills-page.utils-D1wGfm22.js} +1 -1
  73. package/dist/gateway/static/root/assets/{system-settings-panel-CulAkrsV.js → system-settings-panel-BLE8PxWE.js} +1 -1
  74. package/dist/gateway/static/root/assets/task-detail-page-m0oXUKl2.js +1 -0
  75. package/dist/gateway/static/root/assets/{theme-store-zRT6vaZc.js → theme-store-BejLVjIT.js} +1 -1
  76. package/dist/gateway/static/root/assets/{tunnel-api-C01xgCWR.js → tunnel-api-Dur80zSQ.js} +1 -1
  77. package/dist/gateway/static/root/assets/{use-autosave-DfSJ3c2p.js → use-autosave-DmgEA8Qb.js} +1 -1
  78. package/dist/gateway/static/root/assets/user-model-page-Dl10D_5q.js +2 -0
  79. package/dist/gateway/static/root/assets/{voice-api-key-field-9v-miN8o.js → voice-api-key-field-sZ0J83SF.js} +1 -1
  80. package/dist/gateway/static/root/assets/{voice-config-api-DpJEu2RQ.js → voice-config-api-BEbz3avx.js} +1 -1
  81. package/dist/gateway/static/root/assets/{work-discovery-overlay-CREYgh-1.js → work-discovery-overlay-BXnAlbmO.js} +1 -1
  82. package/dist/gateway/static/root/assets/workflow-run-setup-panel-BkwFey0a.js +3 -0
  83. package/dist/gateway/static/root/assets/workflows-page-CswgoIFU.js +6 -0
  84. package/dist/gateway/static/root/index.html +10 -10
  85. package/dist/gateway/static/root/notification-sw.js +15 -0
  86. package/dist/package.js +1 -1
  87. package/dist/packages/gateway-contract/src/notifications.js +15 -1
  88. package/dist/packages/gateway-contract/src/proactive.js +104 -0
  89. package/dist/packages/realtime-protocol/src/voice.js +2 -1
  90. package/dist/src/agent/workflow/subagent-runner.d.ts +1 -0
  91. package/dist/src/agent/workflow/subagent-runner.js +2 -2
  92. package/dist/src/cli/commands/connectors.js +1 -1
  93. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  94. package/dist/src/connectors/connected-source-ingestion.js +1 -1
  95. package/dist/src/connectors/connection-recovery-service.js +1 -1
  96. package/dist/src/connectors/health.js +1 -1
  97. package/dist/src/gateway/hono/routes/capabilities.js +1 -1
  98. package/dist/src/gateway/hono/routes/connectors.js +1 -1
  99. package/dist/src/gateway/hono/routes/index.js +0 -2
  100. package/dist/src/gateway/hono/routes/lazy-bundles.js +12 -0
  101. package/dist/src/gateway/hono/routes/mcp.js +1 -1
  102. package/dist/src/gateway/hono/routes/proactive-control.d.ts +3 -0
  103. package/dist/src/gateway/hono/routes/proactive-control.js +161 -0
  104. package/dist/src/gateway/hono/routes/proactive.d.ts +1 -1
  105. package/dist/src/gateway/hono/routes/proactive.js +12 -5
  106. package/dist/src/gateway/hono/routes/voice.js +1 -1
  107. package/dist/src/gateway/security/gateway-scopes.js +5 -0
  108. package/dist/src/gateway/service.d.ts +0 -1
  109. package/dist/src/gateway/service.js +24 -26
  110. package/dist/src/notifications/planner.js +4 -3
  111. package/dist/src/notifications/proactive-channel.d.ts +8 -0
  112. package/dist/src/notifications/proactive-channel.js +57 -0
  113. package/dist/src/notifications/proactive-policy.d.ts +3 -0
  114. package/dist/src/notifications/proactive-policy.js +32 -0
  115. package/dist/src/notifications/service.d.ts +78 -0
  116. package/dist/src/notifications/service.js +71 -13
  117. package/dist/src/notifications/store.d.ts +2 -0
  118. package/dist/src/notifications/store.js +8 -1
  119. package/dist/src/notifications/web-push.d.ts +19 -0
  120. package/dist/src/notifications/web-push.js +182 -0
  121. package/dist/src/proactive/actions/service.js +10 -2
  122. package/dist/src/proactive/actions/workflow.d.ts +15 -0
  123. package/dist/src/proactive/actions/workflow.js +88 -0
  124. package/dist/src/proactive/execution/agent-executor.d.ts +5 -0
  125. package/dist/src/proactive/execution/agent-executor.js +21 -0
  126. package/dist/src/proactive/execution/authorization.d.ts +3 -0
  127. package/dist/src/proactive/execution/authorization.js +23 -0
  128. package/dist/src/proactive/execution/context.js +2 -11
  129. package/dist/src/proactive/execution/insight.d.ts +7 -0
  130. package/dist/src/proactive/execution/insight.js +33 -2
  131. package/dist/src/proactive/execution/repository.d.ts +2 -0
  132. package/dist/src/proactive/execution/repository.js +38 -4
  133. package/dist/src/proactive/execution/types.d.ts +5 -0
  134. package/dist/src/proactive/execution/worker.js +38 -7
  135. package/dist/src/proactive/inbox/cards.d.ts +21 -0
  136. package/dist/src/proactive/inbox/cards.js +237 -0
  137. package/dist/src/proactive/inbox/delivery.d.ts +6 -0
  138. package/dist/src/proactive/inbox/delivery.js +52 -0
  139. package/dist/src/proactive/inbox/digest.d.ts +8 -0
  140. package/dist/src/proactive/inbox/digest.js +112 -0
  141. package/dist/src/proactive/inbox/lifecycle.d.ts +4 -0
  142. package/dist/src/proactive/inbox/lifecycle.js +58 -0
  143. package/dist/src/proactive/inbox/notification-policy.d.ts +1 -0
  144. package/dist/src/proactive/inbox/notification-policy.js +20 -0
  145. package/dist/src/proactive/inbox/repository.d.ts +1 -0
  146. package/dist/src/proactive/inbox/repository.js +68 -11
  147. package/dist/src/proactive/inbox/types.d.ts +9 -1
  148. package/dist/src/proactive/inbox/worker.js +9 -3
  149. package/dist/src/proactive/index.js +1 -1
  150. package/dist/src/proactive/maintenance.js +7 -0
  151. package/dist/src/proactive/metrics.d.ts +10 -0
  152. package/dist/src/proactive/metrics.js +27 -0
  153. package/dist/src/proactive/policy/presence.d.ts +2 -0
  154. package/dist/src/proactive/policy/presence.js +27 -0
  155. package/dist/src/proactive/policy/service.d.ts +53 -0
  156. package/dist/src/proactive/policy/service.js +122 -0
  157. package/dist/src/proactive/routing/matcher.js +3 -2
  158. package/dist/src/proactive/scenarios/control.d.ts +90 -0
  159. package/dist/src/proactive/scenarios/control.js +109 -0
  160. package/dist/src/proactive/scenarios/preview.d.ts +13 -0
  161. package/dist/src/proactive/scenarios/preview.js +81 -0
  162. package/dist/src/proactive/scenarios/prompt-composer.js +2 -2
  163. package/dist/src/proactive/scenarios/repository.js +5 -1
  164. package/dist/src/proactive/temporal/schedule.d.ts +3 -0
  165. package/dist/src/proactive/temporal/schedule.js +67 -0
  166. package/dist/src/proactive/temporal/worker.js +8 -4
  167. package/dist/src/storage/sqlite/migrations/159_proactive_controls.sql +68 -0
  168. package/dist/src/storage/sqlite/migrations/160_proactive_web_push.sql +27 -0
  169. package/dist/src/storage/sqlite/migrations/161_proactive_delivery_and_lifecycle.sql +51 -0
  170. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  171. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  172. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  173. package/dist/src/storage/sqlite/schema.js +1 -1
  174. package/dist/src/tui/tui-startup-resources.js +1 -1
  175. package/dist/src/voice/realtime/runtime.d.ts +2 -1
  176. package/dist/src/voice/realtime/runtime.js +21 -5
  177. package/dist/src/workflows/domain/run.d.ts +1 -0
  178. package/dist/src/workflows/index.js +2 -2
  179. package/dist/src/workflows/service/workflow-run-service.d.ts +3 -0
  180. package/dist/src/workflows/service/workflow-run-service.js +26 -5
  181. package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -0
  182. package/package.json +6 -4
  183. package/dist/gateway/static/root/assets/agents-BJuTIRPD.js +0 -1
  184. package/dist/gateway/static/root/assets/apps-page-Cz4d7ErX.js +0 -1
  185. package/dist/gateway/static/root/assets/automations-workspace-Bn8nwUOL.js +0 -6
  186. package/dist/gateway/static/root/assets/browser-settings-page-B9RA6x4x.js +0 -8
  187. package/dist/gateway/static/root/assets/capabilities-settings-panel-C1GUih8m.js +0 -7
  188. package/dist/gateway/static/root/assets/channels-settings-CEhVLJNa.js +0 -1
  189. package/dist/gateway/static/root/assets/connectors-page-IQAQDSmO.js +0 -2
  190. package/dist/gateway/static/root/assets/gateway-settings-B3H7Vfbh.js +0 -1
  191. package/dist/gateway/static/root/assets/home-page-CT4JL2jm.js +0 -1
  192. package/dist/gateway/static/root/assets/index-CRJrO4GI.css +0 -2
  193. package/dist/gateway/static/root/assets/local-app-workbench-page-qwLuir8A.js +0 -2
  194. package/dist/gateway/static/root/assets/logs-page-DrNaaPWc.js +0 -2
  195. package/dist/gateway/static/root/assets/note-detail-panel-Dbj5OvXE.js +0 -5
  196. package/dist/gateway/static/root/assets/notes-page-5hRB3WXc.js +0 -8
  197. package/dist/gateway/static/root/assets/project-detail-page-CblWLA8k.js +0 -1
  198. package/dist/gateway/static/root/assets/projects-page-CGk-kzqs.js +0 -1
  199. package/dist/gateway/static/root/assets/remote-access-hub-Du8PweyU.js +0 -1
  200. package/dist/gateway/static/root/assets/sessions-page-BFm94dJC.js +0 -1
  201. package/dist/gateway/static/root/assets/settings-advanced-gate-C0H-cToA.js +0 -1
  202. package/dist/gateway/static/root/assets/shares-settings-CqF9s0pl.js +0 -1
  203. package/dist/gateway/static/root/assets/skill-reload-api-BBL_ZUSC.js +0 -1
  204. package/dist/gateway/static/root/assets/task-detail-page-D1FmRFIn.js +0 -1
  205. package/dist/gateway/static/root/assets/user-model-page-BGC_5baJ.js +0 -2
  206. package/dist/gateway/static/root/assets/workflow-run-setup-panel-BfCWdw8F.js +0 -3
  207. package/dist/gateway/static/root/assets/workflows-page-CvmKQkD0.js +0 -6
@@ -0,0 +1,88 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
2
+ import { ProactiveConflict, effectiveProactivePolicy } from "../policy/service.js";
3
+ import { requireSubscription } from "../scenarios/control.js";
4
+ import { getCard } from "../inbox/cards.js";
5
+ import { randomUUID } from "node:crypto";
6
+ import { z } from "zod";
7
+ //#region src/proactive/actions/workflow.ts
8
+ const StartSchema = z.object({
9
+ expectedRevision: z.number().int().positive(),
10
+ retry: z.boolean().default(false)
11
+ }).strict();
12
+ async function prepareCardWorkflow(workspace, cardId, value, agentId, workflows) {
13
+ const input = StartSchema.parse(value);
14
+ const card = getCard(cardId, workspace);
15
+ const policy = effectiveProactivePolicy(card.subscriptionId);
16
+ const sub = requireSubscription(card.subscriptionId, workspace);
17
+ if (!policy.enabled || card.status === "withdrawn" || card.status === "expired") throw new Error("Card is no longer actionable");
18
+ if (card.revision !== input.expectedRevision) throw new ProactiveConflict("Card changed; refresh before acting");
19
+ const definitionId = policy.settings.preparationWorkflowId;
20
+ if (!definitionId) throw new Error("Choose a preparation workflow in the subscription settings first");
21
+ const claim = runSqliteWriteTransaction((db) => {
22
+ const previous = db.prepare("SELECT * FROM proactive_workflow_links WHERE inbox_item_id = ?").get(cardId);
23
+ if (previous?.lease_until && previous.lease_until > Date.now()) throw new ProactiveConflict("Workflow request is already running");
24
+ if (previous?.run_id && !input.retry) return {
25
+ existing: true,
26
+ ...previous
27
+ };
28
+ if (previous && previous.definition_id !== definitionId) throw new Error("Workflow selection changed; open the existing run to recover it");
29
+ const attemptKey = previous?.status === "starting" || previous?.status === "failed" && !previous.run_id ? previous.attempt_key : `proactive-prepare:${cardId}:${randomUUID()}`;
30
+ db.prepare(`INSERT INTO proactive_workflow_links(inbox_item_id, definition_id, agent_id, attempt_key, status, lease_until)
31
+ VALUES (?, ?, ?, ?, 'starting', ?) ON CONFLICT(inbox_item_id) DO UPDATE SET status = 'starting', attempt_key = excluded.attempt_key, lease_until = excluded.lease_until, error = NULL`).run(cardId, definitionId, agentId, attemptKey, Date.now() + 12e4);
32
+ return {
33
+ existing: false,
34
+ run_id: previous?.run_id ?? null,
35
+ agent_id: previous?.agent_id ?? agentId,
36
+ attempt_key: attemptKey
37
+ };
38
+ });
39
+ if (claim.existing) return workflowForCard(cardId, workspace, workflows);
40
+ try {
41
+ if (input.retry && claim.run_id) {
42
+ const previous = await workflows.createRunStore(claim.agent_id).readRunView(claim.run_id);
43
+ if (previous && ![
44
+ "failed",
45
+ "cancelled",
46
+ "interrupted"
47
+ ].includes(previous.run.status)) throw new Error("Only failed or interrupted workflows can be retried");
48
+ }
49
+ if (["withdrawn", "expired"].includes(getCard(cardId, workspace).status) || !effectiveProactivePolicy(card.subscriptionId).enabled) throw new Error("Card permissions changed");
50
+ const result = await workflows.startWorkflowRun({
51
+ agentId: claim.agent_id,
52
+ definitionId,
53
+ preparationOnly: true,
54
+ writebackPolicy: { targets: [] },
55
+ ...sub.scopeKind === "project" ? { projectId: sub.scopeId } : {},
56
+ goal: `Prepare a reviewable checklist for this user-selected card. Do not send messages or publish results.\n${card.title}\n${card.summary}\n${card.recommendation}`,
57
+ input: {
58
+ cardId,
59
+ evidence: card.evidence,
60
+ purpose: "preparation_checklist"
61
+ },
62
+ source: { kind: "webui" },
63
+ concurrency: 1,
64
+ tokenBudget: 5e3,
65
+ idempotencyKey: claim.attempt_key,
66
+ ...input.retry && claim.run_id ? { retryOfRunId: claim.run_id } : {}
67
+ });
68
+ if (result.ok === false) throw new Error(result.message);
69
+ getSqliteDatabase().prepare("UPDATE proactive_workflow_links SET status = 'started', run_id = ?, session_key = ?, lease_until = NULL WHERE inbox_item_id = ? AND attempt_key = ?").run(result.runId, result.sessionKey, cardId, claim.attempt_key);
70
+ return workflowForCard(cardId, workspace, workflows);
71
+ } catch (error) {
72
+ getSqliteDatabase().prepare("UPDATE proactive_workflow_links SET status = 'failed', lease_until = NULL, error = ? WHERE inbox_item_id = ? AND attempt_key = ?").run(error instanceof Error ? error.message.slice(0, 500) : "Workflow failed", cardId, claim.attempt_key);
73
+ throw error;
74
+ }
75
+ }
76
+ async function workflowForCard(cardId, workspace, workflows) {
77
+ if (getCard(cardId, workspace).status === "withdrawn") return null;
78
+ const row = getSqliteDatabase().prepare("SELECT run_id AS runId, agent_id AS agentId, session_key AS sessionKey, status, error FROM proactive_workflow_links WHERE inbox_item_id = ?").get(cardId);
79
+ if (!row) return null;
80
+ if (!row.runId) return row;
81
+ const view = await workflows.createRunStore(row.agentId).readRunView(row.runId);
82
+ return {
83
+ ...row,
84
+ status: view?.run.status ?? row.status
85
+ };
86
+ }
87
+ //#endregion
88
+ export { prepareCardWorkflow, workflowForCard };
@@ -12,5 +12,10 @@ export declare class ReadonlyProactiveAgentExecutor implements ProactiveAgentExe
12
12
  }): Promise<{
13
13
  text: string;
14
14
  modelRef: string;
15
+ usage: {
16
+ inputTokens: number;
17
+ outputTokens: number;
18
+ estimatedCostUsd?: number;
19
+ };
15
20
  }>;
16
21
  }
@@ -56,6 +56,7 @@ var ReadonlyProactiveAgentExecutor = class {
56
56
  this.config = config;
57
57
  }
58
58
  async execute(input) {
59
+ input.signal?.throwIfAborted();
59
60
  const model = resolveModel(getDefaultModelSync(this.config()));
60
61
  let toolCalls = 0;
61
62
  const agent = new Agent({
@@ -91,8 +92,28 @@ var ReadonlyProactiveAgentExecutor = class {
91
92
  const text = assistantText(agent.state.messages);
92
93
  if (!text) throw new Error("Proactive agent returned empty output");
93
94
  if (toolCalls === 0) throw new Error("Proactive agent did not inspect authorized evidence");
95
+ const usage = {
96
+ inputTokens: 0,
97
+ outputTokens: 0,
98
+ estimatedCostUsd: 0
99
+ };
100
+ for (const message of agent.state.messages) {
101
+ if (message.role !== "assistant") continue;
102
+ const reported = message.usage;
103
+ if (reported) {
104
+ usage.inputTokens += reported.input ?? 0;
105
+ usage.outputTokens += reported.output ?? 0;
106
+ usage.estimatedCostUsd += reported.cost?.total ?? 0;
107
+ }
108
+ }
109
+ const priced = Object.values(model.cost).some((value) => value > 0);
94
110
  return {
95
111
  text,
112
+ usage: {
113
+ inputTokens: usage.inputTokens,
114
+ outputTokens: usage.outputTokens,
115
+ ...priced ? { estimatedCostUsd: usage.estimatedCostUsd } : {}
116
+ },
96
117
  modelRef: `${model.provider}/${model.id}`
97
118
  };
98
119
  } finally {
@@ -0,0 +1,3 @@
1
+ /** Recheck connector consent for stored cards and actions, not just model input. */
2
+ export declare function insightSourcesAuthorized(insightId: string): boolean;
3
+ export declare function authorizedConnectedSource(itemId: string, workspaceId: string, scenarioKey: string, agentId?: string | null): import("../../knowledge/types.js").KnowledgeSourceItem;
@@ -0,0 +1,23 @@
1
+ import { getSqliteDatabase } from "../../storage/sqlite/transaction.js";
2
+ import { getKnowledgeSourceItem } from "../../storage/sqlite/knowledge-repository.js";
3
+ import { getConnectorSyncPolicyForConnection } from "../../storage/sqlite/connector-sync-policy-repository.js";
4
+ //#region src/proactive/execution/authorization.ts
5
+ /** Recheck connector consent for stored cards and actions, not just model input. */
6
+ function insightSourcesAuthorized(insightId) {
7
+ return getSqliteDatabase().prepare(`SELECT e.subject_id, e.workspace_id, x.scenario_key
8
+ FROM proactive_insights x JOIN proactive_runs r USING(run_id)
9
+ JOIN proactive_batch_events be ON be.batch_id = r.batch_id JOIN proactive_events e USING(event_id)
10
+ WHERE x.insight_id = ? AND e.type LIKE 'connected_source.%'`).all(insightId).every((row) => Boolean(authorizedConnectedSource(row.subject_id, row.workspace_id, row.scenario_key)));
11
+ }
12
+ function authorizedConnectedSource(itemId, workspaceId, scenarioKey, agentId) {
13
+ const item = getKnowledgeSourceItem(itemId);
14
+ if (!item || item.deletedAt || item.metadata.workspaceId !== workspaceId || item.sensitivity === "secret" || item.sensitivity === "regulated") return null;
15
+ if (agentId && item.metadata.agentId && item.metadata.agentId !== agentId) return null;
16
+ const connectionId = item.metadata.connectionId;
17
+ if (typeof connectionId !== "string") return null;
18
+ if (!getSqliteDatabase().prepare("SELECT 1 FROM connector_connections WHERE id = ? AND status = 'active'").get(connectionId)) return null;
19
+ const policy = getConnectorSyncPolicyForConnection(connectionId);
20
+ return policy?.scanEnabled && policy.proactiveEnabled && (!policy.allowedScenarioKeys.length || policy.allowedScenarioKeys.includes(scenarioKey)) ? item : null;
21
+ }
22
+ //#endregion
23
+ export { authorizedConnectedSource, insightSourcesAuthorized };
@@ -1,9 +1,8 @@
1
1
  import { getSqliteDatabase } from "../../storage/sqlite/transaction.js";
2
- import { getKnowledgeSourceItem } from "../../storage/sqlite/knowledge-repository.js";
3
- import { getConnectorSyncPolicyForConnection } from "../../storage/sqlite/connector-sync-policy-repository.js";
4
2
  import { getAssertionSlot, listUserAssertions } from "../../user-model/repository.js";
5
3
  import "../../user-model/index.js";
6
4
  import { getDiscussionCapture, getLatestDiscussionOrganization } from "../../discussions/repository.js";
5
+ import { authorizedConnectedSource } from "./authorization.js";
7
6
  import { wrapExternalContent } from "../../gateway/security/external-content.js";
8
7
  //#region src/proactive/execution/context.ts
9
8
  const MAX_EVENT_COUNT = 50;
@@ -28,15 +27,7 @@ function emptyContext() {
28
27
  };
29
28
  }
30
29
  function authorizedConnectedSourceItem(event, scenarioKey) {
31
- const item = getKnowledgeSourceItem(event.subject_id);
32
- const connectionId = typeof item?.metadata.connectionId === "string" ? item.metadata.connectionId : void 0;
33
- if (!item || item.deletedAt || !connectionId) return null;
34
- if (item.sensitivity === "secret" || item.sensitivity === "regulated") return null;
35
- if (item.metadata.workspaceId !== event.workspace_id) return null;
36
- if (event.agent_id && item.metadata.agentId && item.metadata.agentId !== event.agent_id) return null;
37
- const policy = getConnectorSyncPolicyForConnection(connectionId);
38
- const scenarioAllowed = !policy?.allowedScenarioKeys.length || policy.allowedScenarioKeys.includes(scenarioKey);
39
- return policy?.scanEnabled && policy.proactiveEnabled && scenarioAllowed ? item : null;
30
+ return authorizedConnectedSource(event.subject_id, event.workspace_id, scenarioKey, event.agent_id);
40
31
  }
41
32
  var EventBatchContextProvider = class {
42
33
  id = "event_batch";
@@ -5,3 +5,10 @@ export declare function isValuableInsight(candidate: InsightCandidate, policy?:
5
5
  minConfidence: number;
6
6
  minScore: number;
7
7
  }): boolean;
8
+ export declare function parseAnalysisResult(raw: string, allowedEvidenceIds: Set<string>): {
9
+ result: 'no_insight';
10
+ reason: string;
11
+ } | {
12
+ result: 'insight';
13
+ candidate: InsightCandidate;
14
+ };
@@ -57,7 +57,7 @@ function parseInsightCandidate(raw, allowedEvidenceIds) {
57
57
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Insight must be a JSON object");
58
58
  const row = value;
59
59
  if (!URGENCY.has(String(row.urgency))) throw new Error("Insight urgency is invalid");
60
- if (typeof row.confidence !== "number" || row.confidence < 0 || row.confidence > 1) throw new Error("Insight confidence is invalid");
60
+ if (typeof row.confidence !== "number" || !Number.isFinite(row.confidence) || row.confidence < 0 || row.confidence > 1) throw new Error("Insight confidence is invalid");
61
61
  if (!Array.isArray(row.evidenceIds) || row.evidenceIds.length === 0 || row.evidenceIds.length > 20) throw new Error("Insight evidenceIds are invalid");
62
62
  const evidenceIds = [...new Set(row.evidenceIds.map(String))];
63
63
  if (evidenceIds.some((id) => !allowedEvidenceIds.has(id))) throw new Error("Insight cites unknown evidence");
@@ -93,5 +93,36 @@ function isValuableInsight(candidate, policy = {
93
93
  }) {
94
94
  return candidate.confidence >= policy.minConfidence && candidate.urgency !== "low" && scoreInsight(candidate) >= policy.minScore;
95
95
  }
96
+ function parseAnalysisResult(raw, allowedEvidenceIds) {
97
+ let value;
98
+ try {
99
+ value = JSON.parse(stripCodeFences(raw));
100
+ } catch {
101
+ throw new Error("Model output is not valid JSON");
102
+ }
103
+ if (value && typeof value === "object" && !Array.isArray(value)) {
104
+ const row = value;
105
+ if (row.result === "no_insight") {
106
+ if (![
107
+ "unchanged",
108
+ "routine",
109
+ "insufficient_evidence",
110
+ "duplicate"
111
+ ].includes(String(row.reason)) || Object.keys(row).some((key) => key !== "result" && key !== "reason")) throw new Error("Invalid no_insight result");
112
+ return {
113
+ result: "no_insight",
114
+ reason: String(row.reason)
115
+ };
116
+ }
117
+ if (row.result === "insight") return {
118
+ result: "insight",
119
+ candidate: parseInsightCandidate(JSON.stringify(row.candidate), allowedEvidenceIds)
120
+ };
121
+ }
122
+ return {
123
+ result: "insight",
124
+ candidate: parseInsightCandidate(raw, allowedEvidenceIds)
125
+ };
126
+ }
96
127
  //#endregion
97
- export { isValuableInsight, parseInsightCandidate, scoreInsight };
128
+ export { isValuableInsight, parseAnalysisResult, parseInsightCandidate, scoreInsight };
@@ -7,6 +7,7 @@ export interface ClaimedRun {
7
7
  scenarioVersion: number;
8
8
  promptRevisionId?: string;
9
9
  attempt: number;
10
+ subscriptionRevision?: number;
10
11
  }
11
12
  export declare function claimNextRun(owner: string, now?: Date, leaseSeconds?: number): ClaimedRun | null;
12
13
  export declare function eventIdsForBatch(batchId: string): string[];
@@ -18,6 +19,7 @@ export declare function finishRun(input: {
18
19
  valueScore?: number;
19
20
  cooldownSeconds?: number;
20
21
  rawOutput: string;
22
+ outcomeReason?: string;
21
23
  modelRef?: string;
22
24
  }, now?: Date): ProactiveInsight | null;
23
25
  export declare function failRun(run: ClaimedRun, error: unknown, retryable: boolean, now?: Date): void;
@@ -1,10 +1,16 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
2
+ import { effectiveProactivePolicy } from "../policy/service.js";
2
3
  import { createHash, randomUUID } from "node:crypto";
3
4
  //#region src/proactive/execution/repository.ts
4
5
  const str = (row, key) => String(row[key]);
5
6
  function claimNextRun(owner, now = /* @__PURE__ */ new Date(), leaseSeconds = 120) {
6
7
  return runSqliteWriteTransaction((db) => {
7
8
  const nowIso = now.toISOString();
9
+ for (const row of db.prepare("SELECT run_id, batch_id, subscription_id FROM proactive_runs WHERE status IN ('retryable', 'running')").all()) if (!effectiveProactivePolicy(str(row, "subscription_id"), now).enabled) {
10
+ db.prepare("UPDATE proactive_runs SET status = 'discarded', outcome_reason = 'disabled', updated_at = ? WHERE run_id = ?").run(nowIso, str(row, "run_id"));
11
+ db.prepare("UPDATE proactive_signal_batches SET status = 'ignored', updated_at = ? WHERE batch_id = ?").run(nowIso, str(row, "batch_id"));
12
+ }
13
+ for (const row of db.prepare("SELECT batch_id, subscription_id FROM proactive_signal_batches WHERE status = 'ready'").all()) if (!effectiveProactivePolicy(str(row, "subscription_id"), now).enabled) db.prepare("UPDATE proactive_signal_batches SET status = 'ignored' WHERE batch_id = ?").run(str(row, "batch_id"));
8
14
  const dayStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())).toISOString();
9
15
  db.prepare(`UPDATE proactive_signal_batches SET status = 'ignored', updated_at = ? WHERE status = 'ready'
10
16
  AND subscription_id IN (SELECT subscription_id FROM proactive_scenario_subscriptions WHERE enabled = 0)`).run(nowIso);
@@ -38,9 +44,14 @@ function claimNextRun(owner, now = /* @__PURE__ */ new Date(), leaseSeconds = 12
38
44
  WHERE b.status = 'ready' ORDER BY b.ready_at LIMIT 1`).get();
39
45
  if (!batch) return null;
40
46
  const id = randomUUID();
47
+ const policy = effectiveProactivePolicy(str(batch, "subscription_id"), now);
41
48
  db.prepare(`INSERT INTO proactive_runs (run_id, batch_id, subscription_id, scenario_key, scenario_version,
42
- prompt_revision_id, status, attempt, lease_owner, lease_expires_at, started_at, updated_at)
43
- VALUES (?, ?, ?, ?, ?, ?, 'running', 1, ?, ?, ?, ?)`).run(id, str(batch, "batch_id"), str(batch, "subscription_id"), str(batch, "scenario_key"), Number(batch.scenario_version), batch.active_prompt_revision_id ? String(batch.active_prompt_revision_id) : null, owner, new Date(now.getTime() + leaseSeconds * 1e3).toISOString(), nowIso, nowIso);
49
+ prompt_revision_id, status, attempt, lease_owner, lease_expires_at, started_at, updated_at, policy_revision, subscription_revision, policy_snapshot_json)
50
+ VALUES (?, ?, ?, ?, ?, ?, 'running', 1, ?, ?, ?, ?, ?, ?, ?)`).run(id, str(batch, "batch_id"), str(batch, "subscription_id"), str(batch, "scenario_key"), Number(batch.scenario_version), batch.active_prompt_revision_id ? String(batch.active_prompt_revision_id) : null, owner, new Date(now.getTime() + leaseSeconds * 1e3).toISOString(), nowIso, nowIso, policy.preferences.revision, policy.settings.revision, JSON.stringify({
51
+ level: policy.level,
52
+ delivery: policy.settings.delivery,
53
+ scanIntervalMinutes: policy.scanIntervalMinutes
54
+ }));
44
55
  db.prepare("UPDATE proactive_signal_batches SET status = 'processing', updated_at = ? WHERE batch_id = ?").run(nowIso, str(batch, "batch_id"));
45
56
  return runFromRow(db.prepare("SELECT * FROM proactive_runs WHERE run_id = ?").get(id));
46
57
  });
@@ -53,6 +64,7 @@ function runFromRow(row) {
53
64
  scenarioKey: str(row, "scenario_key"),
54
65
  scenarioVersion: Number(row.scenario_version),
55
66
  attempt: Number(row.attempt),
67
+ subscriptionRevision: Number(row.subscription_revision),
56
68
  ...row.prompt_revision_id ? { promptRevisionId: str(row, "prompt_revision_id") } : {}
57
69
  };
58
70
  }
@@ -77,6 +89,19 @@ function attachSnapshot(runId, snapshotId) {
77
89
  function finishRun(input, now = /* @__PURE__ */ new Date()) {
78
90
  return runSqliteWriteTransaction((db) => {
79
91
  const nowIso = now.toISOString();
92
+ const current = db.prepare("SELECT status, attempt FROM proactive_runs WHERE run_id = ?").get(input.run.id);
93
+ if (!current || current.status !== "running" || current.attempt !== input.run.attempt) return null;
94
+ const currentPolicy = effectiveProactivePolicy(input.run.subscriptionId, now);
95
+ if (!currentPolicy.enabled) input = {
96
+ ...input,
97
+ candidate: void 0,
98
+ outcomeReason: "disabled"
99
+ };
100
+ else if (input.run.subscriptionRevision !== void 0 && input.run.subscriptionRevision !== currentPolicy.settings.revision) input = {
101
+ ...input,
102
+ candidate: void 0,
103
+ outcomeReason: "policy_changed"
104
+ };
80
105
  const subjectScope = db.prepare(`SELECT DISTINCT e.subject_kind, e.subject_id
81
106
  FROM proactive_batch_events be
82
107
  JOIN proactive_events e ON e.event_id = be.event_id
@@ -90,8 +115,17 @@ function finishRun(input, now = /* @__PURE__ */ new Date()) {
90
115
  const duplicate = fingerprint ? db.prepare(`SELECT 1 FROM proactive_insights
91
116
  WHERE subscription_id = ? AND scenario_key = ? AND content_fingerprint = ? AND created_at >= ? LIMIT 1`).get(input.run.subscriptionId, input.run.scenarioKey, fingerprint, (/* @__PURE__ */ new Date(now.getTime() - (input.cooldownSeconds ?? 10080 * 60) * 1e3)).toISOString()) : void 0;
92
117
  const valuable = Boolean(input.candidate) && !duplicate;
118
+ const reason = duplicate ? "duplicate" : input.outcomeReason;
119
+ const completed = valuable || [
120
+ "unchanged",
121
+ "routine",
122
+ "insufficient_evidence",
123
+ "duplicate",
124
+ "below_threshold"
125
+ ].includes(reason ?? "");
126
+ db.prepare("UPDATE proactive_runs SET outcome_reason = ? WHERE run_id = ?").run(reason ?? null, input.run.id);
93
127
  db.prepare(`UPDATE proactive_runs SET status = ?, raw_output = ?, model_ref = ?, lease_owner = NULL,
94
- lease_expires_at = NULL, completed_at = ?, updated_at = ? WHERE run_id = ?`).run(valuable ? "completed" : "discarded", input.rawOutput.slice(0, 2e4), input.modelRef ?? null, nowIso, nowIso, input.run.id);
128
+ lease_expires_at = NULL, completed_at = ?, updated_at = ? WHERE run_id = ?`).run(completed ? "completed" : "discarded", input.rawOutput.slice(0, 2e4), input.modelRef ?? null, nowIso, nowIso, input.run.id);
95
129
  db.prepare(`UPDATE proactive_signal_batches SET status = ?, updated_at = ? WHERE batch_id = ?`).run(valuable ? "processed" : "ignored", nowIso, input.run.batchId);
96
130
  if (!input.candidate || duplicate) return null;
97
131
  const insight = {
@@ -115,7 +149,7 @@ function failRun(run, error, retryable, now = /* @__PURE__ */ new Date()) {
115
149
  const finalRetryable = retryable && run.attempt < 3;
116
150
  const nowIso = now.toISOString();
117
151
  const nextAttemptAt = finalRetryable ? new Date(now.getTime() + 15e3 * run.attempt).toISOString() : null;
118
- db.prepare(`UPDATE proactive_runs SET status = ?, error_message = ?, lease_owner = NULL, lease_expires_at = NULL, next_attempt_at = ?, updated_at = ? WHERE run_id = ?`).run(finalRetryable ? "retryable" : "failed", String(error instanceof Error ? error.message : error).slice(0, 2e3), nextAttemptAt, nowIso, run.id);
152
+ if (!db.prepare(`UPDATE proactive_runs SET status = ?, error_message = ?, lease_owner = NULL, lease_expires_at = NULL, next_attempt_at = ?, updated_at = ? WHERE run_id = ? AND status = 'running' AND attempt = ?`).run(finalRetryable ? "retryable" : "failed", String(error instanceof Error ? error.message : error).slice(0, 2e3), nextAttemptAt, nowIso, run.id, run.attempt).changes) return;
119
153
  db.prepare(`UPDATE proactive_signal_batches SET status = ?, updated_at = ? WHERE batch_id = ?`).run(finalRetryable ? "failed_retryable" : "failed_permanent", nowIso, run.batchId);
120
154
  });
121
155
  }
@@ -55,6 +55,11 @@ export interface ProactiveAgentExecutor {
55
55
  }): Promise<{
56
56
  text: string;
57
57
  modelRef?: string;
58
+ usage?: {
59
+ inputTokens: number;
60
+ outputTokens: number;
61
+ estimatedCostUsd?: number;
62
+ };
58
63
  }>;
59
64
  }
60
65
  export interface ContextProvider {
@@ -1,11 +1,13 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { getSqliteDatabase } from "../../storage/sqlite/transaction.js";
4
+ import { effectiveProactivePolicy } from "../policy/service.js";
3
5
  import { getPromptRevision, getScenario } from "../scenarios/repository.js";
4
6
  import { composeScenarioPrompt } from "../scenarios/prompt-composer.js";
5
7
  import { markReadyBatches } from "../routing/batch-repository.js";
6
8
  import { ContextProviderRegistry } from "./context.js";
7
9
  import { attachSnapshot, claimNextRun, eventIdsForBatch, failRun, finishRun, saveSnapshot } from "./repository.js";
8
- import { isValuableInsight, parseInsightCandidate, scoreInsight } from "./insight.js";
10
+ import { isValuableInsight, parseAnalysisResult, scoreInsight } from "./insight.js";
9
11
  import { randomUUID } from "node:crypto";
10
12
  //#region src/proactive/execution/worker.ts
11
13
  init_logger();
@@ -42,6 +44,14 @@ var ProactiveWorker = class {
42
44
  try {
43
45
  const scenario = getScenario(run.scenarioKey, run.scenarioVersion);
44
46
  if (!scenario) throw new Error("Pinned scenario version is unavailable");
47
+ if (!effectiveProactivePolicy(run.subscriptionId).enabled) {
48
+ finishRun({
49
+ run,
50
+ rawOutput: "",
51
+ outcomeReason: "disabled"
52
+ });
53
+ return;
54
+ }
45
55
  const revision = run.promptRevisionId ? getPromptRevision(run.promptRevisionId) ?? void 0 : void 0;
46
56
  const eventIds = eventIdsForBatch(run.batchId);
47
57
  const context = await this.contexts.collect(scenario, {
@@ -49,6 +59,14 @@ var ProactiveWorker = class {
49
59
  eventIds,
50
60
  subscriptionId: run.subscriptionId
51
61
  });
62
+ if (context.evidenceIds.length === 0) {
63
+ finishRun({
64
+ run,
65
+ rawOutput: "",
66
+ outcomeReason: "source_unavailable"
67
+ });
68
+ return;
69
+ }
52
70
  const snapshot = saveSnapshot(run.batchId, context.snapshotContent ?? context.content, context.evidenceIds);
53
71
  attachSnapshot(run.id, snapshot.id);
54
72
  const prompt = composeScenarioPrompt({
@@ -61,9 +79,21 @@ var ProactiveWorker = class {
61
79
  userPrompt: prompt.text,
62
80
  authorizedContext: context.content
63
81
  });
64
- const candidate = parseInsightCandidate(output.text, new Set(context.evidenceIds));
65
- const valueScore = scoreInsight(candidate);
66
- const valuable = isValuableInsight(candidate, scenario.valuePolicy);
82
+ if (output.usage) getSqliteDatabase().prepare("UPDATE proactive_runs SET input_tokens = ?, output_tokens = ?, estimated_cost_usd = ? WHERE run_id = ? AND attempt = ?").run(output.usage.inputTokens, output.usage.outputTokens, output.usage.estimatedCostUsd ?? null, run.id, run.attempt);
83
+ const latestContext = await this.contexts.collect(scenario, {
84
+ batchId: run.batchId,
85
+ eventIds,
86
+ subscriptionId: run.subscriptionId
87
+ });
88
+ const revoked = context.evidenceIds.some((id) => !latestContext.evidenceIds.includes(id));
89
+ const result = revoked ? {
90
+ result: "no_insight",
91
+ reason: "source_unavailable"
92
+ } : parseAnalysisResult(output.text, new Set(latestContext.evidenceIds));
93
+ const candidate = result.result === "insight" ? result.candidate : void 0;
94
+ const valueScore = candidate ? scoreInsight(candidate) : 0;
95
+ const enabled = effectiveProactivePolicy(run.subscriptionId).enabled;
96
+ const valuable = enabled && candidate && isValuableInsight(candidate, scenario.valuePolicy);
67
97
  const insight = finishRun({
68
98
  run,
69
99
  ...valuable ? {
@@ -71,16 +101,17 @@ var ProactiveWorker = class {
71
101
  valueScore
72
102
  } : {},
73
103
  cooldownSeconds: scenario.valuePolicy.cooldownSeconds,
74
- rawOutput: output.text,
104
+ outcomeReason: !enabled ? "disabled" : result.result === "no_insight" ? result.reason : valuable ? "insight" : "below_threshold",
105
+ rawOutput: revoked ? "" : output.text,
75
106
  modelRef: output.modelRef
76
107
  });
77
- const disposition = insight ? "created" : valuable ? "duplicate_suppressed" : "value_gate_discarded";
108
+ const disposition = insight ? "created" : valuable ? "duplicate_suppressed" : result.result === "no_insight" ? result.reason : "value_gate_discarded";
78
109
  log.info({
79
110
  runId: run.id,
80
111
  scenarioKey: run.scenarioKey,
81
112
  disposition,
82
113
  insightId: insight?.id
83
- }, insight ? "Proactive insight created" : "Proactive output discarded");
114
+ }, insight ? "Proactive insight created" : "Proactive check completed without a card");
84
115
  } catch (error) {
85
116
  failRun(run, error, !(error instanceof Error && /Pinned scenario/.test(error.message)));
86
117
  log.warn({
@@ -0,0 +1,21 @@
1
+ import { type ProactiveCard } from '@xopcai/gateway-contract';
2
+ export declare function requireCardScope(id: string, workspaceId: string): void;
3
+ export declare function getCard(id: string, workspaceId: string): ProactiveCard;
4
+ export declare function listCards(workspaceId: string, input?: {
5
+ status?: string;
6
+ before?: string;
7
+ limit?: number;
8
+ }): {
9
+ cards: ProactiveCard[];
10
+ nextCursor: string;
11
+ };
12
+ export declare function cardChanges(workspaceId: string, cursor?: number): {
13
+ items: {
14
+ id: string;
15
+ deleted: boolean;
16
+ card?: ProactiveCard;
17
+ }[];
18
+ nextCursor: number;
19
+ hasMore: boolean;
20
+ };
21
+ export declare function performCardAction(id: string, workspaceId: string, value: unknown): ProactiveCard;