@xopcai/xopc 0.0.225 → 0.0.228

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) 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-CtTYnku4.js → activity-store-M0nAY5XB.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-dizuqu_4.js → agent-avatar-dicebear-CjGZBs4C.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-B0NI4_NI.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-CLRMz2BT.js → api-BywYxpaS.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-Cnk-DtQh.js → app-management-settings-panel--SjycrXk.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-wP0rogVI.js → appearance-settings-8G-TVUtg.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-2NiqD-YP.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-Cpzsm-DM.js → archive-plugin-7_DXaMTP.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-Bd2OOoEK.js → attachment-load-Bx44zSpz.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-page-DNqhguyk.js → automations-page-CrKAHqc0.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-workspace-Q3g9cf0h.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-Dcma5HdI.js → binary-plugins-DjDwnHAQ.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-D-B8ThwK.js → block-editor-7aMOKoZM.js} +3 -3
  16. package/dist/gateway/static/root/assets/browser-settings-page-DTwPkmAC.js +8 -0
  17. package/dist/gateway/static/root/assets/{browser-workflow-inputs-BAPZIXrs.js → browser-workflow-inputs-8NC2qDRF.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-workflows-page-DHtaSKEh.js → browser-workflows-page-DVLrWKz_.js} +2 -2
  19. package/dist/gateway/static/root/assets/capabilities-settings-panel-YcP4FbN7.js +7 -0
  20. package/dist/gateway/static/root/assets/{capability-presets-api-zsZQ7im3.js → capability-presets-api-o0y9WgAI.js} +1 -1
  21. package/dist/gateway/static/root/assets/capability-presets-settings-panel-AcPjHHia.js +3 -0
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-Cp8iaaXp.js → channel-recipient-api-C5ABr73b.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-settings-Ca94Q1D-.js +1 -0
  24. package/dist/gateway/static/root/assets/channels-status-swr-38FsOnR0.js +1 -0
  25. package/dist/gateway/static/root/assets/{chat-terminal-dock-Dr6Rq2ln.js → chat-terminal-dock-BR-_7KC0.js} +2 -2
  26. package/dist/gateway/static/root/assets/connectors-page-SGxfLTd6.js +2 -0
  27. package/dist/gateway/static/root/assets/{cron-expression-BiXrpzmo.js → cron-expression-D8-c-Tkw.js} +1 -1
  28. package/dist/gateway/static/root/assets/{date-picker-BPuRtwKt.js → date-picker-kV-Jyl9J.js} +1 -1
  29. package/dist/gateway/static/root/assets/{dependency-picker-BNC_W7wI.js → dependency-picker-CwKcrfGj.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-DzOpt5nI.js → desktop-pet-D9rnchu3.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-settings-BW0brCvH.js → desktop-pet-settings-CTs5WGzk.js} +1 -1
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-Baml0uTS.js → directory-picker-path-field-CCl0aaDm.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-CBXyKmfS.js → extension-debug-page-Cau40SvC.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-BBhcr_SZ.js → extension-page-BsJXdvIL.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-4rwWpN9_.js → extension-settings-page-qWqJyF7-.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-api-Dg-BlBe4.js → gateway-config-api-BI0SELXm.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-swr-Du0MvK9s.js → gateway-config-swr-BWGjnmDl.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-settings-BOuYZOYe.js → gateway-settings-6ZdOIC7F.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-JbjcHCaN.js → gateway-startup-retry-B_zrtccP.js} +1 -1
  40. package/dist/gateway/static/root/assets/heartbeat-settings-KBwhXeFD.js +1 -0
  41. package/dist/gateway/static/root/assets/home-page-BA2M4CLi.js +1 -0
  42. package/dist/gateway/static/root/assets/{image-generation-api-G2tfPvKP.js → image-generation-api-YKP2me5Z.js} +1 -1
  43. package/dist/gateway/static/root/assets/index-BX0dz1JK.js +70 -0
  44. package/dist/gateway/static/root/assets/index-D_hrEKXM.css +2 -0
  45. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-L1Vtxgp9.js → keyboard-shortcuts-settings-kHtpnyEm.js} +1 -1
  46. package/dist/gateway/static/root/assets/local-app-workbench-page-D2nkn09R.js +2 -0
  47. package/dist/gateway/static/root/assets/{local-apps-page--thlkBLS.js → local-apps-page-BCXa0oUa.js} +1 -1
  48. package/dist/gateway/static/root/assets/{locale-store-DqJz-cOR.js → locale-store-CXhjfe4q.js} +1 -1
  49. package/dist/gateway/static/root/assets/logs-page-D3sNst0K.js +2 -0
  50. package/dist/gateway/static/root/assets/{management-settings-p55fR4XO.js → management-settings-tHnlfOAQ.js} +1 -1
  51. package/dist/gateway/static/root/assets/{markdown-split-DwMcRlBR.js → markdown-split-BMLqHHr5.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-DD_PgVue.css → markdown-view-CLzTdVLW.css} +1 -1
  53. package/dist/gateway/static/root/assets/markdown-view-DvM5PWEH.js +201 -0
  54. package/dist/gateway/static/root/assets/{media-plugins-O6H3GZn7.js → media-plugins-DybwqTgv.js} +1 -1
  55. package/dist/gateway/static/root/assets/messages-BMdD0LWk.js +3 -0
  56. package/dist/gateway/static/root/assets/{notes-api-CrjQbmk1.js → notes-api-BFsOGYkd.js} +1 -1
  57. package/dist/gateway/static/root/assets/{notes-page-miO9ig9C.js → notes-page-C24SfF6r.js} +1 -1
  58. package/dist/gateway/static/root/assets/notes-workbench-CTdZc3ov.js +4 -0
  59. package/dist/gateway/static/root/assets/{product-open-page-BOu07nXf.js → product-open-page-DBfCVlJq.js} +1 -1
  60. package/dist/gateway/static/root/assets/project-detail-page-COHaEpbA.js +1 -0
  61. package/dist/gateway/static/root/assets/projects-page-lMnST7D_.js +1 -0
  62. package/dist/gateway/static/root/assets/{registry-api-Bf_HDLUJ.js → registry-api-BFZrVYfy.js} +1 -1
  63. package/dist/gateway/static/root/assets/remote-access-hub-BIRKBvO_.js +1 -0
  64. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DthuRqUH.js → runtime-tools-settings-panel-BPEGTip5.js} +2 -2
  65. package/dist/gateway/static/root/assets/{schema-form-8W8PhXxa.js → schema-form-Dxlu60Td.js} +1 -1
  66. package/dist/gateway/static/root/assets/sessions-page-CATO9G3y.js +1 -0
  67. package/dist/gateway/static/root/assets/settings-advanced-gate-Cco_W41x.js +1 -0
  68. package/dist/gateway/static/root/assets/{settings-form-section-CrsKGYiG.js → settings-form-section-ChXh3Bwk.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-loading-skeleton-WJtI0AAq.js → settings-loading-skeleton-BCAUqIe2.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-PCjwRF47.js → settings-page-D25W9Okr.js} +1 -1
  71. package/dist/gateway/static/root/assets/{setup-status-panel-D65CwlNC.js → setup-status-panel-DsN7Z4Fl.js} +1 -1
  72. package/dist/gateway/static/root/assets/{share-preview-page-BGtvLLeZ.js → share-preview-page-CRFnUaNb.js} +1 -1
  73. package/dist/gateway/static/root/assets/shares-settings-CAX0eNaP.js +1 -0
  74. package/dist/gateway/static/root/assets/skill-reload-api-Doy1gghe.js +1 -0
  75. package/dist/gateway/static/root/assets/{skills-page-C_npyryI.js → skills-page-Cl7jjTK7.js} +1 -1
  76. package/dist/gateway/static/root/assets/{skills-page.utils-C46Sd78k.js → skills-page.utils-BiCl4nb5.js} +1 -1
  77. package/dist/gateway/static/root/assets/{system-settings-panel-DomTLwki.js → system-settings-panel-BuESR7ZT.js} +1 -1
  78. package/dist/gateway/static/root/assets/task-detail-page-BRiy8NL3.js +1 -0
  79. package/dist/gateway/static/root/assets/{theme-store-Ce7wq__X.js → theme-store-DOdCjyuw.js} +1 -1
  80. package/dist/gateway/static/root/assets/{use-autosave-Do9mCRcY.js → use-autosave-Dg1mgJ_l.js} +1 -1
  81. package/dist/gateway/static/root/assets/user-context-page-CqoADNKu.js +1 -0
  82. package/dist/gateway/static/root/assets/{voice-api-key-field-Bc5p-0tX.js → voice-api-key-field-BDNexlVR.js} +1 -1
  83. package/dist/gateway/static/root/assets/{work-discovery-overlay-BieqsmT1.js → work-discovery-overlay-BHTfeaWL.js} +1 -1
  84. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DVj8iYvm.js → workflow-run-setup-panel-Cf_w-NZy.js} +2 -2
  85. package/dist/gateway/static/root/assets/workflows-page-j0P-uECz.js +5 -0
  86. package/dist/gateway/static/root/index.html +12 -12
  87. package/dist/gateway/static/root/notification-sw.js +1 -1
  88. package/dist/package.js +1 -1
  89. package/dist/packages/gateway-contract/src/notifications.js +56 -0
  90. package/dist/packages/gateway-contract/src/turn-outcome.js +114 -0
  91. package/dist/src/agent/agent-manager.d.ts +3 -1
  92. package/dist/src/agent/agent-manager.js +22 -5
  93. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +1 -0
  94. package/dist/src/agent/lifecycle/timeout-wrapper.js +7 -6
  95. package/dist/src/agent/mcp/mcp-transport-config.d.ts +1 -1
  96. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  97. package/dist/src/agent/memory/understanding/service.js +1 -0
  98. package/dist/src/agent/memory/understanding/types.d.ts +1 -0
  99. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +2 -2
  100. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +4 -4
  101. package/dist/src/agent/service.d.ts +2 -0
  102. package/dist/src/agent/service.js +6 -0
  103. package/dist/src/agent/skills/index.d.ts +22 -2
  104. package/dist/src/agent/skills/index.js +74 -32
  105. package/dist/src/agent/skills/skill-sources.d.ts +0 -1
  106. package/dist/src/agent/skills/skill-sources.js +1 -11
  107. package/dist/src/agent/tools/create-share-tool.js +14 -1
  108. package/dist/src/agent/tools/exec-command.js +3 -3
  109. package/dist/src/agent/tools/execute-code-tool.js +1 -1
  110. package/dist/src/agent/tools/executor.js +2 -2
  111. package/dist/src/agent/tools/image-generate-tool.js +12 -0
  112. package/dist/src/agent/tools/send-media.js +11 -0
  113. package/dist/src/agent/tools/xopc-use-tool.js +1 -1
  114. package/dist/src/cli/command-loaders.js +1 -0
  115. package/dist/src/cli/command-manifest.js +4 -0
  116. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  117. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  118. package/dist/src/cli/commands/history.d.ts +1 -0
  119. package/dist/src/cli/commands/history.js +50 -0
  120. package/dist/src/config/agent-profile.js +1 -1
  121. package/dist/src/connectors/connected-source-understanding.js +1 -1
  122. package/dist/src/gateway/agents-admin.js +1 -1
  123. package/dist/src/gateway/chat-stream/protocol.d.ts +3 -2
  124. package/dist/src/gateway/chat-stream/turn-outcome-collector.d.ts +16 -0
  125. package/dist/src/gateway/chat-stream/turn-outcome-collector.js +111 -0
  126. package/dist/src/gateway/heartbeat/service.js +1 -1
  127. package/dist/src/gateway/hono/lib/static-ui.js +7 -5
  128. package/dist/src/gateway/hono/routes/connectors.js +18 -16
  129. package/dist/src/gateway/hono/routes/index.js +2 -0
  130. package/dist/src/gateway/hono/routes/mobile.js +18 -40
  131. package/dist/src/gateway/hono/routes/notifications.d.ts +3 -0
  132. package/dist/src/gateway/hono/routes/notifications.js +45 -0
  133. package/dist/src/gateway/hono/routes/project-skills.js +30 -2
  134. package/dist/src/gateway/hono/routes/sessions.js +31 -0
  135. package/dist/src/gateway/hono/routes/tasks.js +1 -1
  136. package/dist/src/gateway/hono/routes/voice.js +1 -1
  137. package/dist/src/gateway/hono/routes/you.js +52 -0
  138. package/dist/src/gateway/service/agent-runner.js +6 -2
  139. package/dist/src/gateway/service/run-gateway-agent.js +53 -1
  140. package/dist/src/gateway/service/sessions-api.d.ts +6 -0
  141. package/dist/src/gateway/service/sessions-api.js +60 -3
  142. package/dist/src/gateway/service.d.ts +2 -2
  143. package/dist/src/gateway/service.js +8 -7
  144. package/dist/src/heartbeat/index.js +1 -1
  145. package/dist/src/history/ctx/exporter.d.ts +14 -0
  146. package/dist/src/history/ctx/exporter.js +99 -0
  147. package/dist/src/history/ctx/format.d.ts +25 -0
  148. package/dist/src/history/ctx/format.js +282 -0
  149. package/dist/src/infra/update-restart.js +1 -1
  150. package/dist/src/knowledge/connected-understanding-pipeline.js +12 -3
  151. package/dist/src/knowledge/index.d.ts +0 -1
  152. package/dist/src/knowledge/index.js +1 -2
  153. package/dist/src/media/session-references.d.ts +6 -0
  154. package/dist/src/media/session-references.js +24 -3
  155. package/dist/src/notifications/device-store.d.ts +16 -0
  156. package/dist/src/notifications/device-store.js +96 -0
  157. package/dist/src/notifications/planner.d.ts +6 -0
  158. package/dist/src/notifications/planner.js +156 -0
  159. package/dist/src/notifications/service.d.ts +18 -0
  160. package/dist/src/notifications/service.js +184 -0
  161. package/dist/src/notifications/store.d.ts +53 -0
  162. package/dist/src/notifications/store.js +184 -0
  163. package/dist/src/notifications/types.d.ts +29 -0
  164. package/dist/src/notifications/types.js +14 -0
  165. package/dist/src/projects/project-skill-service.d.ts +28 -6
  166. package/dist/src/projects/project-skill-service.js +76 -29
  167. package/dist/src/session/client-history.d.ts +3 -1
  168. package/dist/src/session/client-history.js +13 -0
  169. package/dist/src/session/index.d.ts +1 -1
  170. package/dist/src/session/manager.d.ts +1 -0
  171. package/dist/src/session/manager.js +6 -0
  172. package/dist/src/session/store.d.ts +15 -0
  173. package/dist/src/session/store.js +58 -0
  174. package/dist/src/storage/sqlite/index.d.ts +2 -1
  175. package/dist/src/storage/sqlite/index.js +3 -2
  176. package/dist/src/storage/sqlite/migrations/135_user_people.sql +59 -0
  177. package/dist/src/storage/sqlite/migrations/136_product_notifications.sql +79 -0
  178. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  179. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  180. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  181. package/dist/src/storage/sqlite/schema.js +1 -1
  182. package/dist/src/storage/sqlite/session-input-repository.d.ts +5 -0
  183. package/dist/src/storage/sqlite/session-input-repository.js +9 -1
  184. package/dist/src/storage/sqlite/user-people-repository.d.ts +32 -0
  185. package/dist/src/storage/sqlite/user-people-repository.js +246 -0
  186. package/dist/src/tasks/index.js +2 -2
  187. package/dist/src/tasks/task-application-service.js +42 -6
  188. package/dist/src/tasks/task-change-events.d.ts +9 -0
  189. package/dist/src/tasks/task-change-events.js +17 -1
  190. package/dist/src/tasks/task-outbox-dispatcher.js +5 -1
  191. package/dist/src/tasks/task-run-dispatcher.js +4 -2
  192. package/dist/src/tasks/task-run-repository.js +13 -0
  193. package/dist/src/user-context/relationships/indexer.d.ts +13 -0
  194. package/dist/src/user-context/relationships/indexer.js +185 -0
  195. package/dist/src/user-context/relationships/service.d.ts +22 -0
  196. package/dist/src/user-context/relationships/service.js +50 -0
  197. package/dist/src/user-context/relationships/types.d.ts +55 -0
  198. package/dist/src/user-context/relationships/types.js +10 -0
  199. package/dist/src/user-context/sources/repository.d.ts +1 -1
  200. package/dist/src/user-context/sources/repository.js +8 -2
  201. package/dist/src/work-discovery/service.js +1 -1
  202. package/package.json +2 -2
  203. package/dist/gateway/static/root/assets/agents-DeEEFZJ4.js +0 -68
  204. package/dist/gateway/static/root/assets/apps-page-Db5YNHVM.js +0 -1
  205. package/dist/gateway/static/root/assets/automations-workspace-CNT05YTb.js +0 -6
  206. package/dist/gateway/static/root/assets/browser-settings-page-UbDj3LIq.js +0 -8
  207. package/dist/gateway/static/root/assets/capabilities-settings-panel-CiO152QS.js +0 -7
  208. package/dist/gateway/static/root/assets/capability-presets-settings-panel-Bc_hRwfL.js +0 -3
  209. package/dist/gateway/static/root/assets/channels-settings-CFW7zBoT.js +0 -1
  210. package/dist/gateway/static/root/assets/channels-status-swr-5rNVq7iI.js +0 -1
  211. package/dist/gateway/static/root/assets/connectors-page-DLBr5nre.js +0 -2
  212. package/dist/gateway/static/root/assets/heartbeat-settings-Dq4YAlSx.js +0 -1
  213. package/dist/gateway/static/root/assets/home-page-BIXkO0Ck.js +0 -1
  214. package/dist/gateway/static/root/assets/index-Csccu8sa.js +0 -74
  215. package/dist/gateway/static/root/assets/index-D3_xSYKd.css +0 -2
  216. package/dist/gateway/static/root/assets/local-app-workbench-page-D-eacBcO.js +0 -2
  217. package/dist/gateway/static/root/assets/logs-page-BRCglHvd.js +0 -2
  218. package/dist/gateway/static/root/assets/markdown-view-CTB9u15e.js +0 -69
  219. package/dist/gateway/static/root/assets/mermaid-render-BbqDc_LQ.js +0 -134
  220. package/dist/gateway/static/root/assets/messages-CeydamsI.js +0 -3
  221. package/dist/gateway/static/root/assets/notes-workbench-_13lmvmF.js +0 -4
  222. package/dist/gateway/static/root/assets/project-detail-page-BStRkOQr.js +0 -1
  223. package/dist/gateway/static/root/assets/projects-page-CY03RxVr.js +0 -1
  224. package/dist/gateway/static/root/assets/remote-access-hub-B19llkld.js +0 -1
  225. package/dist/gateway/static/root/assets/sessions-page-BDA2qbTd.js +0 -1
  226. package/dist/gateway/static/root/assets/settings-advanced-gate-BONIDX4v.js +0 -1
  227. package/dist/gateway/static/root/assets/shares-settings-CdzVKrBl.js +0 -1
  228. package/dist/gateway/static/root/assets/skill-reload-api-CsRWid5h.js +0 -1
  229. package/dist/gateway/static/root/assets/task-detail-page-B6YFX7kK.js +0 -1
  230. package/dist/gateway/static/root/assets/user-context-page-CiCQA40E.js +0 -1
  231. package/dist/gateway/static/root/assets/workflows-page-AG04TTuH.js +0 -5
  232. package/dist/src/knowledge/people-graph.d.ts +0 -25
  233. package/dist/src/knowledge/people-graph.js +0 -92
  234. package/dist/src/mobile/notification-service.d.ts +0 -9
  235. package/dist/src/mobile/notification-service.js +0 -156
  236. package/dist/src/mobile/notification-store.d.ts +0 -28
  237. package/dist/src/mobile/notification-store.js +0 -132
  238. package/dist/src/mobile/notification-types.d.ts +0 -37
  239. package/dist/src/mobile/notification-types.js +0 -9
