@xopcai/xopc 0.0.227 → 0.0.229

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 (249) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-BevwE16n.js → activity-store-M0nAY5XB.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BggVQrEZ.js → agent-avatar-dicebear-CsFAJTIN.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-DA4b6_0C.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-Bx_BpIt8.js → api-BywYxpaS.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-BtKP9n2r.js → app-management-settings-panel-7KcMWuah.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-BoH2NAUX.js → appearance-settings-Cqlw7Fif.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-CsnhdGZ2.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-DnceUyxm.js → archive-plugin-7_DXaMTP.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-Bh8_9gkr.js → attachment-load-q4pDpqkF.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-page-ZKeAAVxD.js → automations-page-BuTqjKGX.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-workspace-CD0E2ThS.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-Cy9er6w-.js → binary-plugins-DUce1Bsc.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-Cc0wWvNX.js → block-editor-D8Ejp-QR.js} +3 -3
  16. package/dist/gateway/static/root/assets/browser-settings-page-CY1k3MMd.js +8 -0
  17. package/dist/gateway/static/root/assets/{browser-workflow-inputs-BS7rpLLl.js → browser-workflow-inputs-8NC2qDRF.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-workflows-page-BdBVUZXm.js → browser-workflows-page-BDvZbR9v.js} +2 -2
  19. package/dist/gateway/static/root/assets/capabilities-settings-panel-D1VfotHQ.js +7 -0
  20. package/dist/gateway/static/root/assets/{capability-presets-api-D1Lqz0Tt.js → capability-presets-api-o0y9WgAI.js} +1 -1
  21. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DXHqRLpU.js +3 -0
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-CGuEl-l2.js → channel-recipient-api-C5ABr73b.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-settings-DrFL57M_.js +1 -0
  24. package/dist/gateway/static/root/assets/channels-status-swr-38FsOnR0.js +1 -0
  25. package/dist/gateway/static/root/assets/{chat-terminal-dock-CdX_7aQh.js → chat-terminal-dock-h1CKexNz.js} +2 -2
  26. package/dist/gateway/static/root/assets/connectors-page-DUAyfFCF.js +2 -0
  27. package/dist/gateway/static/root/assets/{cron-expression-BnSQc4Ek.js → cron-expression-DlaQQOId.js} +1 -1
  28. package/dist/gateway/static/root/assets/{date-picker-BbuG7zQ_.js → date-picker-8R1sXJKb.js} +1 -1
  29. package/dist/gateway/static/root/assets/{dependency-picker-snZeqOmG.js → dependency-picker-CWur0821.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-DJQ8JoR1.js → desktop-pet-Knl-zNDU.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-settings-B9txsRtv.js → desktop-pet-settings-CAQMLVtd.js} +1 -1
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-D7WKWkNB.js → directory-picker-path-field-Caaz4Rw5.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-D_7cOCj2.js → extension-debug-page-B965Mc15.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-Bwu47iUb.js → extension-page-C0UnkBi5.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-Dwg4dSZM.js → extension-settings-page-BUSIAZCR.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-api-jURUSte_.js → gateway-config-api-DkH0eEgy.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-swr-6SQXF1yR.js → gateway-config-swr-BWGjnmDl.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-settings-UklJOMLX.js → gateway-settings-BBgLDm6I.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-DqWaxyvZ.js → gateway-startup-retry-B_zrtccP.js} +1 -1
  40. package/dist/gateway/static/root/assets/{heartbeat-settings-Djs_nduD.js → heartbeat-settings-BtEHVXWD.js} +1 -1
  41. package/dist/gateway/static/root/assets/home-page-5SU2TNJj.js +1 -0
  42. package/dist/gateway/static/root/assets/{image-generation-api-Bxmt6LEr.js → image-generation-api-YKP2me5Z.js} +1 -1
  43. package/dist/gateway/static/root/assets/index-BNWhgmjs.js +70 -0
  44. package/dist/gateway/static/root/assets/index-D_hrEKXM.css +2 -0
  45. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-ByVsCH3d.js → keyboard-shortcuts-settings-CsRz8NrS.js} +1 -1
  46. package/dist/gateway/static/root/assets/local-app-workbench-page-DYPEUgpP.js +2 -0
  47. package/dist/gateway/static/root/assets/{local-apps-page-D19R-xsz.js → local-apps-page-CTBNUG4a.js} +1 -1
  48. package/dist/gateway/static/root/assets/{locale-store-Ci1Qi0Q-.js → locale-store-CXhjfe4q.js} +1 -1
  49. package/dist/gateway/static/root/assets/logs-page-B0P7rG2B.js +2 -0
  50. package/dist/gateway/static/root/assets/{management-settings-G0y7zmfY.js → management-settings-YqUAEwQo.js} +1 -1
  51. package/dist/gateway/static/root/assets/{markdown-split-B69d2HWt.js → markdown-split-dxpSCXb8.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-DD_PgVue.css → markdown-view-CLzTdVLW.css} +1 -1
  53. package/dist/gateway/static/root/assets/markdown-view-DvM5PWEH.js +201 -0
  54. package/dist/gateway/static/root/assets/{media-plugins-6TWEQgN_.js → media-plugins-CJ00i1Lg.js} +1 -1
  55. package/dist/gateway/static/root/assets/messages-BMdD0LWk.js +3 -0
  56. package/dist/gateway/static/root/assets/{notes-api-DLsnnZCS.js → notes-api-BFsOGYkd.js} +1 -1
  57. package/dist/gateway/static/root/assets/{notes-page-CkRJJKDm.js → notes-page-DEnbnkIt.js} +1 -1
  58. package/dist/gateway/static/root/assets/notes-workbench-DmVYu_Oc.js +4 -0
  59. package/dist/gateway/static/root/assets/{product-open-page-HxArIvoN.js → product-open-page-BHxo9U95.js} +1 -1
  60. package/dist/gateway/static/root/assets/project-detail-page-DV2nGmsq.js +1 -0
  61. package/dist/gateway/static/root/assets/projects-page-BNIFwFfc.js +1 -0
  62. package/dist/gateway/static/root/assets/{registry-api-lEar5FOV.js → registry-api-BFZrVYfy.js} +1 -1
  63. package/dist/gateway/static/root/assets/remote-access-hub-DLXqKiA_.js +1 -0
  64. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Bf03UNGD.js → runtime-tools-settings-panel-BpQ2GI-0.js} +1 -1
  65. package/dist/gateway/static/root/assets/{schema-form-CIXMBgx5.js → schema-form-HE1G26Lx.js} +1 -1
  66. package/dist/gateway/static/root/assets/sessions-page-DWaXf_25.js +1 -0
  67. package/dist/gateway/static/root/assets/settings-advanced-gate-JFCbnCL2.js +1 -0
  68. package/dist/gateway/static/root/assets/{settings-form-section-DgdB26mE.js → settings-form-section-CXAn0Xhy.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-loading-skeleton-efk5W6lG.js → settings-loading-skeleton-DsFu7Bcv.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-BHVf4gEP.js → settings-page-C4iib9nO.js} +1 -1
  71. package/dist/gateway/static/root/assets/{setup-status-panel-WK0dGNMv.js → setup-status-panel-BA83Wr1d.js} +1 -1
  72. package/dist/gateway/static/root/assets/{share-preview-page-Cod-w2qm.js → share-preview-page-BOGl3s6M.js} +1 -1
  73. package/dist/gateway/static/root/assets/{shares-settings-CUqQ31gt.js → shares-settings-Q2MdGaBi.js} +1 -1
  74. package/dist/gateway/static/root/assets/skill-reload-api-cFtfVeFU.js +1 -0
  75. package/dist/gateway/static/root/assets/{skills-page-DwCcipqK.js → skills-page-CdaFtNw9.js} +1 -1
  76. package/dist/gateway/static/root/assets/{skills-page.utils-3WSbtl3S.js → skills-page.utils-DYaAddUL.js} +1 -1
  77. package/dist/gateway/static/root/assets/{system-settings-panel-C5VxqC_s.js → system-settings-panel-Co7gilRd.js} +1 -1
  78. package/dist/gateway/static/root/assets/task-detail-page-5B1pswu8.js +1 -0
  79. package/dist/gateway/static/root/assets/{theme-store-Dd89g_ki.js → theme-store-DOdCjyuw.js} +1 -1
  80. package/dist/gateway/static/root/assets/{use-autosave-CeV8VJh2.js → use-autosave-DnnZtMpA.js} +1 -1
  81. package/dist/gateway/static/root/assets/user-context-page-Dr1FKjXv.js +1 -0
  82. package/dist/gateway/static/root/assets/{voice-api-key-field-EmJ-8aZu.js → voice-api-key-field-wetUpi3i.js} +1 -1
  83. package/dist/gateway/static/root/assets/{work-discovery-overlay-zsk6E3Mi.js → work-discovery-overlay-tbTWASJJ.js} +1 -1
  84. package/dist/gateway/static/root/assets/workflow-run-setup-panel-Dy0fDs8w.js +3 -0
  85. package/dist/gateway/static/root/assets/workflows-page-IVP3GSkI.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-instance-gateway.d.ts +1 -1
  92. package/dist/src/agent/agent-manager.d.ts +1 -1
  93. package/dist/src/agent/agent-manager.js +18 -3
  94. package/dist/src/agent/background-review/prompts.d.ts +8 -2
  95. package/dist/src/agent/background-review/prompts.js +25 -12
  96. package/dist/src/agent/background-review/run-background-review.d.ts +7 -2
  97. package/dist/src/agent/background-review/run-background-review.js +175 -160
  98. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +1 -0
  99. package/dist/src/agent/lifecycle/timeout-wrapper.js +7 -6
  100. package/dist/src/agent/mcp/mcp-transport-config.d.ts +1 -1
  101. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  102. package/dist/src/agent/memory/index.d.ts +1 -1
  103. package/dist/src/agent/memory/index.js +2 -2
  104. package/dist/src/agent/memory/manager.d.ts +1 -11
  105. package/dist/src/agent/memory/manager.js +0 -16
  106. package/dist/src/agent/memory/understanding/service.d.ts +0 -11
  107. package/dist/src/agent/memory/understanding/service.js +7 -173
  108. package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -3
  109. package/dist/src/agent/memory/user-context-coordinator.js +3 -66
  110. package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
  111. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +2 -2
  112. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +4 -4
  113. package/dist/src/agent/service/direct-turn-helpers.js +1 -1
  114. package/dist/src/agent/tools/create-share-tool.js +14 -1
  115. package/dist/src/agent/tools/exec-command.js +3 -3
  116. package/dist/src/agent/tools/execute-code-tool.js +1 -1
  117. package/dist/src/agent/tools/executor.js +2 -2
  118. package/dist/src/agent/tools/image-generate-tool.js +12 -0
  119. package/dist/src/agent/tools/send-media.js +11 -0
  120. package/dist/src/agent/tools/xopc-use-tool.js +1 -1
  121. package/dist/src/cli/command-loaders.js +1 -0
  122. package/dist/src/cli/command-manifest.js +4 -0
  123. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  124. package/dist/src/cli/commands/history.d.ts +1 -0
  125. package/dist/src/cli/commands/history.js +50 -0
  126. package/dist/src/config/agent-profile.js +1 -1
  127. package/dist/src/config/schema.d.ts +4 -0
  128. package/dist/src/config/schema.js +1 -0
  129. package/dist/src/connectors/connected-source-understanding.js +35 -21
  130. package/dist/src/connectors/learning-coordinator.js +1 -42
  131. package/dist/src/gateway/agents-admin.js +1 -1
  132. package/dist/src/gateway/chat-stream/mapper.d.ts +2 -0
  133. package/dist/src/gateway/chat-stream/mapper.js +5 -0
  134. package/dist/src/gateway/chat-stream/protocol.d.ts +3 -2
  135. package/dist/src/gateway/chat-stream/turn-outcome-collector.d.ts +16 -0
  136. package/dist/src/gateway/chat-stream/turn-outcome-collector.js +111 -0
  137. package/dist/src/gateway/hono/lib/static-ui.js +7 -5
  138. package/dist/src/gateway/hono/routes/index.js +2 -0
  139. package/dist/src/gateway/hono/routes/mobile.js +18 -40
  140. package/dist/src/gateway/hono/routes/notifications.d.ts +3 -0
  141. package/dist/src/gateway/hono/routes/notifications.js +45 -0
  142. package/dist/src/gateway/hono/routes/sessions.js +31 -0
  143. package/dist/src/gateway/hono/routes/tasks.js +1 -1
  144. package/dist/src/gateway/hono/routes/understanding-sources.js +4 -1
  145. package/dist/src/gateway/hono/routes/you.js +29 -11
  146. package/dist/src/gateway/service/agent-runner.js +6 -2
  147. package/dist/src/gateway/service/run-gateway-agent.js +58 -2
  148. package/dist/src/gateway/service/sessions-api.d.ts +6 -0
  149. package/dist/src/gateway/service/sessions-api.js +60 -3
  150. package/dist/src/gateway/service.d.ts +2 -2
  151. package/dist/src/gateway/service.js +8 -7
  152. package/dist/src/history/ctx/exporter.d.ts +14 -0
  153. package/dist/src/history/ctx/exporter.js +99 -0
  154. package/dist/src/history/ctx/format.d.ts +25 -0
  155. package/dist/src/history/ctx/format.js +282 -0
  156. package/dist/src/knowledge/index.d.ts +0 -1
  157. package/dist/src/knowledge/index.js +1 -2
  158. package/dist/src/media/session-references.d.ts +6 -0
  159. package/dist/src/media/session-references.js +24 -3
  160. package/dist/src/notifications/device-store.d.ts +16 -0
  161. package/dist/src/notifications/device-store.js +96 -0
  162. package/dist/src/notifications/planner.d.ts +6 -0
  163. package/dist/src/notifications/planner.js +160 -0
  164. package/dist/src/notifications/service.d.ts +18 -0
  165. package/dist/src/notifications/service.js +184 -0
  166. package/dist/src/notifications/store.d.ts +53 -0
  167. package/dist/src/notifications/store.js +184 -0
  168. package/dist/src/notifications/types.d.ts +29 -0
  169. package/dist/src/notifications/types.js +14 -0
  170. package/dist/src/session/client-history.d.ts +3 -1
  171. package/dist/src/session/client-history.js +13 -0
  172. package/dist/src/session/index.d.ts +1 -1
  173. package/dist/src/session/manager.d.ts +1 -0
  174. package/dist/src/session/manager.js +6 -0
  175. package/dist/src/session/store.d.ts +15 -0
  176. package/dist/src/session/store.js +58 -0
  177. package/dist/src/storage/sqlite/index.d.ts +2 -2
  178. package/dist/src/storage/sqlite/index.js +3 -3
  179. package/dist/src/storage/sqlite/migrations/136_product_notifications.sql +79 -0
  180. package/dist/src/storage/sqlite/migrations/137_understanding_status_audit.sql +71 -0
  181. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  182. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  183. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  184. package/dist/src/storage/sqlite/schema.js +1 -1
  185. package/dist/src/storage/sqlite/session-input-repository.d.ts +5 -0
  186. package/dist/src/storage/sqlite/session-input-repository.js +9 -1
  187. package/dist/src/storage/sqlite/user-context-repository.d.ts +16 -4
  188. package/dist/src/storage/sqlite/user-context-repository.js +56 -12
  189. package/dist/src/tasks/index.js +2 -2
  190. package/dist/src/tasks/task-application-service.js +42 -6
  191. package/dist/src/tasks/task-change-events.d.ts +9 -0
  192. package/dist/src/tasks/task-change-events.js +17 -1
  193. package/dist/src/tasks/task-outbox-dispatcher.js +5 -1
  194. package/dist/src/tasks/task-run-dispatcher.js +4 -2
  195. package/dist/src/tasks/task-run-repository.js +13 -0
  196. package/dist/src/user-context/config.d.ts +8 -0
  197. package/dist/src/user-context/config.js +3 -0
  198. package/dist/src/user-context/consolidation.js +16 -4
  199. package/dist/src/user-context/extraction/executor.d.ts +22 -0
  200. package/dist/src/user-context/extraction/executor.js +142 -0
  201. package/dist/src/user-context/extraction/registry.d.ts +1 -1
  202. package/dist/src/user-context/extraction/registry.js +5 -34
  203. package/dist/src/user-context/extraction/repair.js +4 -1
  204. package/dist/src/user-context/extraction/semantic.d.ts +18 -0
  205. package/dist/src/user-context/extraction/semantic.js +148 -0
  206. package/dist/src/user-context/understandingQuality.js +4 -0
  207. package/dist/src/work-discovery/analyzer.js +9 -4
  208. package/dist/src/work-discovery/service.d.ts +1 -0
  209. package/dist/src/work-discovery/service.js +5 -4
  210. package/dist/src/work-discovery/types.d.ts +1 -0
  211. package/package.json +14 -8
  212. package/dist/gateway/static/root/assets/agents-TJpjRHnr.js +0 -68
  213. package/dist/gateway/static/root/assets/apps-page-CZ4Da51l.js +0 -1
  214. package/dist/gateway/static/root/assets/automations-workspace-2Y00PvJU.js +0 -6
  215. package/dist/gateway/static/root/assets/browser-settings-page-CbJD27_j.js +0 -8
  216. package/dist/gateway/static/root/assets/capabilities-settings-panel-Dv8y6OuP.js +0 -7
  217. package/dist/gateway/static/root/assets/capability-presets-settings-panel-TZmjzWzt.js +0 -3
  218. package/dist/gateway/static/root/assets/channels-settings-g-68dutA.js +0 -1
  219. package/dist/gateway/static/root/assets/channels-status-swr-D7KsAeWh.js +0 -1
  220. package/dist/gateway/static/root/assets/connectors-page-DXA0rAvQ.js +0 -2
  221. package/dist/gateway/static/root/assets/home-page-i6bXCHdo.js +0 -1
  222. package/dist/gateway/static/root/assets/index-BBBE89vm.css +0 -2
  223. package/dist/gateway/static/root/assets/index-BsD5wygl.js +0 -74
  224. package/dist/gateway/static/root/assets/local-app-workbench-page-BCKEDMlP.js +0 -2
  225. package/dist/gateway/static/root/assets/logs-page-Cy9YQK7j.js +0 -2
  226. package/dist/gateway/static/root/assets/markdown-view-CBM-v2ph.js +0 -69
  227. package/dist/gateway/static/root/assets/mermaid-render-BbqDc_LQ.js +0 -134
  228. package/dist/gateway/static/root/assets/messages-CtOJwGW3.js +0 -3
  229. package/dist/gateway/static/root/assets/notes-workbench-B10T3hGX.js +0 -4
  230. package/dist/gateway/static/root/assets/project-detail-page-DsqRlgsy.js +0 -1
  231. package/dist/gateway/static/root/assets/projects-page-Bu2PN8OK.js +0 -1
  232. package/dist/gateway/static/root/assets/remote-access-hub-CXACdJ2f.js +0 -1
  233. package/dist/gateway/static/root/assets/sessions-page-CS2knWDT.js +0 -1
  234. package/dist/gateway/static/root/assets/settings-advanced-gate-ByFGk0q_.js +0 -1
  235. package/dist/gateway/static/root/assets/skill-reload-api-DweAPjI8.js +0 -1
  236. package/dist/gateway/static/root/assets/task-detail-page-DEj-BFMP.js +0 -1
  237. package/dist/gateway/static/root/assets/user-context-page-f7yh7aD5.js +0 -1
  238. package/dist/gateway/static/root/assets/workflow-run-setup-panel-CBAYvm3G.js +0 -3
  239. package/dist/gateway/static/root/assets/workflows-page-BN0JmHrG.js +0 -5
  240. package/dist/src/agent/memory/understanding/correction.d.ts +0 -3
  241. package/dist/src/agent/memory/understanding/correction.js +0 -39
  242. package/dist/src/knowledge/connected-understanding-pipeline.d.ts +0 -17
  243. package/dist/src/knowledge/connected-understanding-pipeline.js +0 -278
  244. package/dist/src/mobile/notification-service.d.ts +0 -9
  245. package/dist/src/mobile/notification-service.js +0 -156
  246. package/dist/src/mobile/notification-store.d.ts +0 -28
  247. package/dist/src/mobile/notification-store.js +0 -132
  248. package/dist/src/mobile/notification-types.d.ts +0 -37
  249. package/dist/src/mobile/notification-types.js +0 -9
@@ -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
  }
@@ -96,6 +96,10 @@ export declare const UserMemoryConfigSchema: z.ZodDefault<z.ZodObject<{
96
96
  }, z.core.$strict>>;
97
97
  export declare const UserUnderstandingConfigSchema: z.ZodDefault<z.ZodObject<{
98
98
  enabled: z.ZodDefault<z.ZodBoolean>;
99
+ processingPolicy: z.ZodDefault<z.ZodEnum<{
100
+ local_only: "local_only";
101
+ remote_allowed: "remote_allowed";
102
+ }>>;
99
103
  adaptiveCadence: z.ZodDefault<z.ZodBoolean>;
100
104
  reviewIntervalTurns: z.ZodDefault<z.ZodNumber>;
101
105
  maxHistoryMessages: z.ZodDefault<z.ZodNumber>;
@@ -204,6 +208,10 @@ export declare const UserContextConfigSchema: z.ZodDefault<z.ZodObject<{
204
208
  }, z.core.$strict>>;
205
209
  understanding: z.ZodDefault<z.ZodObject<{
206
210
  enabled: z.ZodDefault<z.ZodBoolean>;
211
+ processingPolicy: z.ZodDefault<z.ZodEnum<{
212
+ local_only: "local_only";
213
+ remote_allowed: "remote_allowed";
214
+ }>>;
207
215
  adaptiveCadence: z.ZodDefault<z.ZodBoolean>;
208
216
  reviewIntervalTurns: z.ZodDefault<z.ZodNumber>;
209
217
  maxHistoryMessages: z.ZodDefault<z.ZodNumber>;
@@ -91,12 +91,14 @@ var init_config = __esmMin((() => {
91
91
  });
92
92
  UserUnderstandingConfigSchema = z.object({
93
93
  enabled: z.boolean().default(true),
94
+ processingPolicy: z.enum(["local_only", "remote_allowed"]).default("remote_allowed"),
94
95
  adaptiveCadence: z.boolean().default(true),
95
96
  reviewIntervalTurns: z.number().int().min(1).max(1e3).default(10),
96
97
  maxHistoryMessages: z.number().int().min(1).max(200).default(80),
97
98
  maxDurationMs: z.number().int().min(1e3).max(6e5).default(12e4)
98
99
  }).strict().default({
99
100
  enabled: true,
101
+ processingPolicy: "remote_allowed",
100
102
  adaptiveCadence: true,
101
103
  reviewIntervalTurns: 10,
102
104
  maxHistoryMessages: 80,
@@ -161,6 +163,7 @@ var init_config = __esmMin((() => {
161
163
  },
162
164
  understanding: {
163
165
  enabled: true,
166
+ processingPolicy: "remote_allowed",
164
167
  adaptiveCadence: true,
165
168
  reviewIntervalTurns: 10,
166
169
  maxHistoryMessages: 80,
@@ -97,7 +97,10 @@ async function runContextConsolidation(input) {
97
97
  const evidenceCount = distinctEvidenceCount(understanding.id, "supports");
98
98
  const contradictionCount = distinctEvidenceCount(understanding.id, "contradicts");
99
99
  if (understanding.validTo != null && understanding.validTo <= now || understanding.expiresAt != null && understanding.expiresAt <= now) {
100
- setUnderstandingStatus(understanding.id, "stale");
100
+ setUnderstandingStatus(understanding.id, "stale", {
101
+ actorType: "runtime",
102
+ source: "consolidation-expired"
103
+ });
101
104
  recordContextConsolidationDecision({
102
105
  runId: run.runId,
103
106
  understandingId: understanding.id,
@@ -110,7 +113,10 @@ async function runContextConsolidation(input) {
110
113
  continue;
111
114
  }
112
115
  if (contradictionCount > 0) {
113
- setUnderstandingStatus(understanding.id, "needs_review");
116
+ setUnderstandingStatus(understanding.id, "needs_review", {
117
+ actorType: "runtime",
118
+ source: "consolidation-contradiction"
119
+ });
114
120
  recordContextConsolidationDecision({
115
121
  runId: run.runId,
116
122
  understandingId: understanding.id,
@@ -123,7 +129,10 @@ async function runContextConsolidation(input) {
123
129
  continue;
124
130
  }
125
131
  if (understanding.status === "active" && understanding.reviewAt != null && understanding.reviewAt <= now) {
126
- setUnderstandingStatus(understanding.id, "needs_review");
132
+ setUnderstandingStatus(understanding.id, "needs_review", {
133
+ actorType: "runtime",
134
+ source: "consolidation-review-due"
135
+ });
127
136
  recordContextConsolidationDecision({
128
137
  runId: run.runId,
129
138
  understandingId: understanding.id,
@@ -136,7 +145,10 @@ async function runContextConsolidation(input) {
136
145
  continue;
137
146
  }
138
147
  if (understanding.status === "candidate" && evidenceCount >= resolved.minEvidenceSources) {
139
- setUnderstandingStatus(understanding.id, "needs_review");
148
+ setUnderstandingStatus(understanding.id, "needs_review", {
149
+ actorType: "runtime",
150
+ source: "consolidation-evidence-threshold"
151
+ });
140
152
  recordContextConsolidationDecision({
141
153
  runId: run.runId,
142
154
  understandingId: understanding.id,
@@ -0,0 +1,22 @@
1
+ import type { Config } from '../../config/schema.js';
2
+ import type { MemoryManager } from '../../agent/memory/manager.js';
3
+ import type { UnderstandingReviewResult } from '../../agent/memory/understanding/types.js';
4
+ import { type ExtractorId } from './registry.js';
5
+ import type { SemanticEvidence, SemanticUnderstandingInterpretation } from './semantic.js';
6
+ export type UnderstandingEvidenceMessage = SemanticEvidence & {
7
+ createdAt: number;
8
+ message: unknown;
9
+ };
10
+ export declare function emptyUnderstandingReview(): UnderstandingReviewResult;
11
+ export declare function executeUnderstandingInterpretation(params: {
12
+ interpretation: SemanticUnderstandingInterpretation;
13
+ evidence: UnderstandingEvidenceMessage[];
14
+ extractionRunId: string;
15
+ extractorId: ExtractorId;
16
+ sessionKey: string;
17
+ turnId?: string;
18
+ memoryManager: MemoryManager;
19
+ getConfig: () => Config | undefined;
20
+ reviewSource: 'turn' | 'background';
21
+ processingPolicy: 'local_only' | 'remote_allowed';
22
+ }): Promise<UnderstandingReviewResult>;
@@ -0,0 +1,142 @@
1
+ import { createContextEvidence, getTurnPersonalization, getUnderstanding, recordContextFeedback, rejectUnderstanding, setUnderstandingStatus } from "../../storage/sqlite/user-context-repository.js";
2
+ import { getSessionMetadata } from "../../storage/sqlite/session-repository.js";
3
+ import "../../storage/sqlite/index.js";
4
+ import { extractionInputHash } from "./registry.js";
5
+ //#region src/user-context/extraction/executor.ts
6
+ function emptyUnderstandingReview() {
7
+ return {
8
+ proposed: 0,
9
+ created: 0,
10
+ deduplicated: 0,
11
+ rejected: 0,
12
+ createdRecords: [],
13
+ writeOutputs: []
14
+ };
15
+ }
16
+ function resolveWritePolicy(config) {
17
+ const memory = config?.userContext.memory;
18
+ if (!memory || memory.mode === "off" || memory.mode === "readOnly") return "deny";
19
+ return memory.writePolicy?.understanding ?? (memory.mode === "auto" ? "allow" : "confirm");
20
+ }
21
+ function recordTargetFeedback(turnId, targetIds, rating, reason) {
22
+ if (!turnId || targetIds.length === 0) return;
23
+ const personalization = getTurnPersonalization(turnId);
24
+ if (!personalization) return;
25
+ for (const objectId of targetIds) recordContextFeedback({
26
+ turnId,
27
+ runId: personalization.runId,
28
+ objectType: "understanding",
29
+ objectId,
30
+ rating,
31
+ reason
32
+ });
33
+ }
34
+ async function executeUnderstandingInterpretation(params) {
35
+ const { interpretation } = params;
36
+ const result = emptyUnderstandingReview();
37
+ const targets = interpretation.targetUnderstandingIds.flatMap((id) => {
38
+ const item = getUnderstanding(id);
39
+ return item ? [item] : [];
40
+ });
41
+ const targetIds = targets.map((item) => item.id);
42
+ const policy = resolveWritePolicy(params.getConfig());
43
+ if (interpretation.intent === "memory_forget") {
44
+ for (const target of targets) {
45
+ const rejected = rejectUnderstanding(target.id, "explicit user forget request", "user");
46
+ result.writeOutputs.push({
47
+ candidateKey: rejected.canonicalKey,
48
+ objectId: rejected.id,
49
+ versionId: rejected.versionId,
50
+ outcome: "rejected"
51
+ });
52
+ }
53
+ result.rejected += targets.length;
54
+ recordTargetFeedback(params.turnId, targetIds, "irrelevant", "explicit_user_forget_request");
55
+ return result;
56
+ }
57
+ if (interpretation.intent === "memory_confirm" && policy !== "deny") {
58
+ for (const target of targets) {
59
+ const confirmed = setUnderstandingStatus(target.id, "active", {
60
+ explicitness: "explicit",
61
+ confidence: 1,
62
+ actorType: "user",
63
+ source: "semantic-memory-confirmation"
64
+ });
65
+ result.createdRecords.push({
66
+ id: confirmed.id,
67
+ content: confirmed.statement,
68
+ kind: confirmed.kind,
69
+ status: confirmed.status
70
+ });
71
+ result.writeOutputs.push({
72
+ candidateKey: confirmed.canonicalKey,
73
+ objectId: confirmed.id,
74
+ versionId: confirmed.versionId,
75
+ outcome: "deduplicated"
76
+ });
77
+ result.deduplicated += 1;
78
+ }
79
+ recordTargetFeedback(params.turnId, targetIds, "helpful", "explicit_user_confirmation");
80
+ }
81
+ if (interpretation.intent === "memory_correct" && targets.length && interpretation.candidates.length === 0) {
82
+ for (const target of targets) setUnderstandingStatus(target.id, "needs_review", {
83
+ actorType: "user",
84
+ source: "semantic-memory-correction"
85
+ });
86
+ recordTargetFeedback(params.turnId, targetIds, "wrong", "explicit_user_correction_without_replacement");
87
+ return result;
88
+ }
89
+ if (interpretation.candidates.length === 0) return result;
90
+ if (policy === "deny") {
91
+ result.proposed = interpretation.candidates.length;
92
+ result.rejected = interpretation.candidates.length;
93
+ return result;
94
+ }
95
+ const evidenceByRef = new Map(params.evidence.map((entry) => [entry.ref, entry]));
96
+ const candidates = interpretation.candidates.map((candidate) => ({
97
+ ...candidate,
98
+ explicitness: policy === "confirm" && candidate.explicitness === "explicit" ? "observed" : candidate.explicitness
99
+ }));
100
+ const evidenceIdsByRef = /* @__PURE__ */ new Map();
101
+ for (const ref of new Set(candidates.flatMap((candidate) => candidate.evidenceRefs))) {
102
+ const entry = evidenceByRef.get(ref);
103
+ if (!entry || entry.role !== "user") continue;
104
+ const evidence = createContextEvidence({
105
+ sourceType: "conversation",
106
+ sourceRef: `session:${params.sessionKey}:entry:${entry.ref}`,
107
+ sourceRunId: params.extractionRunId,
108
+ sessionId: params.sessionKey,
109
+ ...params.turnId ? { turnId: params.turnId } : {},
110
+ messageId: entry.ref,
111
+ contentHash: extractionInputHash(JSON.stringify(entry.message)),
112
+ retentionPolicy: "derived_only",
113
+ processingPolicy: params.processingPolicy,
114
+ extractorId: params.extractorId,
115
+ extractorVersion: "1",
116
+ trustLevel: "owner",
117
+ observedAt: entry.createdAt
118
+ });
119
+ evidenceIdsByRef.set(ref, evidence.id);
120
+ }
121
+ const projectId = getSessionMetadata(params.sessionKey)?.projectId;
122
+ for (const candidate of candidates) {
123
+ const applied = await params.memoryManager.applyUnderstandingCandidates([candidate], {
124
+ sessionKey: params.sessionKey,
125
+ ...projectId ? { projectId } : {},
126
+ evidenceIds: candidate.evidenceRefs.flatMap((ref) => evidenceIdsByRef.get(ref) ?? []),
127
+ reviewSource: params.reviewSource,
128
+ extractionRunId: params.extractionRunId,
129
+ ...interpretation.intent === "memory_correct" ? { supersedesRecordIds: targetIds } : {}
130
+ });
131
+ result.proposed += applied.proposed;
132
+ result.created += applied.created;
133
+ result.deduplicated += applied.deduplicated;
134
+ result.rejected += applied.rejected;
135
+ result.createdRecords.push(...applied.createdRecords);
136
+ result.writeOutputs.push(...applied.writeOutputs ?? []);
137
+ }
138
+ if (interpretation.intent === "memory_correct" && result.created > 0) recordTargetFeedback(params.turnId, targetIds, "wrong", "explicit_user_correction_with_replacement");
139
+ return result;
140
+ }
141
+ //#endregion
142
+ export { emptyUnderstandingReview, executeUnderstandingInterpretation };
@@ -1,6 +1,6 @@
1
1
  import { type ContextExtractionRun } from '../../storage/sqlite/index.js';
2
2
  import type { UnderstandingKind } from '../domain.js';
3
- export type ExtractorId = 'explicit-command' | 'deterministic-signal' | 'transcript-synthesis' | 'connector-structural' | 'connector-semantic';
3
+ export type ExtractorId = 'turn-semantics' | 'transcript-synthesis' | 'connector-semantic';
4
4
  export type ExtractorDefinition = {
5
5
  id: ExtractorId;
6
6
  version: string;
@@ -14,62 +14,33 @@ const UNDERSTANDING_KINDS = [
14
14
  "derived_insight"
15
15
  ];
16
16
  const DEFINITIONS = {
17
- "explicit-command": {
18
- id: "explicit-command",
17
+ "turn-semantics": {
18
+ id: "turn-semantics",
19
19
  version: "1",
20
20
  inputKinds: ["conversation"],
21
21
  requiredProcessingPolicy: "local_only",
22
22
  authorityCeiling: "user_explicit",
23
23
  candidateKinds: UNDERSTANDING_KINDS,
24
24
  maxAutomaticStatus: "active",
25
- timeoutMs: 500
26
- },
27
- "deterministic-signal": {
28
- id: "deterministic-signal",
29
- version: "1",
30
- inputKinds: ["conversation", "task"],
31
- requiredProcessingPolicy: "local_only",
32
- authorityCeiling: "user_observed",
33
- candidateKinds: UNDERSTANDING_KINDS,
34
- maxAutomaticStatus: "candidate",
35
- timeoutMs: 500
25
+ timeoutMs: 3e4
36
26
  },
37
27
  "transcript-synthesis": {
38
28
  id: "transcript-synthesis",
39
29
  version: "1",
40
30
  inputKinds: ["conversation"],
41
- requiredProcessingPolicy: "remote_allowed",
31
+ requiredProcessingPolicy: "local_only",
42
32
  authorityCeiling: "system_inferred",
43
33
  candidateKinds: UNDERSTANDING_KINDS,
44
34
  maxAutomaticStatus: "candidate",
45
35
  timeoutMs: 3e4
46
36
  },
47
- "connector-structural": {
48
- id: "connector-structural",
49
- version: "1",
50
- inputKinds: ["connector"],
51
- requiredProcessingPolicy: "local_only",
52
- authorityCeiling: "external_untrusted",
53
- candidateKinds: [
54
- "project_context",
55
- "current_state",
56
- "routine"
57
- ],
58
- maxAutomaticStatus: "candidate",
59
- timeoutMs: 1e4
60
- },
61
37
  "connector-semantic": {
62
38
  id: "connector-semantic",
63
39
  version: "1",
64
40
  inputKinds: ["connector", "file"],
65
41
  requiredProcessingPolicy: "remote_allowed",
66
42
  authorityCeiling: "external_untrusted",
67
- candidateKinds: [
68
- "preference",
69
- "routine",
70
- "current_state",
71
- "project_context"
72
- ],
43
+ candidateKinds: ["preference", "routine"],
73
44
  maxAutomaticStatus: "candidate",
74
45
  timeoutMs: 3e4
75
46
  }
@@ -39,7 +39,10 @@ function repairExtractedContext(filter) {
39
39
  });
40
40
  continue;
41
41
  }
42
- setUnderstandingStatus(output.objectId, "archived");
42
+ setUnderstandingStatus(output.objectId, "archived", {
43
+ actorType: "runtime",
44
+ source: "context-extraction-repair"
45
+ });
43
46
  } else {
44
47
  const focus = listUserFocuses().find((item) => item.id === output.objectId);
45
48
  if (!focus) continue;
@@ -0,0 +1,18 @@
1
+ import type { UnderstandingCandidate } from '../../agent/memory/understanding/types.js';
2
+ export declare const UNDERSTANDING_INTENTS: readonly ["memory_create", "memory_query", "memory_confirm", "memory_correct", "memory_forget", "user_assertion", "task_request", "none"];
3
+ export type UnderstandingIntent = typeof UNDERSTANDING_INTENTS[number];
4
+ export type SemanticEvidence = {
5
+ ref: string;
6
+ role: 'user' | 'assistant';
7
+ text: string;
8
+ };
9
+ export type SemanticUnderstandingCandidate = UnderstandingCandidate & {
10
+ evidenceRefs: string[];
11
+ };
12
+ export type SemanticUnderstandingInterpretation = {
13
+ intent: UnderstandingIntent;
14
+ candidates: SemanticUnderstandingCandidate[];
15
+ targetUnderstandingIds: string[];
16
+ abstentionReason?: string;
17
+ };
18
+ export declare function parseSemanticUnderstanding(raw: string, evidence: SemanticEvidence[], allowedTargetIds?: string[]): SemanticUnderstandingInterpretation | null;
@@ -0,0 +1,148 @@
1
+ import { UNDERSTANDING_KINDS } from "../domain.js";
2
+ import { isDurableUnderstandingCandidate } from "../understandingQuality.js";
3
+ import { inferMemorySensitivity } from "../../agent/memory/sensitivity.js";
4
+ import { z } from "zod";
5
+ //#region src/user-context/extraction/semantic.ts
6
+ const UNDERSTANDING_INTENTS = [
7
+ "memory_create",
8
+ "memory_query",
9
+ "memory_confirm",
10
+ "memory_correct",
11
+ "memory_forget",
12
+ "user_assertion",
13
+ "task_request",
14
+ "none"
15
+ ];
16
+ const EvidenceSchema = z.object({
17
+ ref: z.string().min(1),
18
+ quote: z.string().min(1)
19
+ }).strict();
20
+ const CandidateSchema = z.object({
21
+ factKey: z.string().regex(/^[a-z0-9][a-z0-9:-]{2,119}$/),
22
+ statement: z.string().min(4).max(600),
23
+ kind: z.enum(UNDERSTANDING_KINDS),
24
+ explicitness: z.enum([
25
+ "explicit",
26
+ "observed",
27
+ "inferred"
28
+ ]),
29
+ durability: z.enum([
30
+ "ephemeral",
31
+ "durable",
32
+ "recurring"
33
+ ]),
34
+ scopeHint: z.enum([
35
+ "global",
36
+ "workspace",
37
+ "project",
38
+ "session"
39
+ ]),
40
+ confidence: z.number().min(0).max(1),
41
+ importance: z.number().min(0).max(1),
42
+ sensitivity: z.enum([
43
+ "normal",
44
+ "personal",
45
+ "secret",
46
+ "regulated"
47
+ ]).optional(),
48
+ disclosurePolicy: z.enum([
49
+ "silent",
50
+ "referenceable",
51
+ "ask_before_reference"
52
+ ]).optional(),
53
+ evidence: z.array(EvidenceSchema).min(1).max(8),
54
+ selfContained: z.boolean(),
55
+ unresolvedReferences: z.array(z.string()).max(8)
56
+ }).strict();
57
+ const InterpretationSchema = z.object({
58
+ intent: z.enum(UNDERSTANDING_INTENTS),
59
+ candidates: z.array(CandidateSchema).max(8),
60
+ targetUnderstandingIds: z.array(z.string().min(1)).max(8),
61
+ abstentionReason: z.string().max(500).optional()
62
+ }).strict();
63
+ const CANDIDATE_INTENTS = new Set([
64
+ "memory_create",
65
+ "memory_confirm",
66
+ "memory_correct",
67
+ "user_assertion"
68
+ ]);
69
+ const QUESTION_END = /[??]\s*$/u;
70
+ function extractJson(raw) {
71
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(raw)?.[1];
72
+ const start = raw.indexOf("{");
73
+ const end = raw.lastIndexOf("}");
74
+ const candidate = fenced ?? (start >= 0 && end > start ? raw.slice(start, end + 1) : raw);
75
+ return JSON.parse(candidate);
76
+ }
77
+ function comparableText(value) {
78
+ return value.normalize("NFKC").toLocaleLowerCase().replace(/\s+/g, " ").trim();
79
+ }
80
+ function quoteIsGrounded(quote, source) {
81
+ return comparableText(source).includes(comparableText(quote));
82
+ }
83
+ function bigramSimilarity(left, right) {
84
+ const pairs = (value) => {
85
+ const normalized = comparableText(value).replace(/[^\p{L}\p{N}]/gu, "");
86
+ const output = /* @__PURE__ */ new Set();
87
+ for (let index = 0; index < normalized.length - 1; index += 1) output.add(normalized.slice(index, index + 2));
88
+ return output;
89
+ };
90
+ const a = pairs(left);
91
+ const b = pairs(right);
92
+ if (!a.size || !b.size) return 0;
93
+ let overlap = 0;
94
+ for (const pair of a) if (b.has(pair)) overlap += 1;
95
+ return 2 * overlap / (a.size + b.size);
96
+ }
97
+ function effectiveExplicitness(intent, declared) {
98
+ if (intent === "memory_create" || intent === "memory_correct" || intent === "memory_confirm") return declared;
99
+ return declared === "explicit" ? "observed" : declared;
100
+ }
101
+ function parseSemanticUnderstanding(raw, evidence, allowedTargetIds = []) {
102
+ let parsed;
103
+ try {
104
+ const result = InterpretationSchema.safeParse(extractJson(raw));
105
+ if (!result.success) return null;
106
+ parsed = result.data;
107
+ } catch {
108
+ return null;
109
+ }
110
+ const evidenceByRef = new Map(evidence.map((item) => [item.ref, item]));
111
+ const allowedTargets = new Set(allowedTargetIds);
112
+ const candidates = [];
113
+ if (CANDIDATE_INTENTS.has(parsed.intent)) for (const item of parsed.candidates) {
114
+ if (!item.selfContained || item.unresolvedReferences.length > 0) continue;
115
+ if (QUESTION_END.test(item.statement)) continue;
116
+ if (!isDurableUnderstandingCandidate(item.kind, item.statement)) continue;
117
+ if (!item.evidence.every((claim) => {
118
+ const source = evidenceByRef.get(claim.ref);
119
+ return source?.role === "user" && quoteIsGrounded(claim.quote, source.text);
120
+ })) continue;
121
+ if (item.explicitness === "explicit") {
122
+ const quotedText = item.evidence.map((claim) => claim.quote).join(" ");
123
+ if (bigramSimilarity(item.statement, quotedText) < .45) continue;
124
+ }
125
+ const evidenceRefs = [...new Set(item.evidence.map((claim) => claim.ref))];
126
+ candidates.push({
127
+ kind: item.kind,
128
+ content: item.statement.trim(),
129
+ canonicalKey: `understanding:${item.kind}:${item.factKey}`,
130
+ confidence: item.confidence,
131
+ importance: item.importance,
132
+ explicitness: effectiveExplicitness(parsed.intent, item.explicitness),
133
+ durability: item.durability,
134
+ sensitivity: item.sensitivity ?? inferMemorySensitivity(item.statement),
135
+ disclosurePolicy: item.disclosurePolicy ?? "referenceable",
136
+ payload: { scopeHint: item.scopeHint },
137
+ evidenceRefs
138
+ });
139
+ }
140
+ return {
141
+ intent: parsed.intent,
142
+ candidates,
143
+ targetUnderstandingIds: [...new Set(parsed.targetUnderstandingIds.filter((id) => allowedTargets.has(id)))],
144
+ ...parsed.abstentionReason ? { abstentionReason: parsed.abstentionReason } : {}
145
+ };
146
+ }
147
+ //#endregion
148
+ export { UNDERSTANDING_INTENTS, parseSemanticUnderstanding };
@@ -2,6 +2,8 @@
2
2
  const INCOMPLETE_PREFIX = /^(?:and\b|also\b|then\b|plus\b|as\s+well\s+as\b|which\b|that\b|并且|并将|以及|然后|同时|另外|还有|而且|或者|的事项|事项[,,、::]?)/i;
3
3
  const DEFERRED_OR_NEGATED_ACTION = /(?:not\s+now|no\s+need\s+to|do\s+not\s+(?:start|proceed)|don't\s+(?:start|proceed)|later\s+maybe|暂时不用|暂时不(?:用|要|开始)?|先不用|无需|不用开始|不要推进|不需要推进)/i;
4
4
  const ONE_OFF_ACTION_PREFIX = /^(?:please\s+|update\b|investigate\b|summari[sz]e\b|fix\b|write\b|add\b|record\b|track\b)|^(?:请|帮我|更新|调查|汇总|整理|修复|写入|加入|记录|跟进)/i;
5
+ const DURABLE_MARKER = /(?:从现在起|今后|以后|长期|每次|每回|始终|一直|默认|下次|教训|原则|习惯|from now on|going forward|long[- ]term|every time|always|by default|next time|lesson|principle|habit)/i;
6
+ const TURN_EXECUTION_INSTRUCTION = /(?:不要再问|别再问|直接执行|立即执行|当前任务|这次|本次|现在就|do not ask|don't ask|just do it|execute directly|proceed now|this task|this time)/i;
5
7
  function meaningfulLength(content) {
6
8
  return content.normalize("NFKC").replace(/[\p{P}\p{S}\s]/gu, "").length;
7
9
  }
@@ -15,6 +17,8 @@ function isActionableUnderstandingContent(content) {
15
17
  function isDurableUnderstandingCandidate(kind, content) {
16
18
  if (!isStandaloneUnderstandingContent(content)) return false;
17
19
  if ((kind === "commitment" || kind === "long_term_goal") && ONE_OFF_ACTION_PREFIX.test(content.trim())) return false;
20
+ if (kind === "task_lesson" && !DURABLE_MARKER.test(content)) return false;
21
+ if (kind === "boundary" && TURN_EXECUTION_INSTRUCTION.test(content) && !DURABLE_MARKER.test(content)) return false;
18
22
  return true;
19
23
  }
20
24
  function comparableText(content) {
@@ -83,7 +83,9 @@ function validateProfileCandidate(value, allowedRefs) {
83
83
  if (!value || typeof value !== "object") return null;
84
84
  const item = value;
85
85
  const category = item.category === "role" || item.category === "responsibility" || item.category === "preference" || item.category === "routine" ? item.category : void 0;
86
- if (!category || typeof item.statement !== "string") return null;
86
+ if (!category || typeof item.statement !== "string" || typeof item.factKey !== "string") return null;
87
+ const factKey = item.factKey.trim().toLocaleLowerCase();
88
+ if (!/^[a-z0-9][a-z0-9:-]{2,119}$/.test(factKey)) return null;
87
89
  const statement = item.statement.trim().slice(0, 500);
88
90
  if (statement.length < 4) return null;
89
91
  const confidence = item.confidence === "high" || item.confidence === "low" ? item.confidence : "medium";
@@ -92,6 +94,7 @@ function validateProfileCandidate(value, allowedRefs) {
92
94
  return {
93
95
  id: randomUUID(),
94
96
  category,
97
+ factKey,
95
98
  statement,
96
99
  confidence,
97
100
  evidence: strings(item.evidence, 4).map((entry) => entry.slice(0, 300)),
@@ -183,7 +186,8 @@ async function analyzeWorkContext(input) {
183
186
  "Return only one JSON object with projectSummary, currentState, uncertainties, suggestions, profileCandidates, workThreads, conversationStarter, lowConfidence, and contextQuestion.",
184
187
  "profileCandidates contains only user-specific role, responsibility, preference, or routine facts that are directly supported and would remain useful outside this repository snapshot.",
185
188
  "Do not put the project stack, packages, architecture, deployment setup, repository conventions, or current task in profileCandidates. Put project facts in projectSummary/currentState and current work in workThreads.",
186
- "Each profile candidate has category (role, responsibility, preference, or routine), statement, confidence, and evidence.",
189
+ "Each profile candidate has category (role, responsibility, preference, or routine), factKey, statement, confidence, and evidence.",
190
+ "factKey is a stable language-neutral lowercase identifier such as workflow:code-review:github; equivalent facts in different languages must use the same factKey.",
187
191
  USER_FACING_UNDERSTANDING_WRITING_GUIDANCE,
188
192
  "Do not infer sensitive traits, identity, health, finances, political views, or anything not directly supported by the work evidence.",
189
193
  "workThreads contains every distinct evidence-backed work stream with topicKey, title, summary, horizon, status, confidence, and evidenceRefs. Do not merge unrelated streams to force a fixed count.",
@@ -288,7 +292,8 @@ async function analyzeUnderstandingBatch(input) {
288
292
  "Return only one JSON object with profileCandidates and workThreads.",
289
293
  "Return at most 8 profileCandidates and at most 8 workThreads. Prefer the strongest distinct findings.",
290
294
  "profileCandidates contains only user-specific role, responsibility, preference, or routine facts with direct support. Current tasks belong in workThreads; project technologies, packages, architecture, and repository conventions are not user facts.",
291
- "Each profile candidate has category (role, responsibility, preference, or routine), statement, confidence, evidence, and evidenceRefs.",
295
+ "Each profile candidate has category (role, responsibility, preference, or routine), factKey, statement, confidence, evidence, and evidenceRefs.",
296
+ "factKey is a stable language-neutral lowercase identifier such as workflow:code-review:github; equivalent facts in different languages must use the same factKey.",
292
297
  USER_FACING_UNDERSTANDING_WRITING_GUIDANCE,
293
298
  "Each work thread has topicKey, title, summary, status, horizon, confidence, and evidenceRefs.",
294
299
  "Every evidenceRefs value must be one of the supplied refs. Omit anything without direct support.",
@@ -359,7 +364,7 @@ function mergeProfileCandidates(candidates) {
359
364
  high: 3
360
365
  };
361
366
  for (const candidate of candidates) {
362
- const key = `${candidate.category}:${candidate.statement.toLocaleLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim()}`;
367
+ const key = `${candidate.category}:${candidate.factKey}`;
363
368
  const existing = merged.get(key);
364
369
  if (!existing) {
365
370
  merged.set(key, candidate);
@@ -68,6 +68,7 @@ export declare class WorkDiscoveryService {
68
68
  understandingId: string;
69
69
  id: string;
70
70
  category: "role" | "responsibility" | "preference" | "routine";
71
+ factKey: string;
71
72
  statement: string;
72
73
  confidence: "high" | "medium" | "low";
73
74
  evidence: string[];
@@ -164,8 +164,7 @@ function understandingKind(category) {
164
164
  return "project_context";
165
165
  }
166
166
  function understandingKey(candidate) {
167
- const normalized = candidate.statement.toLocaleLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
168
- return `work-discovery:${candidate.category}:${createHash("sha256").update(normalized).digest("hex").slice(0, 20)}`;
167
+ return `understanding:${understandingKind(candidate.category)}:${candidate.factKey}`;
169
168
  }
170
169
  function sameScope(left, right) {
171
170
  return left.type === right.type && left.id === right.id;
@@ -202,7 +201,7 @@ function decideUnderstanding(input, expectedSourcePrefix) {
202
201
  const current = getUnderstanding(input.understandingId);
203
202
  const owned = current && listUnderstandingEvidence(current.id).some((evidence) => evidence.sourceRef.startsWith(expectedSourcePrefix));
204
203
  if (!current || !owned || current.status === "archived") return void 0;
205
- if (input.status === "rejected") return rejectUnderstanding(current.id, "Rejected from work discovery review");
204
+ if (input.status === "rejected") return rejectUnderstanding(current.id, "Rejected from work discovery review", "user");
206
205
  const statement = input.status === "edited" ? input.statement?.trim().slice(0, 500) : void 0;
207
206
  if (input.status === "edited" && !statement) return void 0;
208
207
  return setUnderstandingStatus((statement ? reviseUnderstanding(current.id, statement, {
@@ -211,7 +210,9 @@ function decideUnderstanding(input, expectedSourcePrefix) {
211
210
  changeReason: "Edited during work discovery review"
212
211
  }) : current).id, "active", {
213
212
  explicitness: "explicit",
214
- confidence: 1
213
+ confidence: 1,
214
+ actorType: "user",
215
+ source: "work-discovery-review"
215
216
  });
216
217
  }
217
218
  function sessionMetadata(agentId, peerId) {
@@ -167,6 +167,7 @@ export interface WorkDiscoveryProfileCandidate {
167
167
  id: string;
168
168
  understandingId?: string;
169
169
  category: 'role' | 'responsibility' | 'preference' | 'routine';
170
+ factKey: string;
170
171
  statement: string;
171
172
  confidence: 'high' | 'medium' | 'low';
172
173
  evidence: string[];