@xopcai/xopc 0.0.374 → 0.0.375

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 (254) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/telegram/xopc.extension.json +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BrA20N2m.js → agent-avatar-dicebear-B2Popl59.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BZrOdxou.js → agent-defaults-settings-panel-BnF3HrHY.js} +1 -1
  6. package/dist/gateway/static/root/assets/{agent-plugin-dialog-B3ux1AhY.js → agent-plugin-dialog-CwP6OrUs.js} +1 -1
  7. package/dist/gateway/static/root/assets/{agents-CXlXFU6M.js → agents-n3IremKl.js} +3 -3
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-BXahEVPm.js → app-management-settings-panel-DNkXIPxK.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-CE2qJMkD.js → appearance-settings-p1_NpH8a.js} +1 -1
  10. package/dist/gateway/static/root/assets/{apps-page-DGVOnCxZ.js → apps-page-CP2u5iEh.js} +1 -1
  11. package/dist/gateway/static/root/assets/{automations-page-BWL7eT4_.js → automations-page-CjYiGOov.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-workspace-BxIShhkZ.js → automations-workspace-BAHiUQbe.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-3n446-mX.js → block-editor-DiOou1HO.js} +1 -1
  14. package/dist/gateway/static/root/assets/{browser-automations-page-BmTK7Gtn.js → browser-automations-page-nArl2uwX.js} +1 -1
  15. package/dist/gateway/static/root/assets/{browser-settings-page-DBQ-WETQ.js → browser-settings-page-Bd8tF-gg.js} +1 -1
  16. package/dist/gateway/static/root/assets/{capabilities-page-D-nQ6Zzi.js → capabilities-page-Cx4cdgse.js} +2 -2
  17. package/dist/gateway/static/root/assets/{capabilities-settings-panel-Dx1fpBmH.js → capabilities-settings-panel-DpWceNd0.js} +1 -1
  18. package/dist/gateway/static/root/assets/{capability-header-actions-BRMqvQVb.js → capability-header-actions-AeBubNQK.js} +1 -1
  19. package/dist/gateway/static/root/assets/{channels-DXfEP1FR.js → channels-Cd1Rf0Vk.js} +1 -1
  20. package/dist/gateway/static/root/assets/{chat-terminal-dock-BRdzCjWO.js → chat-terminal-dock-DrVlbq_P.js} +1 -1
  21. package/dist/gateway/static/root/assets/{computer-settings-page-BhCHMRpD.js → computer-settings-page-TUzZnurj.js} +1 -1
  22. package/dist/gateway/static/root/assets/{connector-service-page-BXE8Z-Ll.js → connector-service-page-BlpwjGW7.js} +1 -1
  23. package/dist/gateway/static/root/assets/{connectors-page-D9RZjhw8.js → connectors-page-j8VFMD4o.js} +1 -1
  24. package/dist/gateway/static/root/assets/{date-picker-BJ7N92wm.js → date-picker-CjKRpA8X.js} +1 -1
  25. package/dist/gateway/static/root/assets/{dependency-picker-gyxCM-hb.js → dependency-picker-J9_0obij.js} +1 -1
  26. package/dist/gateway/static/root/assets/{desktop-pet-B-WM4l-L.js → desktop-pet-DySwgreF.js} +1 -1
  27. package/dist/gateway/static/root/assets/{desktop-pet-settings-usJqcew5.js → desktop-pet-settings-B1-oyKBG.js} +1 -1
  28. package/dist/gateway/static/root/assets/{device-pairing-wizard-DoSR4DGk.js → device-pairing-wizard-C5rjPAyc.js} +1 -1
  29. package/dist/gateway/static/root/assets/{directory-picker-path-field-DqVlV2YH.js → directory-picker-path-field-BY7MwiZd.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-page-CUule9SU.js → extension-page-JoSFhfb6.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-settings-page-_OFukkEJ.js → extension-settings-page-B7LtTNx7.js} +1 -1
  32. package/dist/gateway/static/root/assets/{fallback-models-CiSc3h8i.js → fallback-models-DKSWcBt_.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-settings-qSUrpS1h.js → gateway-settings-CGm-lbsX.js} +1 -1
  34. package/dist/gateway/static/root/assets/{home-page-COjDsuzg.js → home-page-CX3uWkyH.js} +1 -1
  35. package/dist/gateway/static/root/assets/{imports-page-DdvIj-ZL.js → imports-page-DS_RFtWf.js} +1 -1
  36. package/dist/gateway/static/root/assets/{index-DEaVabvo.js → index-CgvOIDy_.js} +5 -5
  37. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DbqFlNsF.js → keyboard-shortcuts-settings-BxwXR2qA.js} +1 -1
  38. package/dist/gateway/static/root/assets/{local-app-workbench-page-REowUzkp.js → local-app-workbench-page-BWd4RMBE.js} +1 -1
  39. package/dist/gateway/static/root/assets/{local-apps-page-DdCMNn_z.js → local-apps-page-BsgcA6-N.js} +1 -1
  40. package/dist/gateway/static/root/assets/{logs-page-B_6fHDoi.js → logs-page-CLP5pu2j.js} +1 -1
  41. package/dist/gateway/static/root/assets/{management-settings-D4vcSRE3.js → management-settings-CZAZwCpE.js} +1 -1
  42. package/dist/gateway/static/root/assets/{note-detail-panel-B7U6EbAi.js → note-detail-panel-BeAHgdtw.js} +3 -3
  43. package/dist/gateway/static/root/assets/{notes-page-BzHVaH8j.js → notes-page-MP21Zf_R.js} +1 -1
  44. package/dist/gateway/static/root/assets/{page-context-capture-button-D7Y4vFRN.js → page-context-capture-button-BZADmKcV.js} +1 -1
  45. package/dist/gateway/static/root/assets/{personal-page-E6huhFk4.js → personal-page-BbaTGmE-.js} +1 -1
  46. package/dist/gateway/static/root/assets/{product-open-page-CK_czbBx.js → product-open-page-CBnKsW4p.js} +1 -1
  47. package/dist/gateway/static/root/assets/{product-preview-content-CU6KlaVP.js → product-preview-content-B0gGexdG.js} +1 -1
  48. package/dist/gateway/static/root/assets/{project-detail-page-c3ye2bdq.js → project-detail-page-ZjQSv_NG.js} +1 -1
  49. package/dist/gateway/static/root/assets/{projects-page-BA1UuuRF.js → projects-page-Bf7Sd_Ck.js} +1 -1
  50. package/dist/gateway/static/root/assets/{remote-access-hub-D_FLVZAm.js → remote-access-hub-Bl8aPuXU.js} +1 -1
  51. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Cxjmwdyf.js → runtime-tools-settings-panel-CQZ1c-re.js} +1 -1
  52. package/dist/gateway/static/root/assets/{scenes-page-Av47PHdM.js → scenes-page-1xGRBOJO.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schema-form-IM7QeSm3.js → schema-form-BYqugMox.js} +1 -1
  54. package/dist/gateway/static/root/assets/{sessions-page-Bol3ScEc.js → sessions-page-DjMSVXU3.js} +1 -1
  55. package/dist/gateway/static/root/assets/settings-advanced-gate-BJuMTtyI.js +1 -0
  56. package/dist/gateway/static/root/assets/{settings-page-BeVtwwVd.js → settings-page-CSqi5fmN.js} +1 -1
  57. package/dist/gateway/static/root/assets/{setup-status-panel-zTmrMwMm.js → setup-status-panel-D5bM0TvA.js} +1 -1
  58. package/dist/gateway/static/root/assets/{shares-settings-Bkhkt_6M.js → shares-settings-C-V9WllF.js} +1 -1
  59. package/dist/gateway/static/root/assets/{skill-api-Dgwc707w.js → skill-api-CFtEHy5e.js} +1 -1
  60. package/dist/gateway/static/root/assets/{skills-page-CFIte-vM.js → skills-page-B-olXUVH.js} +1 -1
  61. package/dist/gateway/static/root/assets/{system-settings-panel-Ck6zGDHb.js → system-settings-panel-DBMR9ks_.js} +1 -1
  62. package/dist/gateway/static/root/assets/{task-detail-page-C3ZPEZqw.js → task-detail-page-CGe34vxR.js} +1 -1
  63. package/dist/gateway/static/root/assets/{tunnel-api-CjxMpAm2.js → tunnel-api-CzNIlgdJ.js} +1 -1
  64. package/dist/gateway/static/root/assets/{usage-page-Dnhp9xQ4.js → usage-page-O3abwewp.js} +1 -1
  65. package/dist/gateway/static/root/assets/user-model-page-Bx2JlCpj.js +2 -0
  66. package/dist/gateway/static/root/assets/{work-discovery-overlay-CVd_y3K2.js → work-discovery-overlay-D967-fVj.js} +1 -1
  67. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Dwg40uGK.js → workflow-run-setup-panel-BJ945m7G.js} +1 -1
  68. package/dist/gateway/static/root/assets/{workflows-page-BQR4JdXB.js → workflows-page-DUpVeIuw.js} +1 -1
  69. package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CaKc_J7j.js → xopc-cloud-account-card-Bq9ubffP.js} +1 -1
  70. package/dist/gateway/static/root/index.html +1 -1
  71. package/dist/package.js +1 -1
  72. package/dist/src/agent/agent-manager.d.ts +2 -7
  73. package/dist/src/agent/agent-manager.js +24 -53
  74. package/dist/src/agent/background-review/coordinator.js +1 -1
  75. package/dist/src/agent/background-review/prompts.d.ts +6 -0
  76. package/dist/src/agent/background-review/prompts.js +7 -1
  77. package/dist/src/agent/background-review/run-background-review.d.ts +3 -0
  78. package/dist/src/agent/background-review/run-background-review.js +184 -16
  79. package/dist/src/agent/context/coordinator.d.ts +1 -0
  80. package/dist/src/agent/context/coordinator.js +11 -2
  81. package/dist/src/agent/context/execution-context.js +40 -4
  82. package/dist/src/agent/context/project-context.js +11 -3
  83. package/dist/src/agent/embedded/model-runtime.js +1 -1
  84. package/dist/src/agent/embedded/run-for-session.js +2 -2
  85. package/dist/src/agent/embedded/run-turn.js +5 -5
  86. package/dist/src/agent/external-tools/endpoint-provider.js +2 -2
  87. package/dist/src/agent/image/tool-model-config.js +1 -1
  88. package/dist/src/agent/inbound/attachment-pipeline.js +1 -1
  89. package/dist/src/agent/memory/compaction-promotion.d.ts +2 -0
  90. package/dist/src/agent/memory/compaction-promotion.js +33 -2
  91. package/dist/src/agent/service/process-direct-streaming.js +1 -1
  92. package/dist/src/agent/service.js +7 -7
  93. package/dist/src/agent/skills/index.js +1 -1
  94. package/dist/src/agent/skills/managed-store.js +1 -1
  95. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  96. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  97. package/dist/src/agent/tools/factory.js +1 -1
  98. package/dist/src/agent/tools/knowledge-memory-tool.js +14 -6
  99. package/dist/src/agent/tools/xopc-use-tool.js +2 -2
  100. package/dist/src/agent/workspace-runtime/registry.js +1 -1
  101. package/dist/src/agent-catalog/capabilities.js +2 -2
  102. package/dist/src/auth/credentials.js +2 -2
  103. package/dist/src/auth/model-credential-store.js +1 -1
  104. package/dist/src/auth/sync-provider-auth.js +2 -2
  105. package/dist/src/automations/capabilities/drafts.js +1 -1
  106. package/dist/src/automations/draft/automation-draft-service.js +1 -1
  107. package/dist/src/automations/service/action-executor.js +1 -1
  108. package/dist/src/bootstrap/application-state.js +2 -2
  109. package/dist/src/capabilities/runtime/dispatcher.js +1 -1
  110. package/dist/src/capabilities/runtime/product.js +1 -1
  111. package/dist/src/chat-commands/agent-edit.js +1 -1
  112. package/dist/src/chat-commands/builtins/config.js +1 -1
  113. package/dist/src/chat-commands/context.js +1 -1
  114. package/dist/src/cli/commands/agent.js +2 -2
  115. package/dist/src/cli/commands/agents.js +1 -1
  116. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  117. package/dist/src/cli/commands/doctor/checks/image-providers.js +1 -1
  118. package/dist/src/cli/commands/doctor/checks/provider-auth.js +2 -2
  119. package/dist/src/cli/commands/models.js +2 -2
  120. package/dist/src/cli/commands/onboard/model.js +4 -4
  121. package/dist/src/cli/extension-cli-register.js +1 -1
  122. package/dist/src/cli/utils/init-workspace-core.js +1 -1
  123. package/dist/src/config/agent-model-intents.js +1 -1
  124. package/dist/src/config/agent-profile.js +2 -2
  125. package/dist/src/config/index.js +4 -4
  126. package/dist/src/config/loader.js +1 -1
  127. package/dist/src/config/models-json.js +2 -2
  128. package/dist/src/config/schema.d.ts +5 -0
  129. package/dist/src/config/schema.js +5 -5
  130. package/dist/src/connectors/connected-source-ingestion.js +45 -7
  131. package/dist/src/connectors/connected-source-understanding.d.ts +1 -0
  132. package/dist/src/connectors/connected-source-understanding.js +59 -67
  133. package/dist/src/connectors/content-enrichment.js +1 -1
  134. package/dist/src/connectors/learning-coordinator.js +2 -2
  135. package/dist/src/extensions/activation-context.js +1 -1
  136. package/dist/src/extensions/agent-plugins/validation.js +1 -1
  137. package/dist/src/gateway/agents-admin.js +2 -2
  138. package/dist/src/gateway/file-path-classifier.js +1 -1
  139. package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -0
  140. package/dist/src/gateway/hono/lib/config-payload.js +2 -2
  141. package/dist/src/gateway/hono/routes/config-patch/misc.js +6 -6
  142. package/dist/src/gateway/hono/routes/connectors.js +1 -1
  143. package/dist/src/gateway/hono/routes/context-sources.js +2 -2
  144. package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
  145. package/dist/src/gateway/hono/routes/mcp.js +1 -1
  146. package/dist/src/gateway/hono/routes/memory-learning.d.ts +3 -0
  147. package/dist/src/gateway/hono/routes/memory-learning.js +76 -0
  148. package/dist/src/gateway/hono/routes/models.js +2 -2
  149. package/dist/src/gateway/hono/routes/notes.js +2 -2
  150. package/dist/src/gateway/hono/routes/observability.js +1 -1
  151. package/dist/src/gateway/hono/routes/projects.js +3 -3
  152. package/dist/src/gateway/hono/routes/session-input-handler.js +1 -1
  153. package/dist/src/gateway/hono/routes/tasks.js +1 -1
  154. package/dist/src/gateway/hono/routes/user-model-refresh.js +1 -1
  155. package/dist/src/gateway/hono/routes/user-model.js +5 -3
  156. package/dist/src/gateway/hono/routes/voice.js +1 -1
  157. package/dist/src/gateway/image-generation-setup.js +2 -2
  158. package/dist/src/gateway/lock.js +1 -1
  159. package/dist/src/gateway/service/config-coordinator.js +1 -1
  160. package/dist/src/gateway/service.js +13 -13
  161. package/dist/src/gateway/xopc-cloud-capability-setup.js +1 -1
  162. package/dist/src/home-intelligence/generator.js +1 -1
  163. package/dist/src/home-intelligence/host.js +2 -2
  164. package/dist/src/imports/inventory.js +1 -1
  165. package/dist/src/knowledge/chunks.d.ts +10 -0
  166. package/dist/src/knowledge/chunks.js +28 -0
  167. package/dist/src/knowledge/connected-knowledge-pipeline.js +67 -33
  168. package/dist/src/knowledge/index.js +1 -1
  169. package/dist/src/knowledge/ingestion-service.js +5 -1
  170. package/dist/src/knowledge/local-folder-adapter.d.ts +2 -1
  171. package/dist/src/knowledge/local-folder-adapter.js +22 -13
  172. package/dist/src/knowledge-memory/domain.d.ts +4 -0
  173. package/dist/src/knowledge-memory/index.d.ts +1 -0
  174. package/dist/src/knowledge-memory/index.js +2 -1
  175. package/dist/src/knowledge-memory/repair-evidence.d.ts +2 -0
  176. package/dist/src/knowledge-memory/repair-evidence.js +57 -0
  177. package/dist/src/knowledge-memory/repository.d.ts +8 -0
  178. package/dist/src/knowledge-memory/repository.js +48 -27
  179. package/dist/src/knowledge-memory/search-text.d.ts +3 -0
  180. package/dist/src/knowledge-memory/search-text.js +32 -0
  181. package/dist/src/knowledge-memory/usage-policy.d.ts +6 -0
  182. package/dist/src/knowledge-memory/usage-policy.js +61 -0
  183. package/dist/src/local-apps/service.js +1 -1
  184. package/dist/src/memory-learning/repository.d.ts +24 -0
  185. package/dist/src/memory-learning/repository.js +96 -0
  186. package/dist/src/memory-learning/service.d.ts +17 -0
  187. package/dist/src/memory-learning/service.js +105 -0
  188. package/dist/src/memory-learning/source-learning.d.ts +7 -0
  189. package/dist/src/memory-learning/source-learning.js +235 -0
  190. package/dist/src/memory-learning/task-receipts.d.ts +3 -0
  191. package/dist/src/memory-learning/task-receipts.js +95 -0
  192. package/dist/src/memory-learning/work-capture.d.ts +51 -0
  193. package/dist/src/memory-learning/work-capture.js +140 -0
  194. package/dist/src/memory-maintenance/service.js +5 -5
  195. package/dist/src/notes/service.js +1 -1
  196. package/dist/src/observability/runtime.js +1 -1
  197. package/dist/src/personal-agent/proactivity/service.js +3 -3
  198. package/dist/src/personal-agent/prompt-context.js +4 -4
  199. package/dist/src/personal-agent/reply-composer.js +2 -2
  200. package/dist/src/personal-agent/service.js +2 -2
  201. package/dist/src/platform/index.js +1 -1
  202. package/dist/src/providers/index.js +4 -4
  203. package/dist/src/providers/local-model.d.ts +2 -0
  204. package/dist/src/providers/local-model.js +16 -0
  205. package/dist/src/providers/model-catalog-persistence.js +1 -1
  206. package/dist/src/providers/model-registry.js +2 -2
  207. package/dist/src/providers/provider-auth-service.js +1 -1
  208. package/dist/src/providers/provider-disconnect-core.js +1 -1
  209. package/dist/src/providers/register-runtime-providers.js +1 -1
  210. package/dist/src/providers/xopc-cloud-catalog-coordinator.js +1 -1
  211. package/dist/src/routing/resolve-route.js +1 -1
  212. package/dist/src/routing/resolve-tui-session-key.js +1 -1
  213. package/dist/src/scenes/taskFollowUp/service.js +1 -1
  214. package/dist/src/session/session-title.js +1 -1
  215. package/dist/src/session/store.js +2 -1
  216. package/dist/src/storage/sqlite/knowledge-repository.js +6 -0
  217. package/dist/src/storage/sqlite/migrations/245_memory_learning.sql +125 -0
  218. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  219. package/dist/src/storage/sqlite/migrations/runner.js +6 -3
  220. package/dist/src/storage/sqlite/schema.js +1 -1
  221. package/dist/src/tasks/capabilities/read.js +2 -2
  222. package/dist/src/tasks/task-handoff-service.js +1 -1
  223. package/dist/src/tasks/task-main-update-decision-service.js +1 -1
  224. package/dist/src/tui/backends/embedded-backend.js +5 -5
  225. package/dist/src/tui/extension-host/load-extensions.js +1 -1
  226. package/dist/src/tui/tui-commands.js +1 -1
  227. package/dist/src/tui/tui-skills-autocomplete.js +1 -1
  228. package/dist/src/tui/tui-startup-resources.js +2 -2
  229. package/dist/src/tui/tui.js +1 -1
  230. package/dist/src/user-context/access-policy.js +2 -1
  231. package/dist/src/user-context/config.d.ts +10 -0
  232. package/dist/src/user-context/config.js +5 -0
  233. package/dist/src/user-context/extraction/registry.d.ts +1 -1
  234. package/dist/src/user-context/extraction/registry.js +10 -0
  235. package/dist/src/user-context/identity-bindings.d.ts +10 -0
  236. package/dist/src/user-context/identity-bindings.js +24 -0
  237. package/dist/src/user-context/sources/consent-fields.d.ts +2 -0
  238. package/dist/src/user-context/sources/consent-fields.js +66 -0
  239. package/dist/src/user-model/bootstrap.js +1 -1
  240. package/dist/src/user-model/capture/executor.js +1 -1
  241. package/dist/src/user-model/capture/semantic.d.ts +3 -1
  242. package/dist/src/user-model/capture/semantic.js +14 -6
  243. package/dist/src/user-model/index.js +1 -1
  244. package/dist/src/user-model/usage-policy.js +34 -0
  245. package/dist/src/voice/platform-streams.js +1 -0
  246. package/dist/src/voice/realtime/memory-context.js +19 -5
  247. package/dist/src/voice/realtime/persona-context.js +1 -1
  248. package/dist/src/work-discovery/incremental-repository.js +1 -1
  249. package/dist/src/work-discovery/service.js +6 -6
  250. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  251. package/dist/src/workflows/service/workflow-session-bridge.js +38 -10
  252. package/package.json +3 -3
  253. package/dist/gateway/static/root/assets/settings-advanced-gate-DpCanqrD.js +0 -1
  254. package/dist/gateway/static/root/assets/user-model-page-sx1lNWKf.js +0 -2
@@ -1,3 +1,4 @@
1
+ import { inferMemorySensitivity } from "../agent/memory/sensitivity.js";
1
2
  import { extname, relative, resolve, sep } from "node:path";
2
3
  import { createHash } from "node:crypto";
3
4
  import { readFile, readdir, realpath, stat } from "node:fs/promises";
@@ -21,22 +22,19 @@ const MAX_FILES = 2e3;
21
22
  const MAX_FILE_BYTES = 2 * 1024 * 1024;
22
23
  var LocalFolderKnowledgeSourceAdapter = class {
23
24
  kind = "local-folder";
24
- constructor(rootPath) {
25
+ constructor(rootPath, pageSize = MAX_FILES) {
25
26
  this.rootPath = rootPath;
27
+ this.pageSize = pageSize;
26
28
  }
27
29
  async pull(input) {
28
30
  const scanStartedAt = (/* @__PURE__ */ new Date()).toISOString();
29
31
  const root = await realpath(resolve(this.rootPath));
30
32
  if (!(await stat(root)).isDirectory()) throw new Error(`Local knowledge source is not a directory: ${root}`);
31
- const cursorMs = input.cursor ? Date.parse(input.cursor) : NaN;
32
- const windowMs = input.windowStart ? Date.parse(input.windowStart) : NaN;
33
- const changedAfter = Math.max(Number.isFinite(cursorMs) ? cursorMs : 0, Number.isFinite(windowMs) ? windowMs : 0);
34
33
  const paths = [];
35
34
  const warnings = [];
36
35
  const walk = async (directory) => {
37
36
  if (input.signal.aborted) throw input.signal.reason ?? /* @__PURE__ */ new Error("Local folder sync aborted.");
38
37
  for (const entry of await readdir(directory, { withFileTypes: true })) {
39
- if (paths.length >= MAX_FILES) return;
40
38
  if (entry.isSymbolicLink()) continue;
41
39
  const path = resolve(directory, entry.name);
42
40
  if (entry.isDirectory()) {
@@ -47,14 +45,24 @@ var LocalFolderKnowledgeSourceAdapter = class {
47
45
  }
48
46
  };
49
47
  await walk(root);
50
- if (paths.length >= MAX_FILES) warnings.push(`Local folder sync was limited to ${MAX_FILES} files.`);
48
+ paths.sort((left, right) => left.localeCompare(right));
49
+ let after;
50
+ try {
51
+ after = JSON.parse(input.cursor ?? "{}").after;
52
+ } catch {}
53
+ const remaining = after ? paths.filter((path) => relative(root, path).localeCompare(after) > 0) : paths;
54
+ const page = remaining.slice(0, this.pageSize);
55
+ const hasMore = remaining.length > page.length;
51
56
  const items = [];
52
- for (const path of paths) {
57
+ for (const path of page) {
53
58
  if (input.signal.aborted) throw input.signal.reason ?? /* @__PURE__ */ new Error("Local folder sync aborted.");
54
59
  const fileStat = await stat(path);
55
- if (fileStat.size > MAX_FILE_BYTES || fileStat.mtimeMs <= changedAfter) continue;
56
60
  const canonicalPath = await realpath(path);
57
61
  if (canonicalPath !== root && !canonicalPath.startsWith(`${root}${sep}`)) continue;
62
+ if (fileStat.size > MAX_FILE_BYTES) {
63
+ warnings.push(`File exceeds ${MAX_FILE_BYTES} bytes and was excluded: ${relative(root, canonicalPath)}`);
64
+ continue;
65
+ }
58
66
  const normalizedText = await readFile(canonicalPath, "utf8");
59
67
  const externalId = relative(root, canonicalPath).split(sep).join("/");
60
68
  items.push({
@@ -62,7 +70,7 @@ var LocalFolderKnowledgeSourceAdapter = class {
62
70
  collectionScope: "files",
63
71
  externalId,
64
72
  itemType: "local_file",
65
- authorRole: "user",
73
+ authorRole: "unknown",
66
74
  sourceUpdatedAt: fileStat.mtime.toISOString(),
67
75
  contentHash: createHash("sha256").update(normalizedText).digest("hex"),
68
76
  normalizedText,
@@ -70,16 +78,17 @@ var LocalFolderKnowledgeSourceAdapter = class {
70
78
  path: externalId,
71
79
  rootPath: root
72
80
  },
73
- sensitivity: "personal",
74
- retentionClass: "durable",
81
+ sensitivity: inferMemorySensitivity(normalizedText),
82
+ retentionClass: "bounded",
75
83
  synthesisPipeline: "connected_knowledge"
76
84
  });
77
85
  }
78
86
  return {
79
87
  items,
80
- nextCursor: scanStartedAt,
88
+ nextCursor: hasMore ? JSON.stringify({ after: relative(root, page.at(-1)).split(sep).join("/") }) : scanStartedAt,
89
+ hasMore,
81
90
  warnings,
82
- snapshotExternalIds: paths.length < MAX_FILES ? paths.map((path) => relative(root, path).split(sep).join("/")) : void 0
91
+ snapshotExternalIds: warnings.length ? void 0 : paths.map((path) => relative(root, path).split(sep).join("/"))
83
92
  };
84
93
  }
85
94
  };
@@ -12,6 +12,10 @@ export interface KnowledgeReadPolicy {
12
12
  contentSources: readonly KnowledgeContentSource[];
13
13
  }
14
14
  export interface KnowledgeItem {
15
+ retrievalSnippet?: string;
16
+ claimMode?: 'explicit' | 'observed' | 'inferred' | 'reported';
17
+ verification?: 'supported' | 'tentative' | 'conflicted';
18
+ sensitivity?: 'normal' | 'personal' | 'secret' | 'regulated';
15
19
  id: string;
16
20
  principalId: string;
17
21
  kind: KnowledgeKind;
@@ -1,2 +1,3 @@
1
1
  export * from './domain.js';
2
2
  export * from './repository.js';
3
+ export * from './usage-policy.js';
@@ -1,2 +1,3 @@
1
+ import { canUseKnowledge, knowledgeContentSources, knowledgeExclusion } from "./usage-policy.js";
1
2
  import { KnowledgeReviewConflictError, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem } from "./repository.js";
2
- export { KnowledgeReviewConflictError, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
3
+ export { KnowledgeReviewConflictError, canUseKnowledge, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeContentSources, knowledgeExclusion, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
@@ -0,0 +1,2 @@
1
+ /** Legacy summaries are usable only when their original retained source can still be verified. */
2
+ export declare function repairLegacyKnowledgeEvidence(): number;
@@ -0,0 +1,57 @@
1
+ import { createContextEvidence, init_context_evidence_repository } from "../storage/sqlite/context-evidence-repository.js";
2
+ import { getKnowledgeSourceItem, init_knowledge_repository } from "../storage/sqlite/knowledge-repository.js";
3
+ import { getActiveUnderstandingConsent } from "../user-context/sources/consent-repository.js";
4
+ import { getUnderstandingSourceRun } from "../user-context/sources/repository.js";
5
+ import { listKnowledgeItems, writeKnowledgeItem } from "./repository.js";
6
+ //#region src/knowledge-memory/repair-evidence.ts
7
+ init_context_evidence_repository();
8
+ init_knowledge_repository();
9
+ /** Legacy summaries are usable only when their original retained source can still be verified. */
10
+ function repairLegacyKnowledgeEvidence() {
11
+ let repaired = 0;
12
+ for (const item of listKnowledgeItems({
13
+ recordClass: "memory",
14
+ statuses: ["candidate"],
15
+ limit: 1e3
16
+ })) {
17
+ if (item.originClass !== "untrusted" || item.verification !== "tentative" || typeof item.source.sourceRunId !== "string" || !Array.isArray(item.source.evidenceRefs)) continue;
18
+ const run = getUnderstandingSourceRun(item.source.sourceRunId);
19
+ const consent = run ? getActiveUnderstandingConsent(run.grantId) : void 0;
20
+ if (!consent || !consent.purposes.includes("work_assistance")) continue;
21
+ const sources = item.source.evidenceRefs.filter((ref) => typeof ref === "string").map((ref) => ref.startsWith("knowledge-source://") ? getKnowledgeSourceItem(ref.slice(19)) : void 0);
22
+ if (!sources.length || sources.some((source) => !source || source.deletedAt || Date.parse(source.updatedAt) > item.updatedAt)) continue;
23
+ const evidence = sources.map((source) => {
24
+ return {
25
+ id: createContextEvidence({
26
+ sourceType: "connector",
27
+ sourceInstanceId: source.sourceInstanceId,
28
+ sourceRef: `knowledge-source://${source.id}`,
29
+ sourceItemId: source.id,
30
+ contentHash: source.contentHash,
31
+ trustLevel: "untrusted",
32
+ observedAt: Date.parse(source.updatedAt),
33
+ processingPolicy: consent.processingPolicy
34
+ }).id,
35
+ contentHash: source.contentHash,
36
+ consentReceiptId: consent.id
37
+ };
38
+ });
39
+ writeKnowledgeItem({
40
+ ...item,
41
+ claimMode: "reported",
42
+ verification: "supported",
43
+ status: item.confidence >= .7 ? "active" : "candidate",
44
+ evidence,
45
+ replaceExisting: true,
46
+ source: {
47
+ ...item.source,
48
+ consentReceiptId: consent.id,
49
+ sourceInstanceId: sources[0].sourceInstanceId
50
+ }
51
+ });
52
+ repaired += 1;
53
+ }
54
+ return repaired;
55
+ }
56
+ //#endregion
57
+ export { repairLegacyKnowledgeEvidence };
@@ -1,6 +1,14 @@
1
1
  import { type UserModelScope } from '../user-model/domain.js';
2
2
  import type { KnowledgeItem, KnowledgeKind, KnowledgeOriginClass, KnowledgeRecordClass, KnowledgeContentSource, KnowledgeReadPolicy, KnowledgeStatus, KnowledgeReviewAction, KnowledgeStatusActor, KnowledgeStatusEvent, KnowledgeVisibilityContext } from './domain.js';
3
3
  export interface WriteKnowledgeInput {
4
+ claimMode?: KnowledgeItem['claimMode'];
5
+ verification?: KnowledgeItem['verification'];
6
+ sensitivity?: KnowledgeItem['sensitivity'];
7
+ evidence?: Array<{
8
+ id: string;
9
+ contentHash?: string;
10
+ consentReceiptId?: string;
11
+ }>;
4
12
  principalId?: string;
5
13
  kind: KnowledgeKind;
6
14
  scope: UserModelScope;
@@ -1,10 +1,12 @@
1
+ import { init_search_text, memorySearchQuery } from "./search-text.js";
1
2
  import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
3
  import { init_domain, validateScope } from "../user-model/domain.js";
3
- import { buildFts5SearchQuery, init_fts } from "../storage/sqlite/fts.js";
4
+ import { inferMemorySensitivity } from "../agent/memory/sensitivity.js";
4
5
  import { clearMemoryAudit, isMemorySuppressed, memoryFingerprint, restoreMemory, suppressMemory } from "../user-context/memory-suppression.js";
6
+ import { canUseKnowledge, knowledgeContentSources } from "./usage-policy.js";
5
7
  import { randomUUID } from "node:crypto";
6
8
  //#region src/knowledge-memory/repository.ts
7
- init_fts();
9
+ init_search_text();
8
10
  init_transaction();
9
11
  init_domain();
10
12
  var KnowledgeReviewConflictError = class extends Error {
@@ -16,6 +18,9 @@ var KnowledgeReviewConflictError = class extends Error {
16
18
  function fromRow(row) {
17
19
  return {
18
20
  id: row.knowledge_id,
21
+ claimMode: row.claim_mode,
22
+ verification: row.verification,
23
+ sensitivity: row.sensitivity,
19
24
  principalId: row.principal_id,
20
25
  kind: row.kind,
21
26
  scope: {
@@ -62,7 +67,8 @@ function writeKnowledgeItem(input) {
62
67
  if (input.validFrom !== void 0 && input.validTo !== void 0 && input.validTo < input.validFrom) throw new Error("Knowledge validTo must be greater than or equal to validFrom.");
63
68
  const principalId = input.principalId ?? "local-owner";
64
69
  const now = input.now ?? Date.now();
65
- const admittedStatus = input.status ?? (input.originClass === "owner" || input.originClass !== "untrusted" && input.confidence >= .7 && !input.derivedFromRecalledContext ? "active" : "candidate");
70
+ const supportedExternal = input.originClass === "untrusted" && input.verification === "supported" && Boolean(input.evidence?.length) && input.confidence >= .7;
71
+ const admittedStatus = input.status ?? (supportedExternal || input.originClass === "owner" || input.originClass !== "untrusted" && input.confidence >= .7 && !input.derivedFromRecalledContext ? "active" : "candidate");
66
72
  return runSqliteWriteTransaction((db) => {
67
73
  const keys = knowledgeFingerprints(input);
68
74
  if (input.restoreDeleted && input.originClass === "owner") restoreMemory(db, keys);
@@ -90,13 +96,12 @@ function writeKnowledgeItem(input) {
90
96
  created: false
91
97
  };
92
98
  if (existing) {
99
+ db.prepare("INSERT INTO knowledge_revisions(revision_id, knowledge_id, content, source_json, created_at) VALUES (?, ?, ?, ?, ?)").run(randomUUID(), existing.knowledge_id, existing.content, existing.source_json, now);
93
100
  const nextStatus = admittedStatus;
94
101
  db.prepare(`UPDATE knowledge_items SET kind = ?, content = ?, record_class = ?, status = ?, confidence = ?,
95
102
  importance = ?, valid_from = ?, valid_to = ?, expires_at = ?, review_at = ?,
96
103
  origin_class = ?, source_agent_id = ?, source_conversation_id = ?, source_turn_id = ?,
97
104
  derived_from_recalled_context = ?, source_json = ?, updated_at = ? WHERE knowledge_id = ?`).run(input.kind, input.content.trim(), input.recordClass ?? "memory", nextStatus, input.confidence, input.importance, input.validFrom ?? null, input.validTo ?? null, input.expiresAt ?? null, input.reviewAt ?? null, input.originClass, input.sourceAgentId ?? null, input.sourceConversationId ?? null, input.sourceTurnId ?? null, input.derivedFromRecalledContext ? 1 : 0, JSON.stringify(input.source ?? {}), now, existing.knowledge_id);
98
- db.prepare("DELETE FROM knowledge_items_fts WHERE knowledge_id = ?").run(existing.knowledge_id);
99
- db.prepare("INSERT INTO knowledge_items_fts(content, knowledge_id) VALUES (?, ?)").run(input.content.trim(), existing.knowledge_id);
100
105
  if (existing.status !== nextStatus) insertStatusEvent(db, {
101
106
  knowledgeId: existing.knowledge_id,
102
107
  fromStatus: existing.status,
@@ -105,6 +110,7 @@ function writeKnowledgeItem(input) {
105
110
  reason: "Knowledge replaced from its source.",
106
111
  now
107
112
  });
113
+ writeEvidence(db, existing.knowledge_id, input);
108
114
  return {
109
115
  item: fromRow(db.prepare("SELECT * FROM knowledge_items WHERE knowledge_id = ?").get(existing.knowledge_id)),
110
116
  created: false
@@ -118,7 +124,6 @@ function writeKnowledgeItem(input) {
118
124
  origin_class, source_agent_id, source_conversation_id, source_turn_id,
119
125
  derived_from_recalled_context, source_json, created_at, updated_at
120
126
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, principalId, input.kind, input.scope.type, input.scope.id ?? null, input.content.trim(), input.canonicalKey.trim(), input.recordClass ?? "memory", status, input.confidence, input.importance, input.validFrom ?? null, input.validTo ?? null, input.expiresAt ?? null, input.reviewAt ?? null, input.originClass, input.sourceAgentId ?? null, input.sourceConversationId ?? null, input.sourceTurnId ?? null, input.derivedFromRecalledContext ? 1 : 0, JSON.stringify(input.source ?? {}), now, now);
121
- db.prepare("INSERT INTO knowledge_items_fts(content, knowledge_id) VALUES (?, ?)").run(input.content.trim(), id);
122
127
  insertStatusEvent(db, {
123
128
  knowledgeId: id,
124
129
  fromStatus: null,
@@ -127,6 +132,7 @@ function writeKnowledgeItem(input) {
127
132
  reason: "Knowledge admitted.",
128
133
  now
129
134
  });
135
+ writeEvidence(db, id, input);
130
136
  return {
131
137
  item: fromRow(db.prepare("SELECT * FROM knowledge_items WHERE knowledge_id = ?").get(id)),
132
138
  created: true
@@ -166,9 +172,11 @@ function transitionKnowledgeStatus(input) {
166
172
  db.prepare(`UPDATE knowledge_items SET status = ?, content = COALESCE(?, content),
167
173
  origin_class = CASE WHEN ? THEN 'owner' ELSE origin_class END, updated_at = ?
168
174
  WHERE knowledge_id = ?`).run(input.status, content ?? null, input.actor === "user" && input.status === "active" ? 1 : 0, now, input.id);
175
+ if (input.actor === "user" && input.status === "active") db.prepare("UPDATE knowledge_items SET claim_mode = 'explicit', verification = 'supported' WHERE knowledge_id = ?").run(input.id);
169
176
  if (content !== void 0) {
170
- db.prepare("DELETE FROM knowledge_items_fts WHERE knowledge_id = ?").run(input.id);
171
- db.prepare("INSERT INTO knowledge_items_fts(content, knowledge_id) VALUES (?, ?)").run(content, input.id);
177
+ db.prepare("INSERT INTO knowledge_revisions(revision_id, knowledge_id, content, source_json, created_at) VALUES (?, ?, ?, ?, ?)").run(randomUUID(), input.id, current.content, current.source_json, now);
178
+ db.prepare("DELETE FROM knowledge_evidence_links WHERE knowledge_id = ?").run(input.id);
179
+ db.prepare("UPDATE knowledge_items SET claim_mode = 'explicit', verification = 'supported', source_json = '{}' WHERE knowledge_id = ?").run(input.id);
172
180
  }
173
181
  if (input.actor === "user" && (input.status === "rejected" || input.status === "archived")) suppressMemory(db, knowledgeFingerprints(fromRow(current)), now);
174
182
  insertStatusEvent(db, {
@@ -280,43 +288,51 @@ function visibilityClause(context, policy) {
280
288
  values
281
289
  };
282
290
  }
283
- function contentSourceClause(policy) {
284
- if (!policy) return "1";
285
- const allowed = new Set(policy.contentSources);
286
- const importedSource = "COALESCE(json_extract(k.source_json, '$.kind'), '') = 'product_import'";
287
- const parts = [];
288
- if (allowed.has("memory")) parts.push("k.record_class = 'memory'");
289
- if (allowed.has("local_import")) parts.push(`(k.record_class = 'source_index' AND ${importedSource})`);
290
- if (allowed.has("connector")) parts.push(`(k.record_class = 'source_index' AND NOT (${importedSource}))`);
291
- return `(${parts.length ? parts.join(" OR ") : "0"})`;
292
- }
293
291
  function searchKnowledgeItems(input) {
294
292
  const principalId = input.principalId ?? "local-owner";
295
293
  const asOf = input.asOf ?? Date.now();
296
294
  const limit = Math.max(1, Math.min(100, input.limit ?? 12));
297
295
  const visible = visibilityClause(input.context, input.policy);
298
- const contentSource = contentSourceClause(input.policy);
299
- const fts = buildFts5SearchQuery(input.query);
296
+ const fts = memorySearchQuery(input.query);
300
297
  if (!fts) return [];
301
- return getSqliteDatabase().prepare(`SELECT k.*, bm25(knowledge_items_fts) AS rank
302
- FROM knowledge_items_fts JOIN knowledge_items k ON k.knowledge_id = knowledge_items_fts.knowledge_id
303
- WHERE knowledge_items_fts MATCH ? AND k.principal_id = ? AND k.status = 'active'
298
+ const selected = getSqliteDatabase().prepare(`SELECT k.*, bm25(knowledge_search_fts) AS rank
299
+ FROM knowledge_search_fts JOIN knowledge_items k ON k.knowledge_id = knowledge_search_fts.knowledge_id
300
+ WHERE knowledge_search_fts MATCH ? AND k.principal_id = ? AND k.status = 'active'
304
301
  AND (k.valid_from IS NULL OR k.valid_from <= ?)
305
302
  AND (k.valid_to IS NULL OR k.valid_to >= ?)
306
303
  AND (k.expires_at IS NULL OR k.expires_at >= ?)
307
304
  AND (k.review_at IS NULL OR k.review_at > ?)
308
305
  ${input.recordClass ? "AND k.record_class = ?" : ""}
309
306
  ${input.trustedOnly ? "AND k.origin_class != 'untrusted'" : ""}
310
- AND ${contentSource}
311
307
  AND ${visible.sql}
312
- ORDER BY rank ASC, k.importance DESC, k.updated_at DESC LIMIT ?`).all(fts, principalId, asOf, asOf, asOf, asOf, ...input.recordClass ? [input.recordClass] : [], ...visible.values, limit).map(fromRow);
308
+ ORDER BY rank ASC, k.importance DESC, k.updated_at DESC LIMIT ?`).all(fts, principalId, asOf, asOf, asOf, asOf, ...input.recordClass ? [input.recordClass] : [], ...visible.values, Math.min(1e3, limit * 10)).map(fromRow).filter((item) => canUseKnowledge(item, input.context, input.policy, asOf));
309
+ const chunks = getSqliteDatabase().prepare(`SELECT k.*, c.text AS chunk_text
310
+ FROM knowledge_chunks_fts f JOIN knowledge_source_chunks c ON c.chunk_id = f.chunk_id
311
+ JOIN knowledge_source_items s ON s.item_id = c.source_item_id AND s.content_hash = c.source_version
312
+ JOIN knowledge_items k ON json_extract(k.source_json, '$.sourceItemId') = s.item_id
313
+ WHERE knowledge_chunks_fts MATCH ? AND s.deleted_at IS NULL AND k.principal_id = ?
314
+ AND c.consent_receipt_id = json_extract(k.source_json, '$.consentReceiptId')
315
+ ${input.recordClass ? "AND k.record_class = ?" : ""} AND ${visible.sql}
316
+ ORDER BY bm25(knowledge_chunks_fts) LIMIT ?`).all(fts, principalId, ...input.recordClass ? [input.recordClass] : [], ...visible.values, Math.min(1e3, limit * 10));
317
+ const seen = new Set(selected.map((item) => item.id));
318
+ for (const row of chunks) {
319
+ const item = fromRow(row);
320
+ if (!seen.has(item.id) && (!input.trustedOnly || item.originClass !== "untrusted") && canUseKnowledge(item, input.context, input.policy, asOf)) {
321
+ selected.push({
322
+ ...item,
323
+ retrievalSnippet: row.chunk_text
324
+ });
325
+ seen.add(item.id);
326
+ }
327
+ }
328
+ return selected.slice(0, limit);
313
329
  }
314
330
  function classifyKnowledgeContentSource(item) {
315
331
  if (item.recordClass === "memory") return "memory";
316
332
  return item.source.kind === "product_import" ? "local_import" : "connector";
317
333
  }
318
334
  function knowledgeItemAllowed(item, policy) {
319
- return policy.scopes.includes(item.scope.type) && policy.contentSources.includes(classifyKnowledgeContentSource(item));
335
+ return policy.scopes.includes(item.scope.type) && knowledgeContentSources(item).every((source) => policy.contentSources.includes(source));
320
336
  }
321
337
  function knowledgeFingerprints(input) {
322
338
  const principal = input.principalId ?? "local-owner";
@@ -347,7 +363,7 @@ function deleteKnowledgeItem(id, now = Date.now()) {
347
363
  const item = fromRow(row);
348
364
  suppressMemory(db, knowledgeFingerprints(item), now);
349
365
  clearMemoryAudit(db, "knowledge", item.id);
350
- db.prepare("DELETE FROM knowledge_items_fts WHERE knowledge_id = ?").run(item.id);
366
+ if (item.recordClass === "source_index" && typeof item.source.sourceItemId === "string") db.prepare("DELETE FROM knowledge_source_chunks WHERE source_item_id = ?").run(item.source.sourceItemId);
351
367
  db.prepare("DELETE FROM knowledge_items WHERE knowledge_id = ?").run(item.id);
352
368
  }
353
369
  return rows.length > 0;
@@ -356,5 +372,10 @@ function deleteKnowledgeItem(id, now = Date.now()) {
356
372
  function isKnowledgeCurrent(item, asOf = Date.now()) {
357
373
  return item.status === "active" && (item.validFrom === void 0 || item.validFrom <= asOf) && (item.validTo === void 0 || item.validTo >= asOf) && (item.expiresAt === void 0 || item.expiresAt >= asOf) && (item.reviewAt === void 0 || item.reviewAt > asOf);
358
374
  }
375
+ function writeEvidence(db, id, input) {
376
+ db.prepare("UPDATE knowledge_items SET claim_mode = ?, verification = ?, sensitivity = ? WHERE knowledge_id = ?").run(input.claimMode ?? (input.originClass === "owner" ? "explicit" : input.originClass === "untrusted" ? "reported" : "observed"), input.verification ?? (input.originClass === "untrusted" && (input.recordClass ?? "memory") === "memory" ? "tentative" : "supported"), input.sensitivity ?? inferMemorySensitivity(input.content), id);
377
+ db.prepare("DELETE FROM knowledge_evidence_links WHERE knowledge_id = ?").run(id);
378
+ for (const evidence of input.evidence ?? []) db.prepare("INSERT INTO knowledge_evidence_links(knowledge_id, evidence_id, content_hash, consent_receipt_id) VALUES (?, ?, ?, ?)").run(id, evidence.id, evidence.contentHash ?? null, evidence.consentReceiptId ?? null);
379
+ }
359
380
  //#endregion
360
381
  export { KnowledgeReviewConflictError, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
@@ -0,0 +1,3 @@
1
+ /** Preserve original words while indexing Chinese characters and adjacent pairs. */
2
+ export declare function memorySearchText(value: string): string;
3
+ export declare function memorySearchQuery(value: string): string;
@@ -0,0 +1,32 @@
1
+ import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
+ //#region src/knowledge-memory/search-text.ts
3
+ /** Preserve original words while indexing Chinese characters and adjacent pairs. */
4
+ function memorySearchText(value) {
5
+ const normalized = value.normalize("NFKC").toLowerCase();
6
+ const terms = /* @__PURE__ */ new Set();
7
+ for (const match of normalized.matchAll(HAN)) {
8
+ const chars = Array.from(match[0]);
9
+ for (let i = 0; i < chars.length; i += 1) {
10
+ terms.add(chars[i]);
11
+ if (chars[i + 1]) terms.add(chars[i] + chars[i + 1]);
12
+ }
13
+ }
14
+ return `${normalized}\n${[...terms].join(" ")}`;
15
+ }
16
+ function memorySearchQuery(value) {
17
+ const words = value.normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}_]+/gu) ?? [];
18
+ const terms = [];
19
+ for (const word of words) if (/^\p{Script=Han}+$/u.test(word)) {
20
+ const chars = Array.from(word);
21
+ if (chars.length === 1) terms.push(word);
22
+ else for (let i = 0; i < chars.length - 1; i += 1) terms.push(chars[i] + chars[i + 1]);
23
+ } else if (word.length > 1) terms.push(word);
24
+ return [...new Set(terms)].slice(0, 32).map((term) => `"${term.replaceAll("\"", "\"\"")}"`).join(" OR ");
25
+ }
26
+ var HAN;
27
+ var init_search_text = __esmMin((() => {
28
+ HAN = /\p{Script=Han}+/gu;
29
+ }));
30
+ //#endregion
31
+ init_search_text();
32
+ export { init_search_text, memorySearchQuery, memorySearchText };
@@ -0,0 +1,6 @@
1
+ import type { KnowledgeItem, KnowledgeReadPolicy, KnowledgeVisibilityContext } from './domain.js';
2
+ export type KnowledgeExclusion = 'principal' | 'scope' | 'source_not_allowed' | 'expired' | 'conflicted' | 'sensitive' | 'evidence_missing' | 'source_revoked' | 'source_changed' | 'agent_not_allowed';
3
+ export declare function knowledgeContentSources(item: KnowledgeItem): Array<'memory' | 'local_import' | 'connector'>;
4
+ /** Source reliability affects how knowledge is described, never execution authority. */
5
+ export declare function knowledgeExclusion(item: KnowledgeItem, context: KnowledgeVisibilityContext, policy?: KnowledgeReadPolicy, asOf?: number): KnowledgeExclusion | undefined;
6
+ export declare function canUseKnowledge(item: KnowledgeItem, context: KnowledgeVisibilityContext, policy?: KnowledgeReadPolicy, asOf?: number): boolean;
@@ -0,0 +1,61 @@
1
+ import { getSqliteDatabase, init_transaction } from "../storage/sqlite/transaction.js";
2
+ import { getSessionMetadata } from "../storage/sqlite/session-read-repository.js";
3
+ import { init_session_repository } from "../storage/sqlite/session-repository.js";
4
+ import { getUnderstandingConsent } from "../user-context/sources/consent-repository.js";
5
+ import { isOwnerIdentity } from "../user-context/identity-bindings.js";
6
+ //#region src/knowledge-memory/usage-policy.ts
7
+ init_transaction();
8
+ init_session_repository();
9
+ function knowledgeContentSources(item) {
10
+ if (item.source.kind === "product_import") return ["local_import"];
11
+ if (typeof item.source.sourceInstanceId === "string" && item.source.sourceInstanceId.startsWith("local-folder:")) return item.recordClass === "memory" ? ["memory", "local_import"] : ["local_import"];
12
+ if (item.recordClass === "source_index" || item.source.sourceInstanceId || item.source.sourceRunId || item.source.consentReceiptId) return item.recordClass === "memory" ? ["memory", "connector"] : ["connector"];
13
+ return ["memory"];
14
+ }
15
+ /** Source reliability affects how knowledge is described, never execution authority. */
16
+ function knowledgeExclusion(item, context, policy, asOf = Date.now()) {
17
+ if (item.principalId !== "local-owner") return "principal";
18
+ if (!(item.scope.type === "global" || item.scope.id === {
19
+ agent: context.agentId,
20
+ workspace: context.workspaceId,
21
+ project: context.projectId,
22
+ session: context.conversationId
23
+ }[item.scope.type]) || policy && !policy.scopes.includes(item.scope.type)) return "scope";
24
+ if (policy && !knowledgeContentSources(item).every((source) => policy.contentSources.includes(source))) return "source_not_allowed";
25
+ if (item.status !== "active" || item.validFrom !== void 0 && item.validFrom > asOf || item.validTo !== void 0 && item.validTo < asOf || item.expiresAt !== void 0 && item.expiresAt < asOf || item.reviewAt !== void 0 && item.reviewAt <= asOf) return "expired";
26
+ if (item.verification === "conflicted" || item.verification === "tentative") return "conflicted";
27
+ if (item.sensitivity === "secret" || item.sensitivity === "regulated") return "sensitive";
28
+ if (item.originClass !== "owner" && item.sourceConversationId) {
29
+ const session = getSessionMetadata(item.sourceConversationId);
30
+ if (!session?.routing || session.routing.peerKind !== "direct" || !isOwnerIdentity(session.routing)) return "source_revoked";
31
+ }
32
+ const db = getSqliteDatabase();
33
+ const links = db.prepare(`SELECT e.*, l.content_hash AS linked_hash, l.consent_receipt_id
34
+ FROM knowledge_evidence_links l JOIN context_evidence e ON e.evidence_id = l.evidence_id
35
+ WHERE l.knowledge_id = ?`).all(item.id);
36
+ if (item.originClass === "untrusted" && !links.length) return "evidence_missing";
37
+ for (const link of links) {
38
+ if (link.conversation_id) {
39
+ const session = getSessionMetadata(link.conversation_id);
40
+ if (!session?.routing || session.routing.peerKind !== "direct" || !isOwnerIdentity(session.routing)) return "source_revoked";
41
+ }
42
+ if (link.linked_hash && link.linked_hash !== link.content_hash) return "source_changed";
43
+ if (link.source_item_id) {
44
+ const source = db.prepare("SELECT content_hash, deleted_at FROM knowledge_source_items WHERE item_id = ?").get(link.source_item_id);
45
+ if (!source || source.deleted_at) return "source_revoked";
46
+ if (link.linked_hash && link.linked_hash !== source.content_hash) return "source_changed";
47
+ }
48
+ if (link.source_type === "connector") {
49
+ if (!link.consent_receipt_id) return "evidence_missing";
50
+ const consent = getUnderstandingConsent(link.consent_receipt_id);
51
+ if (!consent || consent.revokedAt !== void 0 || !consent.purposes.includes("work_assistance")) return "source_revoked";
52
+ if (db.prepare("SELECT status FROM understanding_source_grants WHERE grant_id = ?").get(consent.grantId)?.status !== "active") return "source_revoked";
53
+ if (consent.allowedAgentIds && !consent.allowedAgentIds.includes(context.agentId)) return "agent_not_allowed";
54
+ }
55
+ }
56
+ }
57
+ function canUseKnowledge(item, context, policy, asOf = Date.now()) {
58
+ return knowledgeExclusion(item, context, policy, asOf) === void 0;
59
+ }
60
+ //#endregion
61
+ export { canUseKnowledge, knowledgeContentSources, knowledgeExclusion };
@@ -5,10 +5,10 @@ import { currentOperationId, init_operation_context } from "../infra/operation-c
5
5
  import { afterSqliteCommit, init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
6
6
  import { ExtensionCapabilityBindingsSchema, extensionCapabilityPermissions } from "../../packages/gateway-contract/src/extension-capabilities.js";
7
7
  import { init_src } from "../../packages/gateway-contract/src/index.js";
8
- import { CapabilityError } from "../capabilities/runtime/errors.js";
9
8
  import { init_paths, resolveExtensionsDir } from "../config/paths.js";
10
9
  import { slugifyProjectName } from "../projects/project-store.js";
11
10
  import { LocalAppStore } from "./store.js";
11
+ import { CapabilityError } from "../capabilities/runtime/errors.js";
12
12
  import { DomainOutboxDispatcher } from "../infra/domain-outbox-dispatcher.js";
13
13
  import { validateLocalAppCapabilityContracts } from "./capabilities/contracts.js";
14
14
  import { buildLocalAppFixGuidance } from "./fix-guidance.js";
@@ -0,0 +1,24 @@
1
+ import type { Config } from '../config/schema.js';
2
+ export interface LearningJob {
3
+ job_id: string;
4
+ conversation_id: string;
5
+ transcript_id: string;
6
+ through_seq: number;
7
+ policy_hash: string;
8
+ workspace_id: string;
9
+ lease_token: string;
10
+ attempts: number;
11
+ start_seq?: number | null;
12
+ }
13
+ export declare function learningPolicyHash(config: Config): string;
14
+ /** Recover learning intent from authoritative rows, including notifications lost in a crash. */
15
+ export declare function enqueueConversationLearning(config: Config, conversationId: string, workspaceId: string, force?: boolean, now?: number): string | undefined;
16
+ export declare function claimLearningJob(now?: number): LearningJob | undefined;
17
+ export declare function renewLearningLease(job: LearningJob): boolean;
18
+ /** Apply model results and advance progress atomically under the current lease. */
19
+ export declare function finishLearningJob(job: LearningJob, apply: () => void, processedThrough?: number): boolean;
20
+ export declare function failLearningJob(job: LearningJob, errorCode: string, canceled?: boolean): void;
21
+ export declare function learningStatus(config?: Config): {
22
+ jobs: unknown[];
23
+ coverage: unknown[];
24
+ };
@@ -0,0 +1,96 @@
1
+ import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
+ import { resolveUserContextSessionAccess } from "../user-context/access-policy.js";
3
+ import { createHash, randomUUID } from "node:crypto";
4
+ //#region src/memory-learning/repository.ts
5
+ init_transaction();
6
+ function learningPolicyHash(config) {
7
+ const catalog = getSqliteDatabase().prepare("SELECT revision FROM agent_catalog_settings WHERE singleton_id = 1").get();
8
+ return createHash("sha256").update(JSON.stringify({
9
+ config,
10
+ catalog
11
+ })).digest("hex");
12
+ }
13
+ /** Recover learning intent from authoritative rows, including notifications lost in a crash. */
14
+ function enqueueConversationLearning(config, conversationId, workspaceId, force = false, now = Date.now()) {
15
+ const access = resolveUserContextSessionAccess(config, conversationId);
16
+ if ((!access.userModel || config.userContext.userModel.writePolicy === "deny") && (!access.knowledge || config.userContext.knowledgeMemory.writePolicy === "deny")) return;
17
+ const idleMs = config.userContext.knowledgeMemory.learning?.idleFlushMs ?? 6e4;
18
+ return runSqliteWriteTransaction((db) => {
19
+ const transcript = db.prepare("SELECT active_transcript_id FROM sessions WHERE conversation_id = ?").get(conversationId);
20
+ if (!transcript?.active_transcript_id) return;
21
+ const id = transcript.active_transcript_id;
22
+ const last = db.prepare(`SELECT MAX(seq) AS seq, MAX(created_at) AS last_at FROM transcript_entries
23
+ WHERE transcript_id = ? AND role = 'assistant'`).get(id);
24
+ if (!last.seq) return;
25
+ db.prepare(`INSERT OR IGNORE INTO memory_learning_cursors(conversation_id, transcript_id, updated_at) VALUES (?, ?, ?)`).run(conversationId, id, now);
26
+ const cursor = db.prepare("SELECT processed_seq FROM memory_learning_cursors WHERE conversation_id = ? AND transcript_id = ?").get(conversationId, id);
27
+ if (!force && cursor.processed_seq >= last.seq) return;
28
+ const count = db.prepare(`SELECT COUNT(*) AS n FROM transcript_entries WHERE transcript_id = ? AND seq > ? AND role = 'user'`).get(id, cursor.processed_seq);
29
+ const due = force || count.n >= config.userContext.userModel.extraction.reviewIntervalTurns || now - (last.last_at ?? now) >= idleMs;
30
+ const jobId = randomUUID();
31
+ const inserted = db.prepare(`INSERT OR IGNORE INTO memory_learning_jobs(job_id, conversation_id, transcript_id,
32
+ through_seq, start_seq, extractor_version, policy_hash, workspace_id, state, next_attempt_at, created_at, updated_at)
33
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?, ?, ?)`).run(jobId, conversationId, id, last.seq, force ? 0 : null, force ? `rebuild:${jobId}` : `2:${cursor.processed_seq}`, learningPolicyHash(config), workspaceId, due ? now : (last.last_at ?? now) + idleMs, now, now);
34
+ db.prepare(`UPDATE memory_learning_cursors SET observed_seq = MAX(observed_seq, ?), updated_at = ?
35
+ WHERE conversation_id = ? AND transcript_id = ?`).run(last.seq, now, conversationId, id);
36
+ return inserted.changes ? jobId : void 0;
37
+ });
38
+ }
39
+ function claimLearningJob(now = Date.now()) {
40
+ return runSqliteWriteTransaction((db) => {
41
+ db.prepare("UPDATE memory_learning_jobs SET state = 'failed', error_code = 'lease_expired', updated_at = ? WHERE state = 'running' AND attempts >= 3 AND lease_until < ?").run(now, now);
42
+ const row = db.prepare(`SELECT * FROM memory_learning_jobs j WHERE
43
+ ((state IN ('queued', 'retry_wait') AND next_attempt_at <= ?) OR (state = 'running' AND lease_until < ?))
44
+ AND NOT EXISTS (SELECT 1 FROM memory_learning_jobs other WHERE other.conversation_id = j.conversation_id
45
+ AND other.state = 'running' AND other.lease_until >= ? AND other.job_id != j.job_id)
46
+ AND attempts < 3 ORDER BY created_at LIMIT 1`).get(now, now, now);
47
+ if (!row) return;
48
+ const token = randomUUID();
49
+ db.prepare(`UPDATE memory_learning_jobs SET state = 'running', lease_token = ?, lease_until = ?,
50
+ attempts = attempts + 1, updated_at = ? WHERE job_id = ?`).run(token, now + 18e4, now, row.job_id);
51
+ return {
52
+ ...row,
53
+ lease_token: token,
54
+ attempts: row.attempts + 1
55
+ };
56
+ });
57
+ }
58
+ function renewLearningLease(job) {
59
+ return Boolean(getSqliteDatabase().prepare(`UPDATE memory_learning_jobs SET lease_until = ?
60
+ WHERE job_id = ? AND state = 'running' AND lease_token = ?`).run(Date.now() + 18e4, job.job_id, job.lease_token).changes);
61
+ }
62
+ /** Apply model results and advance progress atomically under the current lease. */
63
+ function finishLearningJob(job, apply, processedThrough = job.through_seq) {
64
+ return runSqliteWriteTransaction((db) => {
65
+ if (!db.prepare(`SELECT 1 FROM memory_learning_jobs WHERE job_id = ? AND state = 'running'
66
+ AND lease_token = ? AND lease_until >= ?`).get(job.job_id, job.lease_token, Date.now())) return false;
67
+ apply();
68
+ db.prepare(`UPDATE memory_learning_cursors SET processed_seq = MAX(processed_seq, ?), updated_at = ?
69
+ WHERE conversation_id = ? AND transcript_id = ?`).run(processedThrough, Date.now(), job.conversation_id, job.transcript_id);
70
+ db.prepare(`UPDATE memory_learning_jobs SET state = 'succeeded', lease_token = NULL, lease_until = NULL, updated_at = ? WHERE job_id = ?`).run(Date.now(), job.job_id);
71
+ db.prepare(`UPDATE memory_learning_jobs SET state = 'canceled', updated_at = ? WHERE conversation_id = ?
72
+ AND transcript_id = ? AND through_seq <= ? AND start_seq IS NULL AND state IN ('queued','retry_wait')`).run(Date.now(), job.conversation_id, job.transcript_id, processedThrough);
73
+ if (processedThrough < job.through_seq) {
74
+ const nextId = randomUUID();
75
+ db.prepare(`INSERT INTO memory_learning_jobs(job_id, conversation_id, transcript_id, through_seq,
76
+ start_seq, extractor_version, policy_hash, workspace_id, state, next_attempt_at, created_at, updated_at)
77
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?, ?, ?)`).run(nextId, job.conversation_id, job.transcript_id, job.through_seq, job.start_seq == null ? null : processedThrough, job.start_seq == null ? `2:${processedThrough}` : `rebuild:${nextId}`, job.policy_hash, job.workspace_id, Date.now(), Date.now(), Date.now());
78
+ }
79
+ return true;
80
+ });
81
+ }
82
+ function failLearningJob(job, errorCode, canceled = false) {
83
+ getSqliteDatabase().prepare(`UPDATE memory_learning_jobs SET state = ?, error_code = ?, lease_token = NULL,
84
+ lease_until = NULL, next_attempt_at = ?, updated_at = ? WHERE job_id = ? AND lease_token = ?`).run(canceled ? "canceled" : job.attempts >= 3 ? "failed" : "retry_wait", errorCode, Date.now() + 3e4 * 2 ** job.attempts, Date.now(), job.job_id, job.lease_token);
85
+ }
86
+ function learningStatus(config) {
87
+ const db = getSqliteDatabase();
88
+ return {
89
+ jobs: db.prepare(`SELECT state, error_code AS errorCode, COUNT(*) AS count FROM (
90
+ SELECT state, error_code, policy_hash FROM memory_learning_jobs UNION ALL SELECT state, error_code, policy_hash FROM memory_source_learning_jobs
91
+ ) ${config ? "WHERE policy_hash = ?" : ""} GROUP BY state, error_code`).all(...config ? [learningPolicyHash(config)] : []),
92
+ coverage: db.prepare("SELECT record_class AS recordClass, status, verification, COUNT(*) AS count FROM knowledge_items GROUP BY record_class, status, verification").all()
93
+ };
94
+ }
95
+ //#endregion
96
+ export { claimLearningJob, enqueueConversationLearning, failLearningJob, finishLearningJob, learningPolicyHash, learningStatus, renewLearningLease };
@@ -0,0 +1,17 @@
1
+ import type { Config } from '../config/schema.js';
2
+ import { type LearningJob } from './repository.js';
3
+ export declare class MemoryLearningService {
4
+ private readonly options;
5
+ private timer?;
6
+ private running;
7
+ private stopped;
8
+ private lastRetentionSweep;
9
+ constructor(options: {
10
+ getConfig: () => Config | undefined;
11
+ workspaceForSession: (id: string) => string;
12
+ execute: (job: LearningJob) => Promise<void>;
13
+ });
14
+ start(): void;
15
+ stop(): void;
16
+ runNow(): Promise<void>;
17
+ }