@@ -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 = 134;
15
+ const XOPC_DB_SCHEMA_VERSION = 136;
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 ?? 134;
60
+ const targetVersion = options.targetVersion ?? 136;
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 ?? 134;
97
+ const appVersion = options.targetVersion ?? 136;
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 = 134;
22
+ export const XOPC_DB_SCHEMA_VERSION = 136;
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: 134,
35
+ targetVersion: 136,
36
36
  databasePath: options.databasePath
37
37
  });
38
38
  }
@@ -31,6 +31,11 @@ export type SessionInputState = {
31
31
  inputs: SessionInput[];
32
32
  };
33
33
  export declare function getSessionInputState(sessionKey: string): SessionInputState;
34
+ /** Durable active webchat runs claimed by the session-input coordinator. */
35
+ export declare function listActiveSessionInputRuns(): Array<{
36
+ sessionKey: string;
37
+ runId: string;
38
+ }>;
34
39
  export declare function findSessionInput(sessionKey: string, clientMessageId: string): SessionInput | undefined;
35
40
  export declare function getSessionInputById(sessionKey: string, id: string): SessionInput | undefined;
36
41
  export declare function insertSessionInput(input: {
@@ -56,6 +56,14 @@ function getSessionInputState(sessionKey) {
56
56
  })
57
57
  };
58
58
  }
59
+ /** Durable active webchat runs claimed by the session-input coordinator. */
60
+ function listActiveSessionInputRuns() {
61
+ return getSqliteDatabase().prepare(`SELECT session_key, active_run_id
62
+ FROM session_input_runtime WHERE active_run_id IS NOT NULL`).all().map((row) => ({
63
+ sessionKey: row.session_key,
64
+ runId: row.active_run_id
65
+ }));
66
+ }
59
67
  function findSessionInput(sessionKey, clientMessageId) {
60
68
  const row = getSqliteDatabase().prepare(`${SELECT_INPUTS} WHERE session_key = ? AND client_message_id = ?`).get(sessionKey, clientMessageId);
61
69
  return row ? mapInput(row) : void 0;
@@ -158,4 +166,4 @@ function recoverSessionInputState() {
158
166
  });
159
167
  }
160
168
  //#endregion
161
- export { cancelQueuedSessionInput, claimNextSessionInput, findSessionInput, finishSessionInputRun, getSessionInputById, getSessionInputState, insertSessionInput, mutateQueuedSessionInput, recoverSessionInputState, setSessionInputStatus };
169
+ export { cancelQueuedSessionInput, claimNextSessionInput, findSessionInput, finishSessionInputRun, getSessionInputById, getSessionInputState, insertSessionInput, listActiveSessionInputRuns, mutateQueuedSessionInput, recoverSessionInputState, setSessionInputStatus };
@@ -0,0 +1,32 @@
1
+ import type { UserPerson, UserPersonIndexEntry, UserPersonKind, UserRelationshipSummary } from '../../user-context/relationships/types.js';
2
+ export declare function replaceUserPeopleIndex(entries: UserPersonIndexEntry[], revision: {
3
+ sourceChangeSequence: number;
4
+ sourceGrantsUpdatedAt: number;
5
+ }, principalId?: string, nowMs?: number): void;
6
+ export declare function getUserPeopleIndexState(principalId?: string): {
7
+ sourceChangeSequence: number;
8
+ sourceGrantsUpdatedAt: number;
9
+ rebuiltAt: number;
10
+ } | null;
11
+ export declare function listUserPeople(options?: {
12
+ query?: string;
13
+ kind?: UserPersonKind;
14
+ sourceInstanceId?: string;
15
+ includeHidden?: boolean;
16
+ hiddenOnly?: boolean;
17
+ offset?: number;
18
+ limit?: number;
19
+ principalId?: string;
20
+ }): {
21
+ items: UserPerson[];
22
+ total: number;
23
+ nextOffset?: number;
24
+ };
25
+ export declare function getUserPerson(personId: string, principalId?: string): UserPerson | null;
26
+ export declare function updateUserPerson(personId: string, patch: {
27
+ displayName?: string | null;
28
+ kind?: UserPersonKind | null;
29
+ hidden?: boolean;
30
+ }, principalId?: string): UserPerson | null;
31
+ export declare function summarizeUserPeople(principalId?: string): UserRelationshipSummary;
32
+ export declare function mergeUserPeople(sourcePersonId: string, targetPersonId: string, principalId?: string): UserPerson | null;
@@ -0,0 +1,246 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
2
+ import { USER_CONTEXT_PRINCIPAL_ID } from "../../user-context/domain.js";
3
+ //#region src/storage/sqlite/user-people-repository.ts
4
+ function sourceMetadata(sourceInstanceId) {
5
+ if (!sourceInstanceId.startsWith("composio:")) return {};
6
+ const connectorId = sourceInstanceId.split(":")[1];
7
+ if (!connectorId) return {};
8
+ return {
9
+ connectorId,
10
+ toolkit: connectorId.replace(/^composio-/, "")
11
+ };
12
+ }
13
+ function handlesFor(personId) {
14
+ return getSqliteDatabase().prepare(`SELECT handle_id, handle_type, display_value,
15
+ source_instance_id, verification, first_observed_at, last_observed_at
16
+ FROM user_person_handles WHERE person_id = ?
17
+ ORDER BY CASE handle_type WHEN 'email' THEN 0 WHEN 'provider_user' THEN 1 WHEN 'username' THEN 2 ELSE 3 END,
18
+ last_observed_at DESC`).all(personId).map((row) => ({
19
+ id: row.handle_id,
20
+ type: row.handle_type,
21
+ value: row.display_value,
22
+ sourceInstanceId: row.source_instance_id,
23
+ verification: row.verification,
24
+ firstObservedAt: row.first_observed_at,
25
+ lastObservedAt: row.last_observed_at
26
+ }));
27
+ }
28
+ function sourcesFor(personId) {
29
+ return getSqliteDatabase().prepare(`SELECT source_instance_id, interaction_count,
30
+ first_observed_at, last_observed_at FROM user_person_source_stats
31
+ WHERE person_id = ? ORDER BY last_observed_at DESC`).all(personId).map((row) => ({
32
+ sourceInstanceId: row.source_instance_id,
33
+ ...sourceMetadata(row.source_instance_id),
34
+ interactionCount: row.interaction_count,
35
+ firstObservedAt: row.first_observed_at,
36
+ lastObservedAt: row.last_observed_at
37
+ }));
38
+ }
39
+ function fromRow(row, includeDetails = false) {
40
+ const handles = handlesFor(row.person_id);
41
+ const sources = sourcesFor(row.person_id);
42
+ const primaryHandle = handles.find((handle) => handle.type === "email")?.value ?? handles.find((handle) => handle.type === "provider_user" || handle.type === "username")?.value;
43
+ return {
44
+ id: row.person_id,
45
+ displayName: row.user_display_name ?? row.display_name,
46
+ kind: row.user_kind ?? row.inferred_kind,
47
+ hidden: row.hidden === 1,
48
+ confidence: row.confidence,
49
+ ...primaryHandle ? { primaryHandle } : {},
50
+ interactionCount: sources.reduce((total, source) => total + source.interactionCount, 0),
51
+ firstObservedAt: row.first_observed_at,
52
+ lastObservedAt: row.last_observed_at,
53
+ handles: includeDetails ? handles : [],
54
+ sources
55
+ };
56
+ }
57
+ function replaceUserPeopleIndex(entries, revision, principalId = USER_CONTEXT_PRINCIPAL_ID, nowMs = Date.now()) {
58
+ runSqliteWriteTransaction((db) => {
59
+ const mergedRows = db.prepare(`SELECT person_id, merged_into_person_id FROM user_people
60
+ WHERE principal_id = ? AND merged_into_person_id IS NOT NULL`).all(principalId);
61
+ const mergedTargets = new Map(mergedRows.map((row) => [row.person_id, row.merged_into_person_id]));
62
+ const targetFor = (personId) => {
63
+ const seen = /* @__PURE__ */ new Set();
64
+ let current = personId;
65
+ while (mergedTargets.has(current) && !seen.has(current)) {
66
+ seen.add(current);
67
+ current = mergedTargets.get(current);
68
+ }
69
+ return current;
70
+ };
71
+ db.prepare(`DELETE FROM user_person_source_stats
72
+ WHERE person_id IN (SELECT person_id FROM user_people WHERE principal_id = ?)`).run(principalId);
73
+ db.prepare(`DELETE FROM user_person_handles
74
+ WHERE person_id IN (SELECT person_id FROM user_people WHERE principal_id = ?)
75
+ AND verification != 'user_confirmed'`).run(principalId);
76
+ for (const entry of entries) {
77
+ const personId = targetFor(entry.id);
78
+ if (personId === entry.id) db.prepare(`INSERT INTO user_people (
79
+ person_id, principal_id, display_name, inferred_kind, confidence,
80
+ first_observed_at, last_observed_at, created_at, updated_at
81
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
82
+ ON CONFLICT(person_id) DO UPDATE SET
83
+ display_name = excluded.display_name,
84
+ inferred_kind = excluded.inferred_kind,
85
+ confidence = excluded.confidence,
86
+ first_observed_at = MIN(user_people.first_observed_at, excluded.first_observed_at),
87
+ last_observed_at = excluded.last_observed_at,
88
+ updated_at = excluded.updated_at`).run(entry.id, principalId, entry.displayName, entry.inferredKind, entry.confidence, entry.firstObservedAt, entry.lastObservedAt, nowMs, nowMs);
89
+ for (const handle of entry.handles) db.prepare(`INSERT INTO user_person_handles (
90
+ handle_id, person_id, handle_type, normalized_value, display_value,
91
+ source_instance_id, verification, first_observed_at, last_observed_at, created_at, updated_at
92
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
93
+ ON CONFLICT(handle_type, normalized_value, source_instance_id) DO UPDATE SET
94
+ person_id = excluded.person_id,
95
+ display_value = excluded.display_value,
96
+ verification = CASE WHEN user_person_handles.verification = 'user_confirmed'
97
+ THEN 'user_confirmed' ELSE excluded.verification END,
98
+ first_observed_at = MIN(user_person_handles.first_observed_at, excluded.first_observed_at),
99
+ last_observed_at = excluded.last_observed_at,
100
+ updated_at = excluded.updated_at`).run(handle.id, personId, handle.type, handle.value.toLocaleLowerCase(), handle.value, handle.sourceInstanceId, handle.verification, handle.firstObservedAt, handle.lastObservedAt, nowMs, nowMs);
101
+ for (const source of entry.sources) db.prepare(`INSERT INTO user_person_source_stats (
102
+ person_id, source_instance_id, interaction_count, first_observed_at,
103
+ last_observed_at, updated_at
104
+ ) VALUES (?, ?, ?, ?, ?, ?)
105
+ ON CONFLICT(person_id, source_instance_id) DO UPDATE SET
106
+ interaction_count = user_person_source_stats.interaction_count + excluded.interaction_count,
107
+ first_observed_at = MIN(user_person_source_stats.first_observed_at, excluded.first_observed_at),
108
+ last_observed_at = MAX(user_person_source_stats.last_observed_at, excluded.last_observed_at),
109
+ updated_at = excluded.updated_at`).run(personId, source.sourceInstanceId, source.interactionCount, source.firstObservedAt, source.lastObservedAt, nowMs);
110
+ }
111
+ db.prepare(`DELETE FROM user_people
112
+ WHERE principal_id = ? AND merged_into_person_id IS NULL AND hidden = 0
113
+ AND user_display_name IS NULL AND user_kind IS NULL
114
+ AND NOT EXISTS (SELECT 1 FROM user_person_source_stats s WHERE s.person_id = user_people.person_id)
115
+ AND NOT EXISTS (SELECT 1 FROM user_person_handles h WHERE h.person_id = user_people.person_id)`).run(principalId);
116
+ db.prepare(`INSERT INTO user_people_index_state (
117
+ principal_id, source_change_sequence, source_grants_updated_at, rebuilt_at
118
+ ) VALUES (?, ?, ?, ?)
119
+ ON CONFLICT(principal_id) DO UPDATE SET
120
+ source_change_sequence = excluded.source_change_sequence,
121
+ source_grants_updated_at = excluded.source_grants_updated_at,
122
+ rebuilt_at = excluded.rebuilt_at`).run(principalId, revision.sourceChangeSequence, revision.sourceGrantsUpdatedAt, nowMs);
123
+ });
124
+ }
125
+ function getUserPeopleIndexState(principalId = USER_CONTEXT_PRINCIPAL_ID) {
126
+ const row = getSqliteDatabase().prepare(`SELECT source_change_sequence, source_grants_updated_at, rebuilt_at
127
+ FROM user_people_index_state WHERE principal_id = ?`).get(principalId);
128
+ return row ? {
129
+ sourceChangeSequence: row.source_change_sequence,
130
+ sourceGrantsUpdatedAt: row.source_grants_updated_at,
131
+ rebuiltAt: row.rebuilt_at
132
+ } : null;
133
+ }
134
+ function listUserPeople(options = {}) {
135
+ const principalId = options.principalId ?? "local-owner";
136
+ const clauses = [
137
+ "p.principal_id = ?",
138
+ "p.merged_into_person_id IS NULL",
139
+ "EXISTS (SELECT 1 FROM user_person_source_stats active_source WHERE active_source.person_id = p.person_id)"
140
+ ];
141
+ const params = [principalId];
142
+ if (options.hiddenOnly) clauses.push("p.hidden = 1");
143
+ else if (!options.includeHidden) clauses.push("p.hidden = 0");
144
+ if (options.kind) {
145
+ clauses.push("COALESCE(p.user_kind, p.inferred_kind) = ?");
146
+ params.push(options.kind);
147
+ }
148
+ if (options.sourceInstanceId) {
149
+ clauses.push("EXISTS (SELECT 1 FROM user_person_source_stats filtered_source WHERE filtered_source.person_id = p.person_id AND filtered_source.source_instance_id = ?)");
150
+ params.push(options.sourceInstanceId);
151
+ }
152
+ const query = options.query?.trim().toLocaleLowerCase();
153
+ if (query) {
154
+ clauses.push(`(LOWER(COALESCE(p.user_display_name, p.display_name)) LIKE ? OR EXISTS (
155
+ SELECT 1 FROM user_person_handles search_handle
156
+ WHERE search_handle.person_id = p.person_id AND search_handle.normalized_value LIKE ?
157
+ ))`);
158
+ params.push(`%${query}%`, `%${query}%`);
159
+ }
160
+ const where = clauses.join(" AND ");
161
+ const total = Number(getSqliteDatabase().prepare(`SELECT COUNT(*) AS count FROM user_people p WHERE ${where}`).get(...params).count);
162
+ const limit = Math.max(1, Math.min(100, Math.floor(options.limit ?? 30)));
163
+ const offset = Math.max(0, Math.floor(options.offset ?? 0));
164
+ const rows = getSqliteDatabase().prepare(`SELECT p.* FROM user_people p WHERE ${where}
165
+ ORDER BY p.last_observed_at DESC, COALESCE(p.user_display_name, p.display_name) ASC
166
+ LIMIT ? OFFSET ?`).all(...params, limit, offset);
167
+ return {
168
+ items: rows.map((row) => fromRow(row)),
169
+ total,
170
+ ...offset + rows.length < total ? { nextOffset: offset + rows.length } : {}
171
+ };
172
+ }
173
+ function getUserPerson(personId, principalId = USER_CONTEXT_PRINCIPAL_ID) {
174
+ const row = getSqliteDatabase().prepare(`SELECT * FROM user_people
175
+ WHERE person_id = ? AND principal_id = ? AND merged_into_person_id IS NULL`).get(personId, principalId);
176
+ return row ? fromRow(row, true) : null;
177
+ }
178
+ function updateUserPerson(personId, patch, principalId = USER_CONTEXT_PRINCIPAL_ID) {
179
+ const current = getUserPerson(personId, principalId);
180
+ if (!current) return null;
181
+ runSqliteWriteTransaction((db) => db.prepare(`UPDATE user_people SET
182
+ user_display_name = ?, user_kind = ?, hidden = ?, updated_at = ?
183
+ WHERE person_id = ? AND principal_id = ?`).run(patch.displayName === void 0 ? getUserDisplayName(personId) : patch.displayName, patch.kind === void 0 ? getUserKind(personId) : patch.kind, patch.hidden === void 0 ? Number(current.hidden) : Number(patch.hidden), Date.now(), personId, principalId));
184
+ return getUserPerson(personId, principalId);
185
+ }
186
+ function getUserDisplayName(personId) {
187
+ return getSqliteDatabase().prepare("SELECT user_display_name AS value FROM user_people WHERE person_id = ?").get(personId)?.value ?? null;
188
+ }
189
+ function getUserKind(personId) {
190
+ return getSqliteDatabase().prepare("SELECT user_kind AS value FROM user_people WHERE person_id = ?").get(personId)?.value ?? null;
191
+ }
192
+ function summarizeUserPeople(principalId = USER_CONTEXT_PRINCIPAL_ID) {
193
+ const row = getSqliteDatabase().prepare(`SELECT
194
+ SUM(CASE WHEN hidden = 0 AND COALESCE(user_kind, inferred_kind) = 'person' THEN 1 ELSE 0 END) AS people,
195
+ SUM(CASE WHEN hidden = 0 AND COALESCE(user_kind, inferred_kind) IN ('bot', 'service', 'group') THEN 1 ELSE 0 END) AS automated,
196
+ SUM(CASE WHEN hidden = 0 AND COALESCE(user_kind, inferred_kind) = 'unknown' THEN 1 ELSE 0 END) AS needs_review,
197
+ SUM(CASE WHEN hidden = 1 THEN 1 ELSE 0 END) AS hidden,
198
+ MAX(last_observed_at) AS last_updated_at
199
+ FROM user_people p WHERE principal_id = ? AND merged_into_person_id IS NULL
200
+ AND EXISTS (SELECT 1 FROM user_person_source_stats s WHERE s.person_id = p.person_id)`).get(principalId);
201
+ const sources = Number(getSqliteDatabase().prepare(`SELECT COUNT(DISTINCT source_instance_id) AS count
202
+ FROM user_person_source_stats s JOIN user_people p ON p.person_id = s.person_id
203
+ WHERE p.principal_id = ? AND p.merged_into_person_id IS NULL`).get(principalId).count);
204
+ return {
205
+ people: Number(row.people ?? 0),
206
+ automatedAccounts: Number(row.automated ?? 0),
207
+ needsReview: Number(row.needs_review ?? 0),
208
+ hidden: Number(row.hidden ?? 0),
209
+ sources,
210
+ ...row.last_updated_at == null ? {} : { lastUpdatedAt: row.last_updated_at }
211
+ };
212
+ }
213
+ function mergeUserPeople(sourcePersonId, targetPersonId, principalId = USER_CONTEXT_PRINCIPAL_ID) {
214
+ if (sourcePersonId === targetPersonId) return getUserPerson(targetPersonId, principalId);
215
+ const source = getUserPerson(sourcePersonId, principalId);
216
+ const target = getUserPerson(targetPersonId, principalId);
217
+ if (!source || !target) return null;
218
+ runSqliteWriteTransaction((db) => {
219
+ const duplicateHandles = db.prepare(`SELECT source.handle_id FROM user_person_handles source
220
+ JOIN user_person_handles target
221
+ ON target.person_id = ? AND target.handle_type = source.handle_type
222
+ AND target.normalized_value = source.normalized_value
223
+ AND target.source_instance_id = source.source_instance_id
224
+ WHERE source.person_id = ?`).all(targetPersonId, sourcePersonId);
225
+ for (const duplicate of duplicateHandles) db.prepare("DELETE FROM user_person_handles WHERE handle_id = ?").run(duplicate.handle_id);
226
+ db.prepare("UPDATE user_person_handles SET person_id = ?, updated_at = ? WHERE person_id = ?").run(targetPersonId, Date.now(), sourcePersonId);
227
+ const sourceStats = db.prepare("SELECT * FROM user_person_source_stats WHERE person_id = ?").all(sourcePersonId);
228
+ for (const stat of sourceStats) db.prepare(`INSERT INTO user_person_source_stats (
229
+ person_id, source_instance_id, interaction_count, first_observed_at,
230
+ last_observed_at, last_source_item_id, updated_at
231
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
232
+ ON CONFLICT(person_id, source_instance_id) DO UPDATE SET
233
+ interaction_count = user_person_source_stats.interaction_count + excluded.interaction_count,
234
+ first_observed_at = MIN(user_person_source_stats.first_observed_at, excluded.first_observed_at),
235
+ last_observed_at = MAX(user_person_source_stats.last_observed_at, excluded.last_observed_at),
236
+ last_source_item_id = CASE WHEN excluded.last_observed_at >= user_person_source_stats.last_observed_at
237
+ THEN excluded.last_source_item_id ELSE user_person_source_stats.last_source_item_id END,
238
+ updated_at = excluded.updated_at`).run(targetPersonId, stat.source_instance_id, stat.interaction_count, stat.first_observed_at, stat.last_observed_at, stat.last_source_item_id, Date.now());
239
+ db.prepare("DELETE FROM user_person_source_stats WHERE person_id = ?").run(sourcePersonId);
240
+ db.prepare(`UPDATE user_people SET merged_into_person_id = ?, updated_at = ?
241
+ WHERE person_id = ? AND principal_id = ?`).run(targetPersonId, Date.now(), sourcePersonId, principalId);
242
+ });
243
+ return getUserPerson(targetPersonId, principalId);
244
+ }
245
+ //#endregion
246
+ export { getUserPeopleIndexState, getUserPerson, listUserPeople, mergeUserPeople, replaceUserPeopleIndex, summarizeUserPeople, updateUserPerson };
@@ -1,12 +1,12 @@
1
1
  import { TaskRepository } from "./task-repository.js";
2
2
  import { TaskConversationConflictError, TaskConversationRepository } from "./task-conversation-repository.js";
3
+ import { enqueueTaskAttentionRequiredEvent, enqueueTaskChanged, enqueueTaskChangedEvent } from "./task-change-events.js";
3
4
  import { TaskRunRepository } from "./task-run-repository.js";
4
5
  import { TaskReadModelProjector } from "./task-read-model-projector.js";
5
6
  import { resolveExecutionContext } from "./execution-context.js";
6
7
  import { AttentionGovernor } from "./attention-governor.js";
7
8
  import { HomeQueryService, buildHomeWorkbench, decisionFromTask } from "./home-query-service.js";
8
9
  import { TaskContextRepository } from "./task-context-repository.js";
9
- import { enqueueTaskChanged, enqueueTaskChangedEvent } from "./task-change-events.js";
10
10
  import { TaskDependencyError, TaskDependencyService } from "./task-dependency-service.js";
11
11
  import { TaskApplicationService } from "./task-application-service.js";
12
12
  import { TaskWorkflowCoordinator } from "./task-workflow-coordinator.js";
@@ -22,4 +22,4 @@ import { TaskDeletionService } from "./task-deletion-service.js";
22
22
  import { TaskOutboxDispatcher } from "./task-outbox-dispatcher.js";
23
23
  import { TaskRunDispatcher } from "./task-run-dispatcher.js";
24
24
  import { TaskSignalService } from "./task-signal-service.js";
25
- export { AttentionGovernor, HomeQueryService, ModelTaskContractPlanner, ProjectMonitoringService, ProjectOperatingViewService, TaskApplicationService, TaskContextRepository, TaskConversationConflictError, TaskConversationRepository, TaskDeletionService, TaskDependencyError, TaskDependencyService, TaskHandoffService, TaskOutboxDispatcher, TaskReadModelProjector, TaskRepository, TaskRunCoordinator, TaskRunDispatcher, TaskRunRepository, TaskSignalService, TaskValueMetricsService, TaskWorkflowCoordinator, assembleTaskContext, buildHomeWorkbench, buildTaskExecutionDirective, decideProactiveDisposition, decisionFromTask, defineTaskContract, enqueueTaskChanged, enqueueTaskChangedEvent, getTaskContextManifest, getTaskExecutionBrief, isInQuietHours, nextQuietHoursEnd, parseTaskContractResponse, proactiveDispositionReason, resolveExecutionContext, summarizeProjectOperatingView };
25
+ export { AttentionGovernor, HomeQueryService, ModelTaskContractPlanner, ProjectMonitoringService, ProjectOperatingViewService, TaskApplicationService, TaskContextRepository, TaskConversationConflictError, TaskConversationRepository, TaskDeletionService, TaskDependencyError, TaskDependencyService, TaskHandoffService, TaskOutboxDispatcher, TaskReadModelProjector, TaskRepository, TaskRunCoordinator, TaskRunDispatcher, TaskRunRepository, TaskSignalService, TaskValueMetricsService, TaskWorkflowCoordinator, assembleTaskContext, buildHomeWorkbench, buildTaskExecutionDirective, decideProactiveDisposition, decisionFromTask, defineTaskContract, enqueueTaskAttentionRequiredEvent, enqueueTaskChanged, enqueueTaskChangedEvent, getTaskContextManifest, getTaskExecutionBrief, isInQuietHours, nextQuietHoursEnd, parseTaskContractResponse, proactiveDispositionReason, resolveExecutionContext, summarizeProjectOperatingView };
@@ -1,9 +1,9 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
2
  import { TaskRepository } from "./task-repository.js";
3
+ import { enqueueTaskAttentionRequiredEvent, enqueueTaskChangedEvent } from "./task-change-events.js";
3
4
  import { TaskRunRepository } from "./task-run-repository.js";
4
5
  import { TaskReadModelProjector } from "./task-read-model-projector.js";
5
6
  import { TaskContextRepository } from "./task-context-repository.js";
6
- import { enqueueTaskChangedEvent } from "./task-change-events.js";
7
7
  import { TaskDependencyService } from "./task-dependency-service.js";
8
8
  import { createHash, randomUUID } from "node:crypto";
9
9
  //#region src/tasks/task-application-service.ts
@@ -153,6 +153,15 @@ var TaskApplicationService = class {
153
153
  ],
154
154
  actor
155
155
  });
156
+ else if (started.ok === false && started.reason === "blocked") {
157
+ if (!this.#runs.listActiveWaits(current.id).some((wait) => wait.kind === "approval" || wait.kind === "user_input")) enqueueTaskAttentionRequiredEvent(db, {
158
+ taskId: current.id,
159
+ taskTitle: current.title,
160
+ ...current.projectId ? { projectId: current.projectId } : {},
161
+ reason: "blocked",
162
+ correlationId: input.idempotencyKey
163
+ });
164
+ }
156
165
  return started;
157
166
  });
158
167
  }
@@ -275,11 +284,15 @@ var TaskApplicationService = class {
275
284
  }
276
285
  }
277
286
  if (result.ok === false) {
278
- if (result.reason === "blocked") this.enqueueEvent(db, "task.attention_required.v2", task.id, input.idempotencyKey, {
279
- taskId: task.id,
280
- reason: "blocked",
281
- projectId: task.projectId
282
- });
287
+ if (result.reason === "blocked") {
288
+ if (!this.#runs.listActiveWaits(task.id).some((wait) => wait.kind === "approval" || wait.kind === "user_input")) enqueueTaskAttentionRequiredEvent(db, {
289
+ taskId: task.id,
290
+ taskTitle: task.title,
291
+ ...task.projectId ? { projectId: task.projectId } : {},
292
+ reason: "blocked",
293
+ correlationId: input.idempotencyKey
294
+ });
295
+ }
283
296
  return result;
284
297
  }
285
298
  this.recordCommand(db, {
@@ -301,6 +314,10 @@ var TaskApplicationService = class {
301
314
  runId: result.runId
302
315
  });
303
316
  if (result.model.task.phase !== task.phase) this.enqueueEvent(db, "task.phase_changed.v2", task.id, input.idempotencyKey, {
317
+ task: {
318
+ id: result.model.task.id,
319
+ title: result.model.task.title
320
+ },
304
321
  taskId: task.id,
305
322
  from: task.phase,
306
323
  to: result.model.task.phase,
@@ -344,6 +361,14 @@ var TaskApplicationService = class {
344
361
  const task = this.#tasks.require(run.taskId);
345
362
  if (input.receipt.status !== "succeeded") {
346
363
  const model = this.#projector.project(task);
364
+ enqueueTaskAttentionRequiredEvent(db, {
365
+ taskId: task.id,
366
+ taskTitle: task.title,
367
+ ...task.projectId ? { projectId: task.projectId } : {},
368
+ reason: "failed",
369
+ detail: input.terminalMessage ?? input.receipt.summary,
370
+ correlationId: run.id
371
+ });
347
372
  enqueueTaskChangedEvent(db, {
348
373
  taskId: task.id,
349
374
  projectId: task.projectId,
@@ -370,6 +395,17 @@ var TaskApplicationService = class {
370
395
  ...phase === "closed" ? { resolution: "done" } : {}
371
396
  });
372
397
  if (!updated) throw new Error("Task changed while its run was being finalized");
398
+ this.enqueueEvent(db, "task.phase_changed.v2", updated.id, run.id, {
399
+ task: {
400
+ id: updated.id,
401
+ title: updated.title
402
+ },
403
+ taskId: updated.id,
404
+ from: task.phase,
405
+ to: updated.phase,
406
+ resolution: updated.resolution,
407
+ projectId: updated.projectId
408
+ });
373
409
  enqueueTaskChangedEvent(db, {
374
410
  taskId: updated.id,
375
411
  projectId: updated.projectId,
@@ -10,4 +10,13 @@ export type TaskChangeInput = {
10
10
  occurredAt?: number;
11
11
  };
12
12
  export declare function enqueueTaskChangedEvent(db: DatabaseSync, input: TaskChangeInput): void;
13
+ export declare function enqueueTaskAttentionRequiredEvent(db: DatabaseSync, input: {
14
+ taskId: string;
15
+ taskTitle: string;
16
+ projectId?: string;
17
+ reason: 'blocked' | 'failed' | 'user_input' | 'approval';
18
+ detail?: string;
19
+ correlationId: string;
20
+ occurredAt?: number;
21
+ }): void;
13
22
  export declare function enqueueTaskChanged(input: TaskChangeInput): void;
@@ -22,8 +22,24 @@ function enqueueTaskChangedEvent(db, input) {
22
22
  occurredAt
23
23
  }), occurredAt);
24
24
  }
25
+ function enqueueTaskAttentionRequiredEvent(db, input) {
26
+ const occurredAt = input.occurredAt ?? Date.now();
27
+ db.prepare(`INSERT INTO domain_outbox (
28
+ event_id, event_type, subject_kind, subject_id, correlation_id,
29
+ payload_json, created_at
30
+ ) VALUES (?, 'task.attention_required.v2', 'task', ?, ?, ?, ?)`).run(randomUUID(), input.taskId, input.correlationId, JSON.stringify({
31
+ task: {
32
+ id: input.taskId,
33
+ title: input.taskTitle
34
+ },
35
+ taskId: input.taskId,
36
+ reason: input.reason,
37
+ ...input.detail ? { detail: input.detail } : {},
38
+ ...input.projectId ? { projectId: input.projectId } : {}
39
+ }), occurredAt);
40
+ }
25
41
  function enqueueTaskChanged(input) {
26
42
  runSqliteWriteTransaction((db) => enqueueTaskChangedEvent(db, input));
27
43
  }
28
44
  //#endregion
29
- export { enqueueTaskChanged, enqueueTaskChangedEvent };
45
+ export { enqueueTaskAttentionRequiredEvent, enqueueTaskChanged, enqueueTaskChangedEvent };
@@ -10,10 +10,14 @@ var TaskOutboxDispatcher = class {
10
10
  ORDER BY created_at, event_id LIMIT ?`).all(Math.max(1, Math.min(500, Math.floor(limit))));
11
11
  let published = 0;
12
12
  for (const row of rows) try {
13
+ const payload = JSON.parse(row.payload_json);
13
14
  this.publish({
14
15
  type: row.event_type,
15
16
  source: "tasks",
16
- payload: JSON.parse(row.payload_json),
17
+ payload: {
18
+ ...payload,
19
+ sourceEventId: row.event_id
20
+ },
17
21
  occurredAtMs: row.created_at
18
22
  });
19
23
  runSqliteWriteTransaction((db) => {
@@ -2,12 +2,14 @@ import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { TaskRepository } from "./task-repository.js";
4
4
  import { TaskRunRepository } from "./task-run-repository.js";
5
+ import { TaskApplicationService } from "./task-application-service.js";
5
6
  //#region src/tasks/task-run-dispatcher.ts
6
7
  init_logger();
7
8
  const log = createLogger("TaskRunDispatcher");
8
9
  var TaskRunDispatcher = class {
9
10
  #runs = new TaskRunRepository();
10
11
  #tasks = new TaskRepository();
12
+ #application = new TaskApplicationService();
11
13
  #draining = /* @__PURE__ */ new Set();
12
14
  constructor(deps) {
13
15
  this.deps = deps;
@@ -49,9 +51,9 @@ var TaskRunDispatcher = class {
49
51
  "running",
50
52
  "waiting",
51
53
  "verifying"
52
- ].includes(current.status)) this.#runs.finalize({
54
+ ].includes(current.status)) this.#application.completeRun({
53
55
  runId: current.id,
54
- expectedVersion: current.version,
56
+ expectedRunVersion: current.version,
55
57
  terminalCode: "dispatch_failed",
56
58
  terminalMessage: error instanceof Error ? error.message : String(error),
57
59
  receipt: {
@@ -1,4 +1,5 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
+ import { enqueueTaskAttentionRequiredEvent } from "./task-change-events.js";
2
3
  import { randomUUID } from "node:crypto";
3
4
  //#region src/tasks/task-run-repository.ts
4
5
  const ACTIVE_RUN_STATUSES = [
@@ -237,6 +238,18 @@ var TaskRunRepository = class {
237
238
  reason: input.reason.trim(),
238
239
  resumeAt: input.resumeAt
239
240
  }, input.actor ?? { kind: "system" }, now);
241
+ if (input.kind === "user_input" || input.kind === "approval") {
242
+ const task = db.prepare("SELECT title, project_id FROM tasks WHERE task_id = ?").get(input.taskId);
243
+ if (task) enqueueTaskAttentionRequiredEvent(db, {
244
+ taskId: input.taskId,
245
+ taskTitle: task.title,
246
+ ...task.project_id ? { projectId: task.project_id } : {},
247
+ reason: input.kind,
248
+ detail: input.reason.trim(),
249
+ correlationId: id,
250
+ occurredAt: now
251
+ });
252
+ }
240
253
  });
241
254
  return this.requireWait(id);
242
255
  }
@@ -0,0 +1,13 @@
1
+ import type { KnowledgeSourceItem } from '../../knowledge/types.js';
2
+ import type { UserPersonIndexEntry } from './types.js';
3
+ type PersonSignal = {
4
+ name?: string;
5
+ email?: string;
6
+ username?: string;
7
+ };
8
+ export declare function personIdentityKey(signal: PersonSignal, sourceInstanceId: string): string | undefined;
9
+ export declare function personIdForIdentity(signal: PersonSignal, sourceInstanceId: string): string | undefined;
10
+ export declare function buildUserPeopleIndex(items: KnowledgeSourceItem[]): UserPersonIndexEntry[];
11
+ export declare function rebuildUserPeopleIndex(): UserPersonIndexEntry[];
12
+ export declare function ensureUserPeopleIndex(): void;
13
+ export {};