@xopcai/xopc 0.0.197 → 0.0.200

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 (244) 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-BwmdJ5pv.js → activity-store-DYVN1hRp.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-C5st7lt5.js → agent-avatar-dicebear-eMarz29u.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-DeLW-454.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-BM1hNzjw.js → api-B8mzbAle.js} +1 -1
  7. package/dist/gateway/static/root/assets/app-management-settings-panel-yfb68wq9.js +1 -0
  8. package/dist/gateway/static/root/assets/appearance-settings-B8SPknFM.js +1 -0
  9. package/dist/gateway/static/root/assets/apps-page-Bmor22Ny.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-qL07mwnY.js → archive-plugin-DmYRiOP3.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CqSobpdi.js → attachment-load-D66JvcPi.js} +1 -1
  12. package/dist/gateway/static/root/assets/automations-page-Q4e8m11h.js +6 -0
  13. package/dist/gateway/static/root/assets/{binary-plugins-DBRPyLYE.js → binary-plugins-ChIsZArT.js} +1 -1
  14. package/dist/gateway/static/root/assets/{block-editor-6wG1LU7t.js → block-editor-Ck-k_d0Z.js} +4 -4
  15. package/dist/gateway/static/root/assets/browser-settings-page--uVL73Sb.js +8 -0
  16. package/dist/gateway/static/root/assets/browser-workflow-inputs-_ndaecsf.js +1 -0
  17. package/dist/gateway/static/root/assets/{browser-workflows-page-BEu3E0et.js → browser-workflows-page-BC5jm1zL.js} +2 -2
  18. package/dist/gateway/static/root/assets/button-CSEbl4zT.js +1 -0
  19. package/dist/gateway/static/root/assets/capabilities-settings-panel-DXpf3QYN.js +7 -0
  20. package/dist/gateway/static/root/assets/{capability-presets-api-BoOzFotX.js → capability-presets-api-Dy1jZJKs.js} +1 -1
  21. package/dist/gateway/static/root/assets/capability-presets-settings-panel-NAjCsrTi.js +3 -0
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-_8IiTxRB.js → channel-recipient-api-D-9qiuio.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-settings-CiWUo7jW.js +1 -0
  24. package/dist/gateway/static/root/assets/channels-status-swr-B4Rbh3bM.js +1 -0
  25. package/dist/gateway/static/root/assets/connectors-api-5xiCRVUS.js +1 -0
  26. package/dist/gateway/static/root/assets/connectors-page-DOwlMseU.js +2 -0
  27. package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
  28. package/dist/gateway/static/root/assets/cron-expression-B3PebXKZ.js +2 -0
  29. package/dist/gateway/static/root/assets/date-picker-C1Vke7Xn.js +1 -0
  30. package/dist/gateway/static/root/assets/dependency-picker-FrQ0tsBC.js +1 -0
  31. package/dist/gateway/static/root/assets/{desktop-pet-CsLR4pWB.js → desktop-pet-CPBAzNJi.js} +1 -1
  32. package/dist/gateway/static/root/assets/desktop-pet-settings-D6s3glDP.js +1 -0
  33. package/dist/gateway/static/root/assets/directory-picker-path-field-CbNQ7QQS.js +1 -0
  34. package/dist/gateway/static/root/assets/dist-BPoKqzeH.js +1 -0
  35. package/dist/gateway/static/root/assets/dist-DY4Af0fY.js +1 -0
  36. package/dist/gateway/static/root/assets/{extension-debug-page-CD73euAM.js → extension-debug-page-dTK14hUv.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-page-DAKjpNwd.js → extension-page-Cpdj1Kip.js} +1 -1
  38. package/dist/gateway/static/root/assets/extension-settings-page-iNHUZGd5.js +1 -0
  39. package/dist/gateway/static/root/assets/external-link-DjqjBmc3.js +1 -0
  40. package/dist/gateway/static/root/assets/form-field-width-DJw_jULe.js +1 -0
  41. package/dist/gateway/static/root/assets/gateway-config-api-SvMvWqMM.js +1 -0
  42. package/dist/gateway/static/root/assets/gateway-config-swr-jB0hAhWO.js +1 -0
  43. package/dist/gateway/static/root/assets/gateway-settings-BilR3dGZ.js +1 -0
  44. package/dist/gateway/static/root/assets/{gateway-startup-retry--2njcDpC.js → gateway-startup-retry-C-fRlHCL.js} +1 -1
  45. package/dist/gateway/static/root/assets/globe-DSSGFWFn.js +1 -0
  46. package/dist/gateway/static/root/assets/heartbeat-settings-zK40dcQ5.js +1 -0
  47. package/dist/gateway/static/root/assets/home-page-NyXy5MLr.js +3 -0
  48. package/dist/gateway/static/root/assets/index-Bp8I_eX4.css +1 -0
  49. package/dist/gateway/static/root/assets/index-CCK8zdlS.js +152 -0
  50. package/dist/gateway/static/root/assets/interaction-BmQogeqz.js +1 -0
  51. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-HMhxgOuD.js → keyboard-shortcuts-settings-nKzsroud.js} +1 -1
  52. package/dist/gateway/static/root/assets/loader-circle-CifKLtgG.js +1 -0
  53. package/dist/gateway/static/root/assets/local-app-workbench-page-Ds3sPXTh.js +2 -0
  54. package/dist/gateway/static/root/assets/local-apps-page-DV1s_FYh.js +1 -0
  55. package/dist/gateway/static/root/assets/{locale-store-Bjvw7jtg.js → locale-store-xULEcFVs.js} +1 -1
  56. package/dist/gateway/static/root/assets/logs-page-Biz4eA02.js +2 -0
  57. package/dist/gateway/static/root/assets/management-settings-D7KjYgHu.js +1 -0
  58. package/dist/gateway/static/root/assets/{markdown-split-DvVBewS4.js → markdown-split-CTYbpkrd.js} +1 -1
  59. package/dist/gateway/static/root/assets/{media-plugins-YVj7NVzD.js → media-plugins-BXYnaiZ6.js} +1 -1
  60. package/dist/gateway/static/root/assets/messages-3qRsLZuX.js +3 -0
  61. package/dist/gateway/static/root/assets/{notes-page-BDzuL2I9.js → notes-page-CWWVXpw5.js} +1 -1
  62. package/dist/gateway/static/root/assets/notes-workbench-DnBkQihH.js +4 -0
  63. package/dist/gateway/static/root/assets/page-tabs-BaEQMYm_.js +1 -0
  64. package/dist/gateway/static/root/assets/popover-select-BzdnTy3h.js +1 -0
  65. package/dist/gateway/static/root/assets/preview-open-alternatives-pimQAkeI.js +1 -0
  66. package/dist/gateway/static/root/assets/{product-open-page-Bwx_CQht.js → product-open-page-BoYSS_YR.js} +1 -1
  67. package/dist/gateway/static/root/assets/project-detail-page-ZO7t1UkB.js +1 -0
  68. package/dist/gateway/static/root/assets/projects-page-By-vq41g.js +1 -0
  69. package/dist/gateway/static/root/assets/remote-access-hub-C0C_Y9d0.js +1 -0
  70. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Fe05Nus6.js → runtime-tools-settings-panel-DT8flaX1.js} +4 -4
  71. package/dist/gateway/static/root/assets/schema-form-DabEP-UI.js +1 -0
  72. package/dist/gateway/static/root/assets/sessions-page-BNlVwUzV.js +1 -0
  73. package/dist/gateway/static/root/assets/settings-advanced-gate-CxYBQDaM.js +1 -0
  74. package/dist/gateway/static/root/assets/settings-form-draft-Dy10ldo8.js +1 -0
  75. package/dist/gateway/static/root/assets/settings-form-section-DCsfZAtC.js +1 -0
  76. package/dist/gateway/static/root/assets/{settings-loading-skeleton-BRE_-7Da.js → settings-loading-skeleton-Dac6Yd0T.js} +1 -1
  77. package/dist/gateway/static/root/assets/{settings-page-C1zxCpqB.js → settings-page-BDoYoEar.js} +1 -1
  78. package/dist/gateway/static/root/assets/settings-page-layout-BRQNel6r.js +1 -0
  79. package/dist/gateway/static/root/assets/settings-shell-dialog-layer-BU3UuiJr.js +1 -0
  80. package/dist/gateway/static/root/assets/setup-status-panel-DWijJ1rw.js +1 -0
  81. package/dist/gateway/static/root/assets/share-preview-page-ie9I2_7h.js +2 -0
  82. package/dist/gateway/static/root/assets/shares-settings-DU9VrJLf.js +1 -0
  83. package/dist/gateway/static/root/assets/skill-reload-api-C5RaIvoE.js +1 -0
  84. package/dist/gateway/static/root/assets/skills-page-N0YhaR5S.js +2 -0
  85. package/dist/gateway/static/root/assets/{skills-page.utils-CYwFWTso.js → skills-page.utils-C639DLXr.js} +1 -1
  86. package/dist/gateway/static/root/assets/system-settings-panel-CpshpQtC.js +1 -0
  87. package/dist/gateway/static/root/assets/task-detail-page-DsTocYkK.js +1 -0
  88. package/dist/gateway/static/root/assets/{theme-store-DtxpMC5S.js → theme-store-CHQRiuCF.js} +1 -1
  89. package/dist/gateway/static/root/assets/tool-activity-R8V9l6nk.js +1 -0
  90. package/dist/gateway/static/root/assets/typed-models-lib-q5-Vfqd8.js +1 -0
  91. package/dist/gateway/static/root/assets/use-autosave-ms6rDy59.js +1 -0
  92. package/dist/gateway/static/root/assets/user-context-page-BUxIemwy.js +1 -0
  93. package/dist/gateway/static/root/assets/voice-api-key-field-CObrQWh2.js +1 -0
  94. package/dist/gateway/static/root/assets/work-discovery-overlay-BBxrnEFt.js +1 -0
  95. package/dist/gateway/static/root/assets/work-discovery-page-DN3l-HW3.js +1 -0
  96. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DqIb1oBB.js → workflow-run-setup-panel-BxXhe3IV.js} +2 -2
  97. package/dist/gateway/static/root/assets/workflows-page-BCHkjd1i.js +5 -0
  98. package/dist/gateway/static/root/assets/working-directory-picker-modal-D-IgcHPp.js +1 -0
  99. package/dist/gateway/static/root/index.html +23 -8
  100. package/dist/package.js +1 -1
  101. package/dist/packages/gateway-contract/src/tasks.js +103 -9
  102. package/dist/src/agent/agent-manager.d.ts +1 -0
  103. package/dist/src/agent/agent-manager.js +1 -0
  104. package/dist/src/agent/context/execution-scope.js +3 -1
  105. package/dist/src/agent/service.js +1 -0
  106. package/dist/src/agent/service.types.d.ts +1 -0
  107. package/dist/src/agent/tasks/task-judge-service.d.ts +1 -1
  108. package/dist/src/agent/tasks/task-judge-service.js +3 -2
  109. package/dist/src/agent/tools/factory.d.ts +2 -0
  110. package/dist/src/agent/tools/factory.js +2 -1
  111. package/dist/src/agent/tools/xopc-use-tool.d.ts +1 -0
  112. package/dist/src/agent/tools/xopc-use-tool.js +69 -9
  113. package/dist/src/automations/domain/validation.d.ts +40 -12
  114. package/dist/src/automations/draft/automation-draft-validator.d.ts +4 -4
  115. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  116. package/dist/src/config/rules.js +2 -2
  117. package/dist/src/gateway/hono/app.d.ts +2 -0
  118. package/dist/src/gateway/hono/app.js +10 -3
  119. package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +4 -0
  120. package/dist/src/gateway/hono/middleware/strict-rate-limit.js +8 -1
  121. package/dist/src/gateway/hono/routes/agent-stream.js +2 -74
  122. package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -1
  123. package/dist/src/gateway/hono/routes/config.js +9 -0
  124. package/dist/src/gateway/hono/routes/deps.d.ts +1 -0
  125. package/dist/src/gateway/hono/routes/projects.js +55 -0
  126. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +62 -0
  127. package/dist/src/gateway/hono/routes/session-input-handler.js +82 -0
  128. package/dist/src/gateway/hono/routes/tasks.js +272 -20
  129. package/dist/src/gateway/host.d.ts +9 -1
  130. package/dist/src/gateway/host.js +22 -5
  131. package/dist/src/gateway/rate-limit/buckets.d.ts +3 -0
  132. package/dist/src/gateway/rate-limit/buckets.js +12 -0
  133. package/dist/src/gateway/runtime-config.js +6 -5
  134. package/dist/src/gateway/security/audit.js +0 -7
  135. package/dist/src/gateway/server.js +4 -1
  136. package/dist/src/gateway/service/task-conversation-query-service.d.ts +58 -0
  137. package/dist/src/gateway/service/task-conversation-query-service.js +84 -0
  138. package/dist/src/gateway/service.d.ts +11 -0
  139. package/dist/src/gateway/service.js +75 -38
  140. package/dist/src/proactive/actions/service.d.ts +2 -0
  141. package/dist/src/proactive/actions/service.js +80 -0
  142. package/dist/src/proactive/execution/insight.js +20 -0
  143. package/dist/src/proactive/execution/repository.js +8 -2
  144. package/dist/src/proactive/execution/types.d.ts +14 -0
  145. package/dist/src/proactive/inbox/repository.js +59 -9
  146. package/dist/src/proactive/inbox/service.d.ts +2 -2
  147. package/dist/src/proactive/inbox/service.js +7 -2
  148. package/dist/src/proactive/inbox/types.d.ts +14 -0
  149. package/dist/src/proactive/inbox/worker.js +2 -0
  150. package/dist/src/proactive/scenarios/prompt-composer.js +2 -2
  151. package/dist/src/projects/project-agent.js +1 -1
  152. package/dist/src/storage/sqlite/migrations/118_task_board_rank.sql +6 -0
  153. package/dist/src/storage/sqlite/migrations/119_proactive_disposition.sql +13 -0
  154. package/dist/src/storage/sqlite/migrations/120_task_conversation.sql +112 -0
  155. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  156. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  157. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  158. package/dist/src/storage/sqlite/schema.js +1 -1
  159. package/dist/src/tasks/execution-context.js +8 -4
  160. package/dist/src/tasks/home-query-service.d.ts +5 -0
  161. package/dist/src/tasks/home-query-service.js +7 -2
  162. package/dist/src/tasks/index.d.ts +3 -0
  163. package/dist/src/tasks/index.js +6 -3
  164. package/dist/src/tasks/project-monitoring-service.d.ts +9 -0
  165. package/dist/src/tasks/project-monitoring-service.js +41 -3
  166. package/dist/src/tasks/project-operating-view-service.d.ts +1 -2
  167. package/dist/src/tasks/project-operating-view-service.js +26 -23
  168. package/dist/src/tasks/task-application-service.js +107 -21
  169. package/dist/src/tasks/task-change-events.d.ts +13 -0
  170. package/dist/src/tasks/task-change-events.js +29 -0
  171. package/dist/src/tasks/task-context-assembler.js +9 -4
  172. package/dist/src/tasks/task-context-repository.d.ts +0 -6
  173. package/dist/src/tasks/task-context-repository.js +0 -4
  174. package/dist/src/tasks/task-conversation-repository.d.ts +71 -0
  175. package/dist/src/tasks/task-conversation-repository.js +194 -0
  176. package/dist/src/tasks/task-handoff-service.d.ts +31 -0
  177. package/dist/src/tasks/task-handoff-service.js +143 -0
  178. package/dist/src/tasks/task-read-model-projector.js +3 -6
  179. package/dist/src/tasks/task-repository.d.ts +7 -2
  180. package/dist/src/tasks/task-repository.js +31 -15
  181. package/dist/src/tasks/task-run-coordinator.js +3 -1
  182. package/dist/src/tunnel/enable-lan-pairing.js +15 -26
  183. package/dist/src/tunnel/pair-context.js +1 -1
  184. package/dist/src/tunnel/pair-url.js +1 -1
  185. package/dist/src/tunnel/tunnel-qr.d.ts +0 -7
  186. package/dist/src/tunnel/tunnel-qr.js +2 -19
  187. package/dist/src/workflows/service/workflow-run-service.js +4 -2
  188. package/package.json +5 -3
  189. package/dist/gateway/static/root/assets/agents-Baxfj44y.js +0 -68
  190. package/dist/gateway/static/root/assets/app-management-settings-panel-S8LhoD6G.js +0 -1
  191. package/dist/gateway/static/root/assets/appearance-settings-D8Cd5vDP.js +0 -1
  192. package/dist/gateway/static/root/assets/apps-page-0uCxKyE5.js +0 -1
  193. package/dist/gateway/static/root/assets/automations-page-XyDat125.js +0 -6
  194. package/dist/gateway/static/root/assets/browser-settings-page-CrYNMR4w.js +0 -8
  195. package/dist/gateway/static/root/assets/browser-workflow-inputs-Abil4puY.js +0 -1
  196. package/dist/gateway/static/root/assets/capabilities-settings-panel-CvMcDhGf.js +0 -7
  197. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CHJDY-Ih.js +0 -3
  198. package/dist/gateway/static/root/assets/channels-settings-Ds9BJbcY.js +0 -1
  199. package/dist/gateway/static/root/assets/channels-status-swr-DPaJSYX_.js +0 -1
  200. package/dist/gateway/static/root/assets/connectors-api-CgWjgPSI.js +0 -1
  201. package/dist/gateway/static/root/assets/connectors-page-NUs-Zf7F.js +0 -2
  202. package/dist/gateway/static/root/assets/cron-expression-ZdDBAoqA.js +0 -2
  203. package/dist/gateway/static/root/assets/dependency-picker-BGeB1jpn.js +0 -1
  204. package/dist/gateway/static/root/assets/desktop-pet-settings-CCjT1bdo.js +0 -1
  205. package/dist/gateway/static/root/assets/directory-picker-path-field-C-Te1QN3.js +0 -1
  206. package/dist/gateway/static/root/assets/extension-settings-page-BLSNdlgd.js +0 -1
  207. package/dist/gateway/static/root/assets/gateway-config-api-CQrf0k3D.js +0 -1
  208. package/dist/gateway/static/root/assets/gateway-settings-CvgH8IJI.js +0 -1
  209. package/dist/gateway/static/root/assets/heartbeat-settings-SPpnqbjl.js +0 -1
  210. package/dist/gateway/static/root/assets/home-page-BFoD6047.js +0 -3
  211. package/dist/gateway/static/root/assets/index-84ys9u-K.js +0 -152
  212. package/dist/gateway/static/root/assets/index-D-srnucP.css +0 -1
  213. package/dist/gateway/static/root/assets/local-app-workbench-page-D5uL2_Fd.js +0 -2
  214. package/dist/gateway/static/root/assets/local-apps-page-DIWkFa3g.js +0 -1
  215. package/dist/gateway/static/root/assets/logs-page--6v8qq6y.js +0 -2
  216. package/dist/gateway/static/root/assets/management-settings-DtEuS-kR.js +0 -1
  217. package/dist/gateway/static/root/assets/messages-DQ5X6cXp.js +0 -3
  218. package/dist/gateway/static/root/assets/notes-workbench-IdjuNXvH.js +0 -4
  219. package/dist/gateway/static/root/assets/page-tabs-7bSEWfSX.js +0 -1
  220. package/dist/gateway/static/root/assets/preview-open-alternatives-tgLbHgcV.js +0 -1
  221. package/dist/gateway/static/root/assets/project-detail-page-DnJXSuM4.js +0 -1
  222. package/dist/gateway/static/root/assets/projects-page-DckZKlBI.js +0 -1
  223. package/dist/gateway/static/root/assets/remote-access-hub-CfbUHBUH.js +0 -1
  224. package/dist/gateway/static/root/assets/schema-form-CFfyC4CB.js +0 -1
  225. package/dist/gateway/static/root/assets/sessions-page-BCfCh4My.js +0 -1
  226. package/dist/gateway/static/root/assets/settings-advanced-gate-Cbi4FvzO.js +0 -1
  227. package/dist/gateway/static/root/assets/settings-form-section-gEnEYT_3.js +0 -1
  228. package/dist/gateway/static/root/assets/settings-page-layout-CRpXF_05.js +0 -1
  229. package/dist/gateway/static/root/assets/setup-status-panel-BcHdpmFf.js +0 -1
  230. package/dist/gateway/static/root/assets/share-preview-page-DU8L68A5.js +0 -2
  231. package/dist/gateway/static/root/assets/shares-settings-kjTqH9yY.js +0 -1
  232. package/dist/gateway/static/root/assets/skill-reload-api-QAGpwzIM.js +0 -1
  233. package/dist/gateway/static/root/assets/skills-page-lE1-YRdK.js +0 -2
  234. package/dist/gateway/static/root/assets/system-settings-panel-CVDsiPKn.js +0 -1
  235. package/dist/gateway/static/root/assets/task-detail-page-DsM13-cy.js +0 -1
  236. package/dist/gateway/static/root/assets/typed-models-lib-DelyYgoc.js +0 -1
  237. package/dist/gateway/static/root/assets/use-autosave-C50Mq5yQ.js +0 -1
  238. package/dist/gateway/static/root/assets/user-context-page-D-VEOt2A.js +0 -1
  239. package/dist/gateway/static/root/assets/voice-api-key-field-DYfgTe3Y.js +0 -1
  240. package/dist/gateway/static/root/assets/work-discovery-overlay-DPbujsp3.js +0 -1
  241. package/dist/gateway/static/root/assets/work-discovery-page-D77opady.js +0 -1
  242. package/dist/gateway/static/root/assets/workflows-page-B9-PLmlX.js +0 -5
  243. package/dist/src/gateway/ensure-network-cors.d.ts +0 -3
  244. package/dist/src/gateway/ensure-network-cors.js +0 -23
@@ -1,5 +1,6 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { executePendingProactiveActions } from "../actions/service.js";
3
4
  import { claimDelivery, finishDelivery, projectInsightsToInbox, recoverExpiredDeliveries, wakeSnoozedItems } from "./repository.js";
4
5
  //#region src/proactive/inbox/worker.ts
5
6
  init_logger();
@@ -28,6 +29,7 @@ var ProactiveInboxWorker = class {
28
29
  this.running = true;
29
30
  try {
30
31
  projectInsightsToInbox();
32
+ executePendingProactiveActions();
31
33
  wakeSnoozedItems();
32
34
  recoverExpiredDeliveries();
33
35
  const claim = claimDelivery();
@@ -5,8 +5,8 @@ Use only supplied evidence identifiers. Separate observations from inferences.
5
5
  Do not invent dates, owners, dependencies, impact, or actions.
6
6
  Return only the protected output schema.`;
7
7
  const OUTPUT_CONTRACT = `Return JSON only with these fields:
8
- {"title":"...","summary":"...","whyNow":"...","impact":"...","workDone":"What you investigated or ruled out","recommendation":"...","decision":{"question":"The exact user decision","options":[{"id":"stable-id","label":"short choice","consequence":"likely consequence"}]},"urgency":"low|medium|high|critical","confidence":0.0,"evidenceIds":["event-id"]}
9
- Use decision only when the user must choose; otherwise set decision to null. Provide 2-3 mutually exclusive options. If evidence does not support a timely and actionable insight, return the same shape with urgency "low" and confidence below 0.65. Never add unknown fields.`;
8
+ {"title":"...","summary":"...","whyNow":"...","impact":"...","workDone":"What you investigated or ruled out","recommendation":"...","decision":{"question":"The exact user decision","options":[{"id":"stable-id","label":"short choice","consequence":"likely consequence"}]},"proposedAction":{"id":"create_project_task","risk":"low","rationale":"why this is safe and useful","input":{"title":"...","objective":"..."}},"urgency":"low|medium|high|critical","confidence":0.0,"evidenceIds":["event-id"]}
9
+ Use proposedAction only when the evidence clearly supports creating a follow-up task in the current project; otherwise set it to null. A proposed action must include exactly two decision options in this order: approve, reject. Use decision without proposedAction only when the user must choose; otherwise set decision to null. Provide 2-3 mutually exclusive options. If evidence does not support a timely and actionable insight, return the same shape with urgency "low" and confidence below 0.65. Never add unknown fields.`;
10
10
  function composeScenarioPrompt(input) {
11
11
  const parts = {
12
12
  platformSafety: PLATFORM_SAFETY,
@@ -8,7 +8,7 @@ function normalizeAgentId(value) {
8
8
  function resolveProjectAgentId(input) {
9
9
  const fallback = getDefaultAgentId(input.config);
10
10
  const explicitAgentId = normalizeAgentId(input.explicitAgentId);
11
- if (explicitAgentId) return agentExists(explicitAgentId, input.config) ? explicitAgentId : fallback;
11
+ if (explicitAgentId && agentExists(explicitAgentId, input.config)) return explicitAgentId;
12
12
  const projectId = input.projectId?.trim();
13
13
  const projectAgentId = projectId ? normalizeAgentId(input.projects.get(projectId)?.defaultAgentId) : void 0;
14
14
  if (projectAgentId && agentExists(projectAgentId, input.config)) return projectAgentId;
@@ -0,0 +1,6 @@
1
+ ALTER TABLE tasks ADD COLUMN board_rank INTEGER NOT NULL DEFAULT 0;
2
+
3
+ UPDATE tasks SET board_rank = created_at;
4
+
5
+ CREATE INDEX idx_tasks_project_phase_rank
6
+ ON tasks(project_id, phase, board_rank, created_at);
@@ -0,0 +1,13 @@
1
+ ALTER TABLE proactive_insights ADD COLUMN proposed_action_json TEXT;
2
+ ALTER TABLE proactive_insights ADD COLUMN disposition TEXT
3
+ CHECK (disposition IN ('record_silently', 'show_in_work', 'request_approval', 'auto_execute'));
4
+ ALTER TABLE proactive_insights ADD COLUMN disposition_reason TEXT;
5
+ ALTER TABLE proactive_insights ADD COLUMN action_status TEXT
6
+ CHECK (action_status IN ('not_authorized', 'approval_required', 'pending', 'executing', 'completed', 'rejected', 'failed'));
7
+ ALTER TABLE proactive_insights ADD COLUMN action_result_json TEXT;
8
+ ALTER TABLE proactive_insights ADD COLUMN action_error TEXT;
9
+ ALTER TABLE proactive_insights ADD COLUMN disposition_at TEXT;
10
+ ALTER TABLE proactive_insights ADD COLUMN action_updated_at TEXT;
11
+
12
+ CREATE INDEX idx_proactive_insights_action_status
13
+ ON proactive_insights(action_status, created_at);
@@ -0,0 +1,112 @@
1
+ ALTER TABLE task_sessions ADD COLUMN agent_id TEXT;
2
+ ALTER TABLE task_sessions ADD COLUMN run_id TEXT;
3
+ ALTER TABLE task_sessions ADD COLUMN assignment_epoch INTEGER NOT NULL DEFAULT 0;
4
+ ALTER TABLE task_sessions ADD COLUMN status TEXT NOT NULL DEFAULT 'completed'
5
+ CHECK (status IN ('active', 'completed', 'superseded', 'failed'));
6
+ ALTER TABLE task_sessions ADD COLUMN started_at INTEGER;
7
+ ALTER TABLE task_sessions ADD COLUMN ended_at INTEGER;
8
+
9
+ UPDATE task_sessions
10
+ SET agent_id = (
11
+ SELECT sessions.agent_id FROM sessions WHERE sessions.session_key = task_sessions.session_key
12
+ ), started_at = created_at;
13
+
14
+ UPDATE task_sessions
15
+ SET run_id = (
16
+ SELECT task_runs.run_id
17
+ FROM task_runs
18
+ WHERE task_runs.task_id = task_sessions.task_id
19
+ AND task_runs.session_key = task_sessions.session_key
20
+ ORDER BY task_runs.queued_at DESC
21
+ LIMIT 1
22
+ )
23
+ WHERE role = 'execution';
24
+
25
+ UPDATE task_sessions
26
+ SET status = 'superseded', ended_at = created_at
27
+ WHERE role = 'execution';
28
+
29
+ DELETE FROM task_sessions
30
+ WHERE role = 'execution'
31
+ AND session_key IS NOT NULL
32
+ AND task_session_id NOT IN (
33
+ SELECT canonical.task_session_id
34
+ FROM task_sessions AS canonical
35
+ WHERE canonical.role = 'execution'
36
+ AND canonical.session_key IS NOT NULL
37
+ AND canonical.task_session_id = (
38
+ SELECT candidate.task_session_id
39
+ FROM task_sessions AS candidate
40
+ WHERE candidate.role = 'execution'
41
+ AND candidate.session_key = canonical.session_key
42
+ ORDER BY candidate.created_at DESC, candidate.task_session_id DESC
43
+ LIMIT 1
44
+ )
45
+ );
46
+
47
+ UPDATE task_sessions
48
+ SET status = 'active', ended_at = NULL, assignment_epoch = 1
49
+ WHERE task_session_id IN (
50
+ SELECT latest.task_session_id FROM task_sessions AS latest
51
+ WHERE latest.role = 'execution'
52
+ AND latest.task_session_id = (
53
+ SELECT candidate.task_session_id
54
+ FROM task_sessions AS candidate
55
+ WHERE candidate.task_id = latest.task_id AND candidate.role = 'execution'
56
+ ORDER BY candidate.created_at DESC, candidate.task_session_id DESC
57
+ LIMIT 1
58
+ )
59
+ );
60
+
61
+ CREATE UNIQUE INDEX idx_task_sessions_active_execution
62
+ ON task_sessions(task_id)
63
+ WHERE role = 'execution' AND status = 'active';
64
+ CREATE UNIQUE INDEX idx_task_sessions_execution_owner
65
+ ON task_sessions(session_key)
66
+ WHERE role = 'execution' AND session_key IS NOT NULL;
67
+
68
+ CREATE TABLE task_conversation_state (
69
+ task_id TEXT PRIMARY KEY,
70
+ active_session_key TEXT,
71
+ current_executor_agent_id TEXT,
72
+ assignment_epoch INTEGER NOT NULL DEFAULT 0,
73
+ status TEXT NOT NULL DEFAULT 'idle'
74
+ CHECK (status IN ('idle', 'active')),
75
+ updated_at INTEGER NOT NULL,
76
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id) ON DELETE CASCADE,
77
+ FOREIGN KEY (active_session_key) REFERENCES sessions(session_key) ON DELETE SET NULL
78
+ );
79
+
80
+ INSERT INTO task_conversation_state (
81
+ task_id, active_session_key, current_executor_agent_id,
82
+ assignment_epoch, status, updated_at
83
+ )
84
+ SELECT
85
+ tasks.task_id,
86
+ active.session_key,
87
+ COALESCE(active.agent_id, tasks.delegate_agent_id, tasks.owner_id),
88
+ COALESCE(active.assignment_epoch, 0),
89
+ CASE WHEN active.session_key IS NULL THEN 'idle' ELSE 'active' END,
90
+ tasks.updated_at
91
+ FROM tasks
92
+ LEFT JOIN task_sessions AS active
93
+ ON active.task_id = tasks.task_id
94
+ AND active.role = 'execution'
95
+ AND active.status = 'active';
96
+
97
+ CREATE TABLE task_handoff_snapshots (
98
+ snapshot_id TEXT PRIMARY KEY,
99
+ task_id TEXT NOT NULL,
100
+ from_session_key TEXT,
101
+ to_session_key TEXT NOT NULL,
102
+ from_agent_id TEXT,
103
+ to_agent_id TEXT NOT NULL,
104
+ assignment_epoch INTEGER NOT NULL,
105
+ payload_json TEXT NOT NULL,
106
+ created_at INTEGER NOT NULL,
107
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id) ON DELETE CASCADE,
108
+ FOREIGN KEY (from_session_key) REFERENCES sessions(session_key) ON DELETE SET NULL,
109
+ FOREIGN KEY (to_session_key) REFERENCES sessions(session_key) ON DELETE CASCADE
110
+ );
111
+ CREATE INDEX idx_task_handoffs_task
112
+ ON task_handoff_snapshots(task_id, assignment_epoch DESC);
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
3
3
  /** Baseline schema version applied from schema.sql on first open. */
4
4
  export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
5
5
  /** Latest schema version this release supports (increment when adding migrations). */
6
- export declare const XOPC_DB_SCHEMA_VERSION = 117;
6
+ export declare const XOPC_DB_SCHEMA_VERSION = 120;
7
7
  export declare function resolveMigrationsDir(override?: string): string;
8
8
  /**
9
9
  * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
12
12
  /** Baseline schema version applied from schema.sql on first open. */
13
13
  const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
14
14
  /** Latest schema version this release supports (increment when adding migrations). */
15
- const XOPC_DB_SCHEMA_VERSION = 117;
15
+ const XOPC_DB_SCHEMA_VERSION = 120;
16
16
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
17
17
  function backupBeforeTaskCutover(db, databasePath) {
18
18
  db.exec("PRAGMA wal_checkpoint(FULL)");
@@ -57,7 +57,7 @@ function applySingleMigration(db, migration) {
57
57
  * Each step runs in its own transaction and updates schema_meta on success.
58
58
  */
59
59
  function applyPendingMigrations(db, options = {}) {
60
- const targetVersion = options.targetVersion ?? 117;
60
+ const targetVersion = options.targetVersion ?? 120;
61
61
  let currentVersion = readSchemaVersion(db);
62
62
  if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
63
63
  if (currentVersion >= targetVersion) return currentVersion;
@@ -94,7 +94,7 @@ function applyPendingMigrations(db, options = {}) {
94
94
  }
95
95
  /** Inspect schema version without mutating the database. */
96
96
  function inspectSchemaMigrationStatus(db, options = {}) {
97
- const appVersion = options.targetVersion ?? 117;
97
+ const appVersion = options.targetVersion ?? 120;
98
98
  const dbVersion = readSchemaVersion(db);
99
99
  if (dbVersion > appVersion) return {
100
100
  dbVersion,
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
19
19
  export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
20
20
 
21
21
  /** Latest schema version this release supports (increment when adding migrations). */
22
- export const XOPC_DB_SCHEMA_VERSION = 117;
22
+ export const XOPC_DB_SCHEMA_VERSION = 120;
23
23
 
24
24
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
25
25
 
@@ -32,7 +32,7 @@ function ensureXopcDatabaseSchema(db, options = {}) {
32
32
  currentVersion = 11;
33
33
  }
34
34
  applyPendingMigrations(db, {
35
- targetVersion: 117,
35
+ targetVersion: 120,
36
36
  databasePath: options.databasePath
37
37
  });
38
38
  }
@@ -1,3 +1,6 @@
1
+ import { isXopcDatabaseOpen } from "../storage/sqlite/connection.js";
2
+ import "../storage/sqlite/index.js";
3
+ import { TaskConversationRepository } from "./task-conversation-repository.js";
1
4
  //#region src/tasks/execution-context.ts
2
5
  const ORIGINS = new Set([
3
6
  "chat",
@@ -17,10 +20,10 @@ const TRIGGERS = new Set([
17
20
  function optionalString(value) {
18
21
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
19
22
  }
20
- function metadataOrigin(metadata) {
23
+ function metadataOrigin(metadata, taskId) {
21
24
  const explicit = optionalString(metadata.customData?.origin);
22
25
  if (explicit && ORIGINS.has(explicit)) return explicit;
23
- if (optionalString(metadata.customData?.taskId)) return "task";
26
+ if (taskId) return "task";
24
27
  if (metadata.sessionType === "workflow-run" || metadata.sessionType === "workflow-subagent") return "workflow";
25
28
  if (metadata.sessionType === "cron") return "automation";
26
29
  if (metadata.sessionType === "heartbeat") return "proactive";
@@ -35,14 +38,15 @@ function metadataTrigger(metadata) {
35
38
  return "user";
36
39
  }
37
40
  function resolveExecutionContext(input) {
41
+ const taskId = isXopcDatabaseOpen() ? new TaskConversationRepository().resolveActiveExecutionSession(input.sessionKey)?.taskId : void 0;
38
42
  return {
39
43
  runId: input.runId,
40
44
  sessionKey: input.sessionKey,
41
45
  channel: input.channel,
42
46
  agentId: input.agentId,
43
47
  projectId: input.metadata.projectId,
44
- taskId: optionalString(input.metadata.customData?.taskId),
45
- origin: metadataOrigin(input.metadata),
48
+ taskId,
49
+ origin: metadataOrigin(input.metadata, taskId),
46
50
  triggerKind: metadataTrigger(input.metadata),
47
51
  parentRunId: optionalString(input.metadata.customData?.parentRunId),
48
52
  contextTraceId: optionalString(input.metadata.customData?.contextTraceId),
@@ -105,6 +105,11 @@ export declare function buildHomeBriefing(input: {
105
105
  recommendation: string;
106
106
  confidence: number;
107
107
  valueScore?: number;
108
+ disposition?: "show_in_work" | "request_approval" | "auto_execute";
109
+ dispositionReason?: string;
110
+ actionStatus?: "failed" | "completed" | "rejected" | "pending" | "approval_required" | "not_authorized" | "executing";
111
+ proposedActionTitle?: string;
112
+ actionError?: string;
108
113
  decision?: {
109
114
  question: string;
110
115
  options: {
@@ -451,7 +451,7 @@ var HomeQueryService = class {
451
451
  kind: "agent_judgment",
452
452
  title: item.insight.title,
453
453
  detail: item.insight.summary,
454
- reason: "decision_needed",
454
+ reason: item.insight.disposition === "request_approval" ? "approval_required" : "decision_needed",
455
455
  urgency: item.insight.urgency === "critical" || item.insight.urgency === "high" ? "now" : "soon",
456
456
  href: `/?judgment=${encodeURIComponent(item.id)}`,
457
457
  updatedAt: Date.parse(item.updatedAt),
@@ -463,7 +463,12 @@ var HomeQueryService = class {
463
463
  recommendation: item.insight.recommendation,
464
464
  confidence: item.insight.confidence,
465
465
  valueScore: item.insight.valueScore,
466
- ...item.insight.decision ? { decision: item.insight.decision } : {}
466
+ disposition: item.insight.disposition,
467
+ dispositionReason: item.insight.dispositionReason,
468
+ ...item.insight.actionStatus ? { actionStatus: item.insight.actionStatus } : {},
469
+ ...item.insight.proposedAction ? { proposedActionTitle: item.insight.proposedAction.input.title } : {},
470
+ ...item.insight.actionError ? { actionError: item.insight.actionError } : {},
471
+ ...item.insight.decision && (!item.insight.proposedAction || item.insight.actionStatus === "approval_required") ? { decision: item.insight.decision } : {}
467
472
  }
468
473
  })),
469
474
  ...activeTasks.map((task) => decisionFromTask(this.#projector.project(task), task.projectId ? projectsById.get(task.projectId)?.name : void 0)).filter((item) => Boolean(item)),
@@ -11,7 +11,10 @@ export * from './project-operating-view-service.js';
11
11
  export * from './project-monitoring-service.js';
12
12
  export * from './task-value-metrics-service.js';
13
13
  export * from './task-application-service.js';
14
+ export * from './task-change-events.js';
14
15
  export * from './task-context-repository.js';
16
+ export * from './task-conversation-repository.js';
17
+ export * from './task-handoff-service.js';
15
18
  export * from './task-dependency-service.js';
16
19
  export * from './task-read-model-projector.js';
17
20
  export * from './task-outbox-dispatcher.js';
@@ -1,10 +1,12 @@
1
1
  import { TaskRepository } from "./task-repository.js";
2
+ import { TaskConversationConflictError, TaskConversationRepository } from "./task-conversation-repository.js";
2
3
  import { TaskRunRepository } from "./task-run-repository.js";
3
4
  import { TaskReadModelProjector } from "./task-read-model-projector.js";
4
5
  import { resolveExecutionContext } from "./execution-context.js";
5
6
  import { AttentionGovernor } from "./attention-governor.js";
6
7
  import { HomeQueryService, buildHomeBriefing, buildHomeFocusItems, decisionFromTask } from "./home-query-service.js";
7
8
  import { TaskContextRepository } from "./task-context-repository.js";
9
+ import { enqueueTaskChanged, enqueueTaskChangedEvent } from "./task-change-events.js";
8
10
  import { TaskDependencyError, TaskDependencyService } from "./task-dependency-service.js";
9
11
  import { TaskApplicationService } from "./task-application-service.js";
10
12
  import { TaskWorkflowCoordinator } from "./task-workflow-coordinator.js";
@@ -12,10 +14,11 @@ import { defineTaskContract } from "./task-contract-definition.js";
12
14
  import { ModelTaskContractPlanner, parseTaskContractResponse } from "./task-contract-planner.js";
13
15
  import { assembleTaskContext, buildTaskExecutionDirective, getTaskContextManifest, getTaskExecutionBrief } from "./task-context-assembler.js";
14
16
  import { TaskRunCoordinator } from "./task-run-coordinator.js";
15
- import { ProjectMonitoringService, decideProactiveDisposition } from "./project-monitoring-service.js";
16
- import { ProjectOperatingViewService, projectTaskLane, summarizeProjectOperatingView } from "./project-operating-view-service.js";
17
+ import { ProjectMonitoringService, decideProactiveDisposition, isInQuietHours, nextQuietHoursEnd, proactiveDispositionReason } from "./project-monitoring-service.js";
18
+ import { ProjectOperatingViewService, summarizeProjectOperatingView } from "./project-operating-view-service.js";
17
19
  import { TaskValueMetricsService } from "./task-value-metrics-service.js";
20
+ import { TaskHandoffService } from "./task-handoff-service.js";
18
21
  import { TaskOutboxDispatcher } from "./task-outbox-dispatcher.js";
19
22
  import { TaskRunDispatcher } from "./task-run-dispatcher.js";
20
23
  import { TaskSignalService } from "./task-signal-service.js";
21
- export { AttentionGovernor, HomeQueryService, ModelTaskContractPlanner, ProjectMonitoringService, ProjectOperatingViewService, TaskApplicationService, TaskContextRepository, TaskDependencyError, TaskDependencyService, TaskOutboxDispatcher, TaskReadModelProjector, TaskRepository, TaskRunCoordinator, TaskRunDispatcher, TaskRunRepository, TaskSignalService, TaskValueMetricsService, TaskWorkflowCoordinator, assembleTaskContext, buildHomeBriefing, buildHomeFocusItems, buildTaskExecutionDirective, decideProactiveDisposition, decisionFromTask, defineTaskContract, getTaskContextManifest, getTaskExecutionBrief, parseTaskContractResponse, projectTaskLane, resolveExecutionContext, summarizeProjectOperatingView };
24
+ export { AttentionGovernor, HomeQueryService, ModelTaskContractPlanner, ProjectMonitoringService, ProjectOperatingViewService, TaskApplicationService, TaskContextRepository, TaskConversationConflictError, TaskConversationRepository, TaskDependencyError, TaskDependencyService, TaskHandoffService, TaskOutboxDispatcher, TaskReadModelProjector, TaskRepository, TaskRunCoordinator, TaskRunDispatcher, TaskRunRepository, TaskSignalService, TaskValueMetricsService, TaskWorkflowCoordinator, assembleTaskContext, buildHomeBriefing, buildHomeFocusItems, buildTaskExecutionDirective, decideProactiveDisposition, decisionFromTask, defineTaskContract, enqueueTaskChanged, enqueueTaskChangedEvent, getTaskContextManifest, getTaskExecutionBrief, isInQuietHours, nextQuietHoursEnd, parseTaskContractResponse, proactiveDispositionReason, resolveExecutionContext, summarizeProjectOperatingView };
@@ -7,6 +7,15 @@ export declare function decideProactiveDisposition(policy: ProjectMonitoringPoli
7
7
  actionId?: string;
8
8
  requiresApproval?: boolean;
9
9
  }): ProactiveDisposition;
10
+ export declare function proactiveDispositionReason(policy: ProjectMonitoringPolicy, input: {
11
+ confidence: number;
12
+ valueScore: number;
13
+ risk: 'low' | 'medium' | 'high';
14
+ actionId?: string;
15
+ requiresApproval?: boolean;
16
+ }, disposition: ProactiveDisposition): string;
17
+ export declare function isInQuietHours(quietHours: QuietHours | undefined, now?: Date): boolean;
18
+ export declare function nextQuietHoursEnd(quietHours: QuietHours | undefined, now?: Date): Date | undefined;
10
19
  export declare class ProjectMonitoringService {
11
20
  #private;
12
21
  get(projectId: string): ProjectMonitoringPolicy;
@@ -2,6 +2,7 @@ import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/
2
2
  import { ProactiveScenarioService } from "../proactive/scenarios/service.js";
3
3
  //#region src/tasks/project-monitoring-service.ts
4
4
  const DEFAULT_SCENARIOS = ["project_delivery_risk", "blocked_work"];
5
+ const SUPPORTED_ACTIONS = new Set(["create_project_task"]);
5
6
  function validHour(value) {
6
7
  return Number.isInteger(value) && value >= 0 && value <= 23;
7
8
  }
@@ -19,10 +20,45 @@ function rowToPolicy(row, scenarios) {
19
20
  }
20
21
  function decideProactiveDisposition(policy, input) {
21
22
  if (policy.mode === "observe" || input.confidence < policy.confidenceThreshold || input.valueScore < .5) return "record_silently";
22
- if (input.requiresApproval || input.risk !== "low") return "request_approval";
23
- if (policy.mode === "auto_low_risk" && input.actionId && policy.allowedActions.includes(input.actionId)) return "auto_execute";
23
+ if (input.actionId) {
24
+ if (input.requiresApproval || input.risk !== "low" || policy.mode === "ask_before_action") return "request_approval";
25
+ if (policy.mode === "auto_low_risk" && policy.allowedActions.includes(input.actionId)) return "auto_execute";
26
+ return "show_in_work";
27
+ }
28
+ if (input.requiresApproval) return "request_approval";
24
29
  return "show_in_work";
25
30
  }
31
+ function proactiveDispositionReason(policy, input, disposition) {
32
+ if (policy.mode === "observe") return "Project monitoring is observe-only";
33
+ if (input.confidence < policy.confidenceThreshold) return `Confidence ${input.confidence} is below threshold ${policy.confidenceThreshold}`;
34
+ if (input.valueScore < .5) return `Value score ${input.valueScore} is below 0.5`;
35
+ if (disposition === "request_approval") return input.risk === "low" ? "Project policy requires approval before acting" : `${input.risk} risk requires approval`;
36
+ if (disposition === "auto_execute") return `Low-risk action ${input.actionId} is explicitly allowed`;
37
+ if (input.actionId && !policy.allowedActions.includes(input.actionId)) return `Action ${input.actionId} is not in the project allowlist`;
38
+ return "Insight passed the project confidence and value gates";
39
+ }
40
+ function hourInTimezone(now, timezone) {
41
+ const hour = new Intl.DateTimeFormat("en-US", {
42
+ timeZone: timezone,
43
+ hour: "2-digit",
44
+ hourCycle: "h23"
45
+ }).formatToParts(now).find((part) => part.type === "hour")?.value;
46
+ return Number(hour);
47
+ }
48
+ function isInQuietHours(quietHours, now = /* @__PURE__ */ new Date()) {
49
+ if (!quietHours || quietHours.startHour === quietHours.endHour) return false;
50
+ const hour = hourInTimezone(now, quietHours.timezone);
51
+ return quietHours.startHour < quietHours.endHour ? hour >= quietHours.startHour && hour < quietHours.endHour : hour >= quietHours.startHour || hour < quietHours.endHour;
52
+ }
53
+ function nextQuietHoursEnd(quietHours, now = /* @__PURE__ */ new Date()) {
54
+ if (!isInQuietHours(quietHours, now)) return void 0;
55
+ const minute = new Date(now);
56
+ minute.setUTCSeconds(0, 0);
57
+ for (let offset = 1; offset <= 2880; offset += 1) {
58
+ const candidate = new Date(minute.getTime() + offset * 6e4);
59
+ if (!isInQuietHours(quietHours, candidate)) return candidate;
60
+ }
61
+ }
26
62
  var ProjectMonitoringService = class {
27
63
  #scenarios = new ProactiveScenarioService();
28
64
  get(projectId) {
@@ -40,9 +76,11 @@ var ProjectMonitoringService = class {
40
76
  configure(input) {
41
77
  if (!getSqliteDatabase().prepare("SELECT 1 FROM projects WHERE project_id = ?").get(input.projectId)) throw new Error("Project not found");
42
78
  if (input.quietHours && (!validHour(input.quietHours.startHour) || !validHour(input.quietHours.endHour) || !input.quietHours.timezone.trim())) throw new Error("Quiet hours need a timezone and integer hours from 0 through 23");
79
+ if (input.quietHours) hourInTimezone(/* @__PURE__ */ new Date(), input.quietHours.timezone);
43
80
  const threshold = input.confidenceThreshold ?? .75;
44
81
  if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) throw new Error("confidenceThreshold must be between 0 and 1");
45
82
  const allowedActions = [...new Set(input.allowedActions ?? [])].map((action) => action.trim()).filter(Boolean);
83
+ if (allowedActions.some((action) => !SUPPORTED_ACTIONS.has(action))) throw new Error("Unknown proactive action");
46
84
  const scenarios = [...new Set(input.scenarios ?? DEFAULT_SCENARIOS)];
47
85
  const availableScenarios = new Set(this.#scenarios.list().map((scenario) => scenario.key));
48
86
  if (scenarios.some((scenario) => !availableScenarios.has(scenario))) throw new Error("Unknown proactive scenario");
@@ -73,4 +111,4 @@ var ProjectMonitoringService = class {
73
111
  }
74
112
  };
75
113
  //#endregion
76
- export { ProjectMonitoringService, decideProactiveDisposition };
114
+ export { ProjectMonitoringService, decideProactiveDisposition, isInQuietHours, nextQuietHoursEnd, proactiveDispositionReason };
@@ -1,6 +1,5 @@
1
- import type { ProjectOperatingSummary, ProjectOperatingView, ProjectTaskCard } from '@xopcai/gateway-contract';
1
+ import type { ProjectOperatingSummary, ProjectOperatingView } from '@xopcai/gateway-contract';
2
2
  import type { ProjectService } from '../projects/index.js';
3
- export declare function projectTaskLane(card: Pick<ProjectTaskCard, 'phase' | 'operationalState' | 'attention'>): ProjectTaskCard['lane'];
4
3
  export declare function summarizeProjectOperatingView(view: ProjectOperatingView): ProjectOperatingSummary;
5
4
  export declare class ProjectOperatingViewService {
6
5
  #private;
@@ -4,17 +4,6 @@ import { TaskReadModelProjector } from "./task-read-model-projector.js";
4
4
  import { TaskDependencyService } from "./task-dependency-service.js";
5
5
  import { ProjectMonitoringService } from "./project-monitoring-service.js";
6
6
  //#region src/tasks/project-operating-view-service.ts
7
- function projectTaskLane(card) {
8
- if (card.phase === "closed") return "done";
9
- if (card.attention.some((item) => item.kind === "input_required" || item.kind === "approval_required")) return "needs_user";
10
- if ([
11
- "queued",
12
- "running",
13
- "verifying"
14
- ].includes(card.operationalState)) return "moving";
15
- if (card.operationalState === "waiting" || card.operationalState === "blocked") return "waiting";
16
- return "ready";
17
- }
18
7
  function summarizeProjectOperatingView(view) {
19
8
  const counts = {
20
9
  ready: 0,
@@ -23,8 +12,20 @@ function summarizeProjectOperatingView(view) {
23
12
  needsUser: 0,
24
13
  done: 0
25
14
  };
26
- for (const task of view.tasks) if (task.lane === "needs_user") counts.needsUser += 1;
27
- else counts[task.lane] += 1;
15
+ for (const task of view.tasks) {
16
+ if (task.attention.some((item) => item.kind === "input_required" || item.kind === "approval_required")) {
17
+ counts.needsUser += 1;
18
+ continue;
19
+ }
20
+ if (task.phase === "closed") counts.done += 1;
21
+ else if ([
22
+ "queued",
23
+ "running",
24
+ "verifying"
25
+ ].includes(task.operationalState)) counts.moving += 1;
26
+ else if (task.operationalState === "waiting" || task.operationalState === "blocked") counts.waiting += 1;
27
+ else counts.ready += 1;
28
+ }
28
29
  return {
29
30
  ...view.digest,
30
31
  counts,
@@ -48,7 +49,7 @@ var ProjectOperatingViewService = class {
48
49
  const model = this.#projector.project(task);
49
50
  const latest = this.#runs.getLatestRoot(task.id);
50
51
  const receipt = latest ? this.#runs.getReceipt(latest.id) : void 0;
51
- const base = {
52
+ return {
52
53
  id: task.id,
53
54
  title: task.title,
54
55
  phase: task.phase,
@@ -63,20 +64,22 @@ var ProjectOperatingViewService = class {
63
64
  ...receipt ? { latestVerification: receipt.verification.status } : {},
64
65
  updatedAt: task.updatedAt
65
66
  };
66
- return {
67
- ...base,
68
- lane: projectTaskLane(base)
69
- };
70
67
  });
71
68
  const recentResults = projectTasks.flatMap((task) => this.#runs.listReceipts(task.id, 10).map((receipt) => ({
72
69
  taskId: task.id,
73
70
  taskTitle: task.title,
74
71
  receipt
75
72
  }))).sort((a, b) => b.receipt.finalizedAt - a.receipt.finalizedAt).slice(0, 10);
76
- const attentionCount = cards.filter((card) => card.lane === "needs_user").length;
73
+ const needsUser = (card) => card.attention.some((item) => item.kind === "input_required" || item.kind === "approval_required");
74
+ const moving = (card) => [
75
+ "queued",
76
+ "running",
77
+ "verifying"
78
+ ].includes(card.operationalState);
79
+ const attentionCount = cards.filter((card) => card.attention.length > 0).length;
77
80
  return {
78
81
  project,
79
- tasks: cards.sort((a, b) => b.updatedAt - a.updatedAt),
82
+ tasks: cards,
80
83
  dependencyEdges: this.#dependencies.listProjectEdges(project.id),
81
84
  blockers: cards.flatMap((card) => card.attention.map((item) => ({
82
85
  id: item.sourceId ?? `${card.id}:${item.kind}`,
@@ -91,12 +94,12 @@ var ProjectOperatingViewService = class {
91
94
  recentResults,
92
95
  digest: {
93
96
  health: cards.length === 0 ? "empty" : attentionCount > 0 ? "attention" : "healthy",
94
- summary: cards.length === 0 ? "No tasks yet" : `${cards.filter((card) => card.lane === "moving").length} moving, ${attentionCount} need attention`,
95
- recommendedAction: cards.find((card) => card.lane === "needs_user")?.attention[0]?.summary ?? cards.find((card) => card.lane === "moving")?.title ?? cards.find((card) => card.lane === "ready")?.title
97
+ summary: cards.length === 0 ? "No tasks yet" : `${cards.filter(moving).length} moving, ${attentionCount} need attention`,
98
+ recommendedAction: cards.find(needsUser)?.attention[0]?.summary ?? cards.find((card) => card.attention.length > 0)?.attention[0]?.summary ?? cards.find(moving)?.title ?? cards.find((card) => card.phase === "ready")?.title ?? cards.find((card) => card.phase === "backlog")?.title
96
99
  },
97
100
  monitoring: this.#monitoring.get(project.id)
98
101
  };
99
102
  }
100
103
  };
101
104
  //#endregion
102
- export { ProjectOperatingViewService, projectTaskLane, summarizeProjectOperatingView };
105
+ export { ProjectOperatingViewService, summarizeProjectOperatingView